Repository navigation
Conversation
FuncArgs::bind_for rejected surplus positional arguments only after FromArgs::from_args had converted every declared parameter. A conversion can run user code (__iter__ for PyIter, __index__ for ArgIndex), so filter(None, it, 0), hex(x, 1) and pairwise(it, 0) raised whatever that code raised instead of the TypeError CPython reports before it touches any argument. Check the count against FromArgs::arity() before from_args runs. For that bound to be safe: - The default arity() has no upper bound, since a manual impl may take positional arguments itself. KwArgs, (), DirFd, OpenDirFd and csv's FormatOptions declare the bound they actually have. - A derived struct adds the upper bound of each flattened field. - A tuple sums upper bounds with saturating_add. Assisted-by: Claude Code:claude-opus-5-5
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
| fn arity() -> ::std::ops::RangeInclusive<usize> { | ||
| #min_arity..=#max_arity | ||
| #min_arity..=#max_arity #( | ||
| .saturating_add(*<#flattened as ::rustpython_vm::function::FromArgs>::arity().end()) |
There was a problem hiding this comment.
could you tell me why saturating_add is required here? I am missing something.
There was a problem hiding this comment.
PosArgs doesn't override arity(), so with this change it gets the default 0..=usize::MAX. A struct like PyMapNewArgs has two positional fields plus a flattened PosArgs, so the derived bound becomes 2 + usize::MAX. With plain + that panics in debug builds and wraps to 1 in release, and then the new check in bind_for would reject map(f, it). Saturating keeps it at usize::MAX, meaning "no upper bound". The tuple impl uses saturating_add for the same reason.
Happy to add a short comment there if that makes it clearer.
Comment that an unbounded upper bound is usize::MAX, which is why the tuple and derived arity() sums use saturating_add, and drop the CPython mention from the positional count check comment. Assisted-by: Claude Code:claude-opus-5-5
Summary
FuncArgs::bind_forrejects leftover positional arguments only afterfrom_argshas converted every parameter. A conversion can run user code (__iter__forPyIter,__index__forArgIndex), so a call with too many arguments raises whatever that code raises. CPython checks the count first, infilter_vectorcalland incfunction_vectorcall_Oforhex()and friends. #8957 made the same change for keywords.AS-IS
TO-BE
(matching CPython's exception type; the
filtermessage is identical, thehexone keeps RustPython's shared wording)Changes
FromArgs::arity()inbind_forbeforefrom_argsruns.arity()unbounded, since a hand-written impl such asObjectArgsorReplaceChangesmay take any number of positional arguments itself. GiveKwArgs,(),DirFd,OpenDirFdand csv'sFormatOptionsthe bound they actually take, so their existing messages keep a finite count.arity(), and sum tuple bounds withsaturating_add.*argsnow saysexpected at least 1 argumentinstead ofexpected 1 argument(for examplestruct.pack()andint.__new__()). Both are stillTypeError.extra_tests/snippets/vm_argument_errors.py.Test plan
Built and run on macOS aarch64 against upstream/main 398dd70, using the CI feature set
--no-default-features --features stdlib,importlib,stdio,encodings,sqlite,ssl-rustls-aws-lc,host_env,doc,ucd_3_2_0, plusthreading, compared with CPython 3.14.4.itertools,operator,functools,mathand constructors with too many, too few and valid arguments: the patch changes exactly 10 results, all fromRuntimeErrortoTypeError(filtertwice,enumeratewith 3 arguments,hex,oct,bin,chr,itertools.compress,pairwiseandtakewhile). The other 59 are unchanged.rustpython -m test -j 6 --timeout 600on the unpatched and patched builds: 45,717 run on both, and the same 3 modules fail on both (test_cmd_line,test_dict,test_frame).pytest -k rustpythoninextra_tests: 246 pass on the patched build. On the unpatched build the only failure is the newvm_argument_errors.pycase, which also passes under CPython 3.14.4.cargo test -p rustpython-vm -p rustpython-derive-impl --lib: 128 pass.cargo fmt --checkclean,cargo clippy -p rustpython-vm -p rustpython-stdlib -p rustpython-derive-impl --all-targetsreports nothing, andpre-commit run --filespasses on the changed files.I used Claude Code (claude-opus-5-5) to draft the analysis, the patch and this description; I reviewed the change and ran every command above myself.
Summary by CodeRabbit
Bug Fixes
Compatibility