Feature
map() called with one positional argument returns a map object in RustPython, but CPython raises TypeError.
Reproduction
RustPython:
CPython:
TypeError: map() must have at least two arguments.
Root cause
PyMap::py_new collects the trailing positional arguments into PosArgs<PyIter> and stores the resulting vector without checking that at least one iterable was supplied, so map(f) constructs a map object out of a single argument, and PyMap::next does not special-case an empty iterator vector either, so that object is an iterator calling f() with no arguments forever. CPython validates the count before touching any argument, in both map_vectorcall and map_new, and converts arguments 2..n with PyObject_GetIter only after that check passes. The neighbouring iterators do bound the count: zip.rs special-cases the empty vector, and filter.rs makes the iterable mandatory at the type level with type Args = (PyObjectRef, PyIter).
Fix
In PyMap::py_new, return vm.new_type_error("map() must have at least two arguments.") when the collected iterator vector is empty, rather than checking in IterNext::next, since CPython reports this as an argument error at call time.
Environment
- RustPython d248a04 (Python 3.14.0), also reproduced on 0.5.0
- CPython v3.14.3
- OS: Debian 12
Python Documentation or reference to CPython source code
-
CPython map_vectorcall: Python/bltinmodule.c#L1423-L1434 — rejects fewer than two positional arguments before any argument is converted to an iterator:
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) {
// Fallback to map_new()
PyThreadState *tstate = _PyThreadState_GET();
return _PyObject_MakeTpCall(tstate, type, args, nargs, kwnames);
}
if (nargs < 2) {
PyErr_SetString(PyExc_TypeError,
"map() must have at least two arguments.");
return NULL;
}
-
CPython map_new: Python/bltinmodule.c#L1382-L1387 — repeats the same check on the keyword-argument path that map_vectorcall falls back to, so both entry points reject the call.
-
Documentation: builtins.map
Related arity-validation gaps in other builtin constructors: #8460, #8461.
AI disclosure: this report was drafted with Claude Code (claude-opus-5) and reviewed by me before submission. The divergence comes from a differential-testing run against CPython 3.14.3, and every source reference above was checked against the linked revisions.
Feature
map()called with one positional argument returns amapobject in RustPython, but CPython raisesTypeError.Reproduction
RustPython:
CPython:
Root cause
PyMap::py_newcollects the trailing positional arguments intoPosArgs<PyIter>and stores the resulting vector without checking that at least one iterable was supplied, somap(f)constructs amapobject out of a single argument, andPyMap::nextdoes not special-case an empty iterator vector either, so that object is an iterator callingf()with no arguments forever. CPython validates the count before touching any argument, in bothmap_vectorcallandmap_new, and converts arguments 2..n withPyObject_GetIteronly after that check passes. The neighbouring iterators do bound the count:zip.rsspecial-cases the empty vector, andfilter.rsmakes the iterable mandatory at the type level withtype Args = (PyObjectRef, PyIter).Fix
In
PyMap::py_new, returnvm.new_type_error("map() must have at least two arguments.")when the collected iterator vector is empty, rather than checking inIterNext::next, since CPython reports this as an argument error at call time.Environment
Python Documentation or reference to CPython source code
CPython
map_vectorcall:Python/bltinmodule.c#L1423-L1434— rejects fewer than two positional arguments before any argument is converted to an iterator:CPython
map_new:Python/bltinmodule.c#L1382-L1387— repeats the same check on the keyword-argument path thatmap_vectorcallfalls back to, so both entry points reject the call.Documentation:
builtins.mapRelated arity-validation gaps in other builtin constructors: #8460, #8461.
AI disclosure: this report was drafted with Claude Code (claude-opus-5) and reviewed by me before submission. The divergence comes from a differential-testing run against CPython 3.14.3, and every source reference above was checked against the linked revisions.