diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1459,6 +1459,9 @@ are always available. They are listed here in alphabetical order. See also :ref:`bltin-type-objects`. + .. versionchanged:: 3.6 + Subclasses of :class:`type` which don't override :meth:`__new__` may no + longer use the one-argument form to get the type of an object. .. function:: vars([object]) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -1000,6 +1000,19 @@ class ClassCreationTests(unittest.TestCase): with self.assertRaises(TypeError): X = types.new_class("X", (int(), C)) + def test_one_argument_type(self): + # Issue 27157: only type itself can use the one-argument form + self.assertIs(type(5), int) + class M(type): + pass + with self.assertRaises(TypeError): + M(5) + + class N(type, metaclass=M): + pass + with self.assertRaises(TypeError): + N(5) + class SimpleNamespaceTests(unittest.TestCase): diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2282,12 +2282,14 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) assert(args != NULL && PyTuple_Check(args)); assert(kwds == NULL || PyDict_Check(kwds)); - /* Special case: type(x) should return x->ob_type */ - { + /* Special case: type(x) should return x->ob_type + Issue 27157: we only want type itself to accept the one-argument form + we don't call PyType_CheckExact as that also allows subclasses */ + 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 (PyType_CheckExact(metatype) && nargs == 1 && nkwds == 0) { + if (nargs == 1 && nkwds == 0) { PyObject *x = PyTuple_GET_ITEM(args, 0); Py_INCREF(Py_TYPE(x)); return (PyObject *) Py_TYPE(x); @@ -2304,8 +2306,8 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) } /* Check arguments: (name, bases, dict) */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "UO!O!:type", kwlist, - &name, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "UO!O!:type.__new__", + kwlist, &name, &PyTuple_Type, &bases, &PyDict_Type, &orig_dict)) return NULL;