cpython: ecc7bff738e0 (original) (raw)

Mercurial > cpython

changeset 102491:ecc7bff738e0

Issue #27366: Implement PEP 487 - __init_subclass__ called when new subclasses defined - __set_name__ called when descriptors are part of a class definition [#27366]

Nick Coghlan ncoghlan@gmail.com
date Sat, 30 Jul 2016 16:26:03 +1000
parents 520c0d391f00
children 92b3ce7ca95c
files Doc/reference/datamodel.rst Doc/whatsnew/3.6.rst Lib/test/test_builtin.py Lib/test/test_descrtut.py Lib/test/test_pydoc.py Lib/test/test_subclassinit.py Misc/ACKS Misc/NEWS Objects/typeobject.c
diffstat 9 files changed, 411 insertions(+), 24 deletions(-)[+] [-] Doc/reference/datamodel.rst 43 Doc/whatsnew/3.6.rst 20 Lib/test/test_builtin.py 20 Lib/test/test_descrtut.py 1 Lib/test/test_pydoc.py 3 Lib/test/test_subclassinit.py 244 Misc/ACKS 1 Misc/NEWS 4 Objects/typeobject.c 99

line wrap: on

line diff

--- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1492,6 +1492,12 @@ class' :attr:~object.__dict__. Called to delete the attribute on an instance instance of the owner class. +.. method:: object.set_name(self, owner, name) +

+ The attribute :attr:__objclass__ is interpreted by the :mod:inspect module as specifying the class where this object was defined (setting this appropriately can assist in runtime introspection of dynamic class attributes). @@ -1629,11 +1635,46 @@ Notes on using slots

+

+

+ +.. _metaclasses: + +Metaclasses +^^^^^^^^^^^ + By default, classes are constructed using :func:type. The class body is executed in a new namespace and the class name is bound locally to the result of type(name, bases, namespace).

--- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -110,6 +110,26 @@ evaluated at run time, and then formatte See :pep:498 and the main documentation at :ref:f-strings. +PEP 487: Simpler customization of class creation +------------------------------------------------ + +Upon subclassing a class, the __init_subclass__ classmethod (if defined) is +called on the base class. This makes it straightforward to write classes that +customize initialization of future subclasses without introducing the +complexity of a full custom metaclass. + +The descriptor protocol has also been expanded to include a new optional method, +__set_name__. Whenever a new class is defined, the new method will be called +on all descriptors included in the definition, providing them with a reference +to the class being defined and the name given to the descriptor within the +class namespace. + +Also see :pep:487 and the updated class customization documentation at +:ref:class-customization and :ref:descriptors. + +(Contributed by Martin Teichmann in :issue:27366) + + PYTHONMALLOC environment variable ---------------------------------

--- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1699,21 +1699,11 @@ class TestType(unittest.TestCase): self.assertEqual(x.spam(), 'spam42') self.assertEqual(x.to_bytes(2, 'little'), b'\x2a\x00')

def test_type_name(self): for name in 'A', '\xc4', '\U0001f40d', 'B.A', '42', '':

--- a/Lib/test/test_descrtut.py +++ b/Lib/test/test_descrtut.py @@ -182,6 +182,7 @@ You can get the information from the lis 'iadd', 'imul', 'init',

--- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -638,8 +638,9 @@ class PydocDocTest(unittest.TestCase): del expected['doc'] del expected['class'] # inspect resolves descriptors on type into methods, but vars doesn't,

methods = pydoc.allmethods(TestClass) self.assertDictEqual(methods, expected)

new file mode 100644 --- /dev/null +++ b/Lib/test/test_subclassinit.py @@ -0,0 +1,244 @@ +from unittest import TestCase, main +import sys +import types + + +class Test(TestCase):

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+ + +if name == "main":

--- a/Misc/ACKS +++ b/Misc/ACKS @@ -1475,6 +1475,7 @@ Amy Taylor Julian Taylor Monty Taylor Anatoly Techtonik +Martin Teichmann Gustavo Temple Mikhail Terekhov Victor TerrĂ³n

--- a/Misc/NEWS +++ b/Misc/NEWS @@ -31,6 +31,10 @@ Core and Builtins

--- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -53,10 +53,12 @@ static size_t method_cache_collisions = _Py_IDENTIFIER(getattribute); _Py_IDENTIFIER(getitem); _Py_IDENTIFIER(hash); +_Py_IDENTIFIER(init_subclass); _Py_IDENTIFIER(len); _Py_IDENTIFIER(module); _Py_IDENTIFIER(name); _Py_IDENTIFIER(new); +_Py_IDENTIFIER(set_name); _Py_IDENTIFIER(setitem); _Py_IDENTIFIER(builtins); @@ -2027,6 +2029,8 @@ static void object_dealloc(PyObject *); static int object_init(PyObject *, PyObject *, PyObject *); static int update_slot(PyTypeObject *, PyObject *); static void fixup_slot_dispatchers(PyTypeObject *); +static int set_names(PyTypeObject *); +static int init_subclass(PyTypeObject *, PyObject ); /

@@ -2269,7 +2274,6 @@ static PyObject * type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) { PyObject *name, *bases = NULL, *orig_dict, *dict = NULL;

@@ -2304,10 +2308,8 @@ type_new(PyTypeObject metatype, PyObjec } / Check arguments: (name, bases, dict) */

/* Determine the proper metatype to deal with this: */ @@ -2587,6 +2589,20 @@ type_new(PyTypeObject *metatype, PyObjec Py_DECREF(tmp); }

+ /* Add descriptors for custom slots from slots, or for dict */ mp = PyHeapType_GET_MEMBERS(et); slotoffset = base->tp_basicsize; @@ -2667,6 +2683,12 @@ type_new(PyTypeObject *metatype, PyObjec et->ht_cached_keys = _PyDict_NewKeysForClass(); }

+

+ Py_DECREF(dict); return (PyObject *)type; @@ -4312,6 +4334,19 @@ PyDoc_STRVAR(object_subclasshook_doc, "NotImplemented, the normal algorithm is used. Otherwise, it\n" "overrides the normal algorithm (and the outcome is cached).\n"); +static PyObject * +object_init_subclass(PyObject *cls, PyObject *arg) +{

+} + +PyDoc_STRVAR(object_init_subclass_doc, +"This method is called when a class is subclassed\n" +"\n" +"Whenever a class is subclassed, this method is called. The default\n" +"implementation does nothing. It may be overridden to extend\n" +"subclasses.\n"); + /* from PEP 3101, this code implements: @@ -4416,6 +4451,8 @@ static PyMethodDef object_methods[] = { PyDoc_STR("helper for pickle")}, {"subclasshook", object_subclasshook, METH_CLASS | METH_VARARGS, object_subclasshook_doc},

+

+

+} + +/* Call init_subclass on the parent of a newly generated type */ +static int +init_subclass(PyTypeObject *type, PyObject *kwds) +{

+

+

+

+

+

+} + /* recurse_down_subclasses() and update_subclasses() are mutually recursive functions to call a callback for all subclasses, but refraining from recursing into subclasses that define 'name'. */