[issue27157] Unhelpful error message when one calls a subclass of type with a custom metaclass
Eryk Sun
report at bugs.python.org
Sun May 29 23:17:17 EDT 2016
Eryk Sun added the comment:
All types are instances of `type`, so the single argument case makes sense to me as a 'constructor'. It always returns an instance of `type`, just not a new instance.
>>> X = type('X', (type,), {})
>>> type(X)
<class 'type'>
>>> isinstance(type(X), type)
True
OTOH, implementing this for subclasses of `type` doesn't generally make sense to me. That this is allowed (sometimes) is I think a mistake:
>>> X(X)
<class 'type'>
>>> isinstance(X(X), X)
False
PyType_CheckExact(metatype) isn't checking that metatype is `type`. It's checking that the type of metatype is exactly `type`, which is true for `type` and immediate instances of type, i.e. normal metaclasses. But it's not the case for a metaclass that's an instance of another metaclass:
>>> Y = X('Y', (X,), {})
>>> Y(Y)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type() takes 1 or 3 arguments
Maybe I'm missing something, but it makes more sense to me if metatype is required to be *exactly* `type`, i.e. metatype == &PyType_Type, and that this check is used to gate the entire special case:
/* Special case: type(x) should return x->ob_type */
if (metatype == &PyType_Type) {
const Py_ssize_t nargs = PyTuple_GET_SIZE(args);
const Py_ssize_t nkwds = kwds == NULL ? 0 : PyDict_Size(kwds);
if (nargs == 1 && nkwds == 0) {
PyObject *x = PyTuple_GET_ITEM(args, 0);
Py_INCREF(Py_TYPE(x));
return (PyObject *) Py_TYPE(x);
}
/* SF bug 475327 -- if that didn't trigger, we need 3
arguments. but PyArg_ParseTupleAndKeywords below may give
a msg saying type() needs exactly 3. */
if (nargs + nkwds != 3) {
PyErr_SetString(PyExc_TypeError,
"type() takes 1 or 3 arguments");
return NULL;
}
}
This change yields the following behavior:
>>> X = type('X', (type,), {})
>>> type(X)
<class 'type'>
>>> type(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type() takes 1 or 3 arguments
>>> X()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Required argument 'name' (pos 1) not found
>>> X(X)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: type() argument 1 must be str, not type
----------
nosy: +eryksun
_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue27157>
_______________________________________
More information about the Python-bugs-list
mailing list