Skip to content

property() accepts a fifth positional argument instead of raising TypeError #8460

Description

@jseop-lim

Feature

property(None, None, None, None, None) constructs a property object in RustPython, but CPython raises TypeError because property() takes at most four arguments.

Reproduction

print(type(property(None, None, None, None, None)))

RustPython:

<class 'property'>

CPython:

TypeError: property() takes at most 4 arguments (5 given)

Root cause

PropertyArgs declares a fifth #[pyarg(any, default)] field, name, and compute_arity_bounds counts every positional-or-keyword field toward the maximum arity, so PropertyArgs::arity() is 0..=5 and the leftover-positional check in FuncArgs::bind passes for five arguments; Initializer::init then stores the fifth one in the __name__ slot. CPython's property.__init__ is Argument Clinic generated with maxpos = 4 and the keyword list {fget, fset, fdel, doc}, so _PyArg_UnpackKeywords rejects the fifth argument before property_init_impl runs. The same field also makes RustPython accept property(name='x'), which is not in CPython's keyword list either.

Fix

Remove the name field from PropertyArgs and the assignment that consumes it in Initializer::init, which drops the derived arity back to 0..=4. No caller passes it: clone_property_with already passes name: None and copies the name separately, and the PyProperty name slot stays filled by __set_name__ and the __name__ setter.

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 property_init: Objects/clinic/descrobject.c.h#L133-L150 — the Argument Clinic generated parser fixes the keyword list at four entries and passes maxpos = 4:

    static const char * const _keywords[] = {"fget", "fset", "fdel", "doc", NULL};
    static _PyArg_Parser _parser = {
        .keywords = _keywords,
        .fname = "property",
        .kwtuple = KWTUPLE,
    };
    ...
    fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser,
            /*minpos*/ 0, /*maxpos*/ 4, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
  • CPython _PyArg_UnpackKeywords: Python/getargs.c#L2429-L2441 — raises the TypeError once the argument count exceeds maxargs, before the implementation function is reached:

    if (!varpos && nargs + nkwargs > maxargs) {
        /* Adding "keyword" (when nargs == 0) prevents producing wrong error
           messages in some special cases (see bpo-31229). */
        PyErr_Format(PyExc_TypeError,
                     "%.200s%s takes at most %d %sargument%s (%zd given)",
                     (parser->fname == NULL) ? "function" : parser->fname,
                     (parser->fname == NULL) ? "" : "()",
                     maxargs,
                     (nargs == 0) ? "keyword " : "",
                     (maxargs == 1) ? "" : "s",
                     nargs + nkwargs);
        return NULL;
    }
  • Documentation: property, whose signature is property(fget=None, fset=None, fdel=None, doc=None).

Related arity-validation gaps in other builtin constructors: #8461, #8462.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions