Index: Objects/typeobject.c =================================================================== --- Objects/typeobject.c (revision 54463) +++ Objects/typeobject.c (working copy) @@ -2391,26 +2391,68 @@ /* 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: + + 1. When neither is overridden, we want to hear complaints about + excess (i.e., any) arguments, since their presence could + indicate there's a bug. + + 2. 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. + + 3. 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. + + 4. When __init__() is overridden, and the subclass __init__() calls + object.__init__(), the latter should complain about excess + arguments; ditto for __new__(). + + Use cases 2 and 3 make it unattractive to unconditionally check for + excess arguments. The best solution that addresses all four use + cases is as follows: __init__() complains about excess arguments + unless __new__() is overridden and __init__() is not overridden; + symmetrically, __new__() complains about excess arguments unless + __init__() is overridden and __new__() is not 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 || type->tp_init != object_init) { + if (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"); - return NULL; + if (type->tp_init == object_init || type-> tp_new != object_new) { + if (PyTuple_GET_SIZE(args) || + (kwds && PyDict_Check(kwds) && PyDict_Size(kwds))) { + PyErr_SetString(PyExc_TypeError, + "object.__new__() takes no parameters"); + return NULL; + } } return type->tp_alloc(type, 0); } Index: Misc/NEWS =================================================================== --- Misc/NEWS (revision 54463) +++ Misc/NEWS (working copy) @@ -12,6 +12,10 @@ Core and builtins ----------------- +- The object.__init__() and object.__new__() methods are now stricter + in rejecting excess arguments. The only time when either allows + excess arguments is when it is not overridden and the other one is. + - Patch #1675423: PyComplex_AsCComplex() now tries to convert an object to complex using its __complex__() method before falling back to the __float__() method. Therefore, the functions in the cmath module now Index: Lib/string.py =================================================================== --- Lib/string.py (revision 54463) +++ Lib/string.py (working copy) @@ -108,7 +108,7 @@ """ def __init__(cls, name, bases, dct): - super(_TemplateMetaclass, cls).__init__(name, bases, dct) + super(_TemplateMetaclass, cls).__init__() if 'pattern' in dct: pattern = cls.pattern else: Index: Lib/test/test_init_new.py =================================================================== --- Lib/test/test_init_new.py (revision 0) +++ Lib/test/test_init_new.py (revision 0) @@ -0,0 +1,96 @@ +doctests = """ + +Check that object.__new__() and object.__init__() complain about +excess arguments *unless* the specific condition is met that the +current method is not overridden and the other is. See the long +comment in typeobject.c just abouve object_new() and object_init(); +cases corresponde to the cases there. + +Case 1: neither __init__() nor __new__() overridden. + + >>> class OverrideNeither(object): + ... pass + ... + >>> a = OverrideNeither() + >>> a = OverrideNeither(1) + Traceback (most recent call last): + [...] + TypeError: object.__new__() takes no parameters + >>> a = OverrideNeither(kw=1) + Traceback (most recent call last): + [...] + TypeError: object.__new__() takes no parameters + >>> + +Case 2: only __new__() overridden. + + >>> class OverrideNew(object): + ... def __new__(cls, foo, kw=0, *args, **kwds): + ... return object.__new__(cls, *args, **kwds) + ... + >>> a = OverrideNew(1) + >>> a = OverrideNew(1, 2) + >>> a = OverrideNew(1, kw=2) + >>> a = OverrideNew(1, 2, 3) + Traceback (most recent call last): + [...] + TypeError: object.__new__() takes no parameters + >>> a = OverrideNew(1, bar=1) + Traceback (most recent call last): + [...] + TypeError: object.__new__() takes no parameters + >>> + +Case 3: only __init__() overridden. + + >>> class OverrideInit(object): + ... def __init__(self, foo, kw=0, *args, **kwds): + ... object.__init__(self, *args, **kwds) + ... + >>> a = OverrideInit(1) + >>> a = OverrideInit(1, 2) + >>> a = OverrideInit(1, kw=2) + >>> a = OverrideInit(1, 2, 3) + Traceback (most recent call last): + [...] + TypeError: object.__init__() takes no parameters + >>> a = OverrideInit(1, bar=1) + Traceback (most recent call last): + [...] + TypeError: object.__init__() takes no parameters + >>> + +Case 4: both __init__() and __new__() overridden. + +(Note: __new__() only passes *args to its super; __init__() only +passes **kwds.) + + >>> class OverrideBoth(object): + ... def __new__(cls, foo, kw=0, *args, **kwds): + ... return object.__new__(cls, *args) + ... def __init__(self, foo, kw=0, *args, **kwds): + ... object.__init__(self, **kwds) + ... + >>> a = OverrideBoth(1) + >>> a = OverrideBoth(1, 2) + >>> a = OverrideBoth(1, kw=2) + >>> a = OverrideBoth(1, 2, 3) + Traceback (most recent call last): + [...] + TypeError: object.__new__() takes no parameters + >>> a = OverrideBoth(1, bar=1) + Traceback (most recent call last): + [...] + TypeError: object.__init__() takes no parameters + +""" + +__test__ = {'doctests' : doctests} + +def test_main(verbose=False): + from test import test_support + from test import test_init_new + test_support.run_doctest(test_init_new, verbose) + +if __name__ == "__main__": + test_main(verbose=True) Property changes on: Lib/test/test_init_new.py ___________________________________________________________________ Name: svn:keywords + Id Name: svn:eol-style + native