Index: Objects/typeobject.c =================================================================== --- Objects/typeobject.c (revision 54211) +++ Objects/typeobject.c (working copy) @@ -2376,25 +2376,55 @@ /* The base type of all types (eventually)... except itself. */ +/* You may wonder why object.__new__() only complains about arguments + when object.__init__() is not overridden, and vice versa. + + Consider the use cases: + + - When neither is overridden, we want to hear complaints about excess + (i.e., any) arguments, since their presence could indicate there's a bug. + + - When defining an Immutable type, we are likely to override only + __new__(), since __init__() is called too late to initialize an Immutable + object. Since __new__() defines the signature for the type, it would be + a pain to have to override __init__() just to stop it from complaining + about excess arguments. + + - When defining a Mutable type, we are likely to override only __init__(). + So here the converse reasoning applies: we don't want to have to override + __new__() just to stop it from complaining. + + Together, the last two use cases make it unattractive to put an + unconditional check for excess arguments in either __new__() or __init__(); + and yet, the first use case requires a check *somewhere*. The solution it + to make the check conditional on whether the *other* method is being + overridden. +*/ + +/* Forward */ +static PyObject * +object_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + static int object_init(PyObject *self, PyObject *args, PyObject *kwds) { + PyTypeObject *type = self->ob_type; + if (type->tp_new == object_new && (PyTuple_GET_SIZE(args) || + (kwds && PyDict_Check(kwds) && PyDict_Size(kwds)))) { + PyErr_SetString(PyExc_TypeError, + "object.__init__() takes no parameters"); + return -1; + } return 0; } -/* If we don't have a tp_new for a new-style class, new will use this one. - Therefore this should take no arguments/keywords. However, this new may - also be inherited by objects that define a tp_init but no tp_new. These - objects WILL pass argumets to tp_new, because it gets the same args as - tp_init. So only allow arguments if we aren't using the default init, in - which case we expect init to handle argument parsing. */ static PyObject * object_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { if (type->tp_init == object_init && (PyTuple_GET_SIZE(args) || (kwds && PyDict_Check(kwds) && PyDict_Size(kwds)))) { PyErr_SetString(PyExc_TypeError, - "default __new__ takes no parameters"); + "object.__new__() takes no parameters"); return NULL; } return type->tp_alloc(type, 0);