diff options
-rw-r--r-- | Doc/c-api/function.rst | 10 | ||||
-rw-r--r-- | Doc/data/refcounts.dat | 5 | ||||
-rw-r--r-- | Doc/glossary.rst | 18 | ||||
-rw-r--r-- | Doc/library/stdtypes.rst | 7 | ||||
-rw-r--r-- | Doc/reference/datamodel.rst | 5 | ||||
-rw-r--r-- | Include/funcobject.h | 2 | ||||
-rw-r--r-- | Include/object.h | 2 | ||||
-rwxr-xr-x | Lib/pydoc.py | 2 | ||||
-rw-r--r-- | Lib/test/test_code.py | 2 | ||||
-rw-r--r-- | Lib/test/test_descr.py | 11 | ||||
-rw-r--r-- | Lib/test/test_dis.py | 1 | ||||
-rw-r--r-- | Lib/test/test_funcattrs.py | 27 | ||||
-rw-r--r-- | Lib/test/test_metaclass.py | 8 | ||||
-rw-r--r-- | Lib/test/test_reprlib.py | 13 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 4 | ||||
-rw-r--r-- | Misc/NEWS | 2 | ||||
-rw-r--r-- | Objects/funcobject.c | 44 | ||||
-rw-r--r-- | Objects/typeobject.c | 56 | ||||
-rw-r--r-- | Python/ceval.c | 4 | ||||
-rw-r--r-- | Python/compile.c | 139 | ||||
-rw-r--r-- | Python/import.c | 3 |
21 files changed, 322 insertions, 43 deletions
diff --git a/Doc/c-api/function.rst b/Doc/c-api/function.rst index 31805fd..ad98322 100644 --- a/Doc/c-api/function.rst +++ b/Doc/c-api/function.rst @@ -38,6 +38,16 @@ There are a few functions specific to Python functions. object, the argument defaults and closure are set to *NULL*. +.. c:function:: PyObject* PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) + + As :c:func:`PyFunction_New`, but also allows to set the function object's + ``__qualname__`` attribute. *qualname* should be a unicode object or NULL; + if NULL, the ``__qualname__`` attribute is set to the same value as its + ``__name__`` attribute. + + .. versionadded:: 3.3 + + .. c:function:: PyObject* PyFunction_GetCode(PyObject *op) Return the code object associated with the function object *op*. diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index c7d7bd1..a1004ad 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -465,6 +465,11 @@ PyFunction_New:PyObject*::+1: PyFunction_New:PyObject*:code:+1: PyFunction_New:PyObject*:globals:+1: +PyFunction_NewWithQualName:PyObject*::+1: +PyFunction_NewWithQualName:PyObject*:code:+1: +PyFunction_NewWithQualName:PyObject*:globals:+1: +PyFunction_NewWithQualName:PyObject*:qualname:+1: + PyFunction_SetClosure:int::: PyFunction_SetClosure:PyObject*:op:0: PyFunction_SetClosure:PyObject*:closure:+1: diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 04b3fbb..f5ca0d9 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -544,6 +544,24 @@ Glossary for piece in food: print(piece) + qualified name + A dotted name showing the "path" from a module's global scope to a + class, function or method defined in that module, as defined in + :pep:`3155`. For top-level functions and classes, the qualified name + is the same as the object's name:: + + >>> class C: + ... class D: + ... def meth(self): + ... pass + ... + >>> C.__qualname__ + 'C' + >>> C.D.__qualname__ + 'C.D' + >>> C.D.meth.__qualname__ + 'C.D.meth' + reference count The number of references to an object. When the reference count of an object drops to zero, it is deallocated. Reference counting is diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 5bb4324..ee76cd3 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -2824,6 +2824,13 @@ types, where they are relevant. Some of these are not reported by the The name of the class or type. +.. attribute:: class.__qualname__ + + The :term:`qualified name` of the class or type. + + .. versionadded:: 3.3 + + .. attribute:: class.__mro__ This attribute is a tuple of classes that are considered when looking for diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index a93c09a..55fd76b 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -448,6 +448,11 @@ Callable types +-------------------------+-------------------------------+-----------+ | :attr:`__name__` | The function's name | Writable | +-------------------------+-------------------------------+-----------+ + | :attr:`__qualname__` | The function's | Writable | + | | :term:`qualified name` | | + | | | | + | | .. versionadded:: 3.3 | | + +-------------------------+-------------------------------+-----------+ | :attr:`__module__` | The name of the module the | Writable | | | function was defined in, or | | | | ``None`` if unavailable. | | diff --git a/Include/funcobject.h b/Include/funcobject.h index 521d87b..cc1426c 100644 --- a/Include/funcobject.h +++ b/Include/funcobject.h @@ -31,6 +31,7 @@ typedef struct { PyObject *func_weakreflist; /* List of weak references */ PyObject *func_module; /* The __module__ attribute, can be anything */ PyObject *func_annotations; /* Annotations, a dict or NULL */ + PyObject *func_qualname; /* The qualified name */ /* Invariant: * func_closure contains the bindings for func_code->co_freevars, so @@ -44,6 +45,7 @@ PyAPI_DATA(PyTypeObject) PyFunction_Type; #define PyFunction_Check(op) (Py_TYPE(op) == &PyFunction_Type) PyAPI_FUNC(PyObject *) PyFunction_New(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_NewWithQualName(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyFunction_GetCode(PyObject *); PyAPI_FUNC(PyObject *) PyFunction_GetGlobals(PyObject *); PyAPI_FUNC(PyObject *) PyFunction_GetModule(PyObject *); diff --git a/Include/object.h b/Include/object.h index b97f716..3cd5297 100644 --- a/Include/object.h +++ b/Include/object.h @@ -418,7 +418,7 @@ typedef struct _heaptypeobject { a given operator (e.g. __getitem__). see add_operators() in typeobject.c . */ PyBufferProcs as_buffer; - PyObject *ht_name, *ht_slots; + PyObject *ht_name, *ht_slots, *ht_qualname; /* here are optional user slots, followed by the members. */ } PyHeapTypeObject; diff --git a/Lib/pydoc.py b/Lib/pydoc.py index caae46a..b5dbde6 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -166,7 +166,7 @@ def visiblename(name, all=None, obj=None): if name in {'__builtins__', '__doc__', '__file__', '__path__', '__module__', '__name__', '__slots__', '__package__', '__cached__', '__author__', '__credits__', '__date__', - '__version__'}: + '__version__', '__qualname__'}: return 0 # Private names are hidden, but special names are displayed. if name.startswith('__') and name.endswith('__'): return 1 diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index e1c7a78..3377a7b 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -16,7 +16,7 @@ cellvars: ('x',) freevars: () nlocals: 2 flags: 3 -consts: ('None', '<code object g>') +consts: ('None', '<code object g>', "'f.<locals>.g'") >>> dump(f(4).__code__) name: g diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 15219db..4a7a9d2 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4492,9 +4492,14 @@ class DictProxyTests(unittest.TestCase): self.assertEqual(type(C.__dict__), type(B.__dict__)) def test_repr(self): - # Testing dict_proxy.__repr__ - dict_ = {k: v for k, v in self.C.__dict__.items()} - self.assertEqual(repr(self.C.__dict__), 'dict_proxy({!r})'.format(dict_)) + # Testing dict_proxy.__repr__. + # We can't blindly compare with the repr of another dict as ordering + # of keys and values is arbitrary and may differ. + r = repr(self.C.__dict__) + self.assertTrue(r.startswith('dict_proxy('), r) + self.assertTrue(r.endswith(')'), r) + for k, v in self.C.__dict__.items(): + self.assertIn('{!r}: {!r}'.format(k, v), r) class PTypesLongInitTest(unittest.TestCase): diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 4b0b02a..be38007 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -339,6 +339,7 @@ Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, GENERATOR Constants: 0: None 1: <code object f at (.*), file "(.*)", line (.*)> + 2: 'tricky.<locals>.f' Variable names: 0: x 1: y diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 4d19368..f4a38b9 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -2,6 +2,15 @@ from test import support import types import unittest + +def global_function(): + def inner_function(): + class LocalClass: + pass + return LocalClass + return lambda: inner_function + + class FuncAttrsTest(unittest.TestCase): def setUp(self): class F: @@ -96,6 +105,24 @@ class FunctionPropertiesTest(FuncAttrsTest): self.assertEqual(self.fi.a.__name__, 'a') self.cannot_set_attr(self.fi.a, "__name__", 'a', AttributeError) + def test___qualname__(self): + # PEP 3155 + self.assertEqual(self.b.__qualname__, 'FuncAttrsTest.setUp.<locals>.b') + self.assertEqual(FuncAttrsTest.setUp.__qualname__, 'FuncAttrsTest.setUp') + self.assertEqual(global_function.__qualname__, 'global_function') + self.assertEqual(global_function().__qualname__, + 'global_function.<locals>.<lambda>') + self.assertEqual(global_function()().__qualname__, + 'global_function.<locals>.inner_function') + self.assertEqual(global_function()()().__qualname__, + 'global_function.<locals>.inner_function.<locals>.LocalClass') + self.b.__qualname__ = 'c' + self.assertEqual(self.b.__qualname__, 'c') + self.b.__qualname__ = 'd' + self.assertEqual(self.b.__qualname__, 'd') + # __qualname__ must be a string + self.cannot_set_attr(self.b, '__qualname__', 7, TypeError) + def test___code__(self): num_one, num_two = 7, 8 def a(): pass diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py index 6862900..e6fe20a 100644 --- a/Lib/test/test_metaclass.py +++ b/Lib/test/test_metaclass.py @@ -159,6 +159,7 @@ Use a __prepare__ method that returns an instrumented dict. ... bar = 123 ... d['__module__'] = 'test.test_metaclass' + d['__qualname__'] = 'C' d['foo'] = 4 d['foo'] = 42 d['bar'] = 123 @@ -177,12 +178,12 @@ Use a metaclass that doesn't derive from type. ... b = 24 ... meta: C () - ns: [('__module__', 'test.test_metaclass'), ('a', 42), ('b', 24)] + ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)] kw: [] >>> type(C) is dict True >>> print(sorted(C.items())) - [('__module__', 'test.test_metaclass'), ('a', 42), ('b', 24)] + [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)] >>> And again, with a __prepare__ attribute. @@ -199,11 +200,12 @@ And again, with a __prepare__ attribute. ... prepare: C () [('other', 'booh')] d['__module__'] = 'test.test_metaclass' + d['__qualname__'] = 'C' d['a'] = 1 d['a'] = 2 d['b'] = 3 meta: C () - ns: [('__module__', 'test.test_metaclass'), ('a', 2), ('b', 3)] + ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 2), ('b', 3)] kw: [('other', 'booh')] >>> diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py index 439fa33..0365cea 100644 --- a/Lib/test/test_reprlib.py +++ b/Lib/test/test_reprlib.py @@ -129,8 +129,8 @@ class ReprTests(unittest.TestCase): self.assertIn(s.find("..."), [12, 13]) def test_lambda(self): - self.assertTrue(repr(lambda x: x).startswith( - "<function <lambda")) + r = repr(lambda x: x) + self.assertTrue(r.startswith("<function ReprTests.test_lambda.<locals>.<lambda"), r) # XXX anonymous functions? see func_repr def test_builtin_function(self): @@ -278,13 +278,14 @@ class aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ''') from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import qux # Unbound methods first - self.assertTrue(repr(qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod).startswith( - '<function amethod')) + r = repr(qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod) + self.assertTrue(r.startswith('<function aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod'), r) # Bound method next iqux = qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa() - self.assertTrue(repr(iqux.amethod).startswith( + r = repr(iqux.amethod) + self.assertTrue(r.startswith( '<bound method aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod of <%s.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa object at 0x' \ - % (qux.__name__,) )) + % (qux.__name__,) ), r) def test_builtin_function(self): # XXX test built-in functions and methods with really long names diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index ba0b592..3169f67 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -730,7 +730,7 @@ class SizeofTest(unittest.TestCase): check(x, size(vh + '12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) # function def func(): pass - check(func, size(h + '11P')) + check(func, size(h + '12P')) class c(): @staticmethod def foo(): @@ -828,7 +828,7 @@ class SizeofTest(unittest.TestCase): # type # (PyTypeObject + PyNumberMethods + PyMappingMethods + # PySequenceMethods + PyBufferProcs) - s = size(vh + 'P2P15Pl4PP9PP11PI') + size('16Pi17P 3P 10P 2P 2P') + s = size(vh + 'P2P15Pl4PP9PP11PI') + size('16Pi17P 3P 10P 2P 3P') check(int, s) # class class newstyleclass(object): pass @@ -10,6 +10,8 @@ What's New in Python 3.3 Alpha 1? Core and Builtins ----------------- +- PEP 3155 / issue #13448: Qualified name for classes and functions. + - Issue #13436: Fix a bogus error message when an AST object was passed an invalid integer value. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 45f9f57..2839a24 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -6,7 +6,7 @@ #include "structmember.h" PyObject * -PyFunction_New(PyObject *code, PyObject *globals) +PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) { PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); @@ -54,6 +54,11 @@ PyFunction_New(PyObject *code, PyObject *globals) Py_INCREF(module); op->func_module = module; } + if (qualname) + op->func_qualname = qualname; + else + op->func_qualname = op->func_name; + Py_INCREF(op->func_qualname); } else return NULL; @@ -62,6 +67,12 @@ PyFunction_New(PyObject *code, PyObject *globals) } PyObject * +PyFunction_New(PyObject *code, PyObject *globals) +{ + return PyFunction_NewWithQualName(code, globals, NULL); +} + +PyObject * PyFunction_GetCode(PyObject *op) { if (!PyFunction_Check(op)) { @@ -334,6 +345,32 @@ func_set_name(PyFunctionObject *op, PyObject *value) } static PyObject * +func_get_qualname(PyFunctionObject *op) +{ + Py_INCREF(op->func_qualname); + return op->func_qualname; +} + +static int +func_set_qualname(PyFunctionObject *op, PyObject *value) +{ + PyObject *tmp; + + /* Not legal to del f.__qualname__ or to set it to anything + * other than a string object. */ + if (value == NULL || !PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "__qualname__ must be set to a string object"); + return -1; + } + tmp = op->func_qualname; + Py_INCREF(value); + op->func_qualname = value; + Py_DECREF(tmp); + return 0; +} + +static PyObject * func_get_defaults(PyFunctionObject *op) { if (op->func_defaults == NULL) { @@ -441,6 +478,7 @@ static PyGetSetDef func_getsetlist[] = { (setter)func_set_annotations}, {"__dict__", (getter)func_get_dict, (setter)func_set_dict}, {"__name__", (getter)func_get_name, (setter)func_set_name}, + {"__qualname__", (getter)func_get_qualname, (setter)func_set_qualname}, {NULL} /* Sentinel */ }; @@ -561,6 +599,7 @@ func_dealloc(PyFunctionObject *op) Py_XDECREF(op->func_dict); Py_XDECREF(op->func_closure); Py_XDECREF(op->func_annotations); + Py_XDECREF(op->func_qualname); PyObject_GC_Del(op); } @@ -568,7 +607,7 @@ static PyObject* func_repr(PyFunctionObject *op) { return PyUnicode_FromFormat("<function %U at %p>", - op->func_name, op); + op->func_qualname, op); } static int @@ -584,6 +623,7 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg) Py_VISIT(f->func_dict); Py_VISIT(f->func_closure); Py_VISIT(f->func_annotations); + Py_VISIT(f->func_qualname); return 0; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index d53ae93..010120a 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -242,6 +242,19 @@ type_name(PyTypeObject *type, void *context) } } +static PyObject * +type_qualname(PyTypeObject *type, void *context) +{ + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + PyHeapTypeObject* et = (PyHeapTypeObject*)type; + Py_INCREF(et->ht_qualname); + return et->ht_qualname; + } + else { + return type_name(type, context); + } +} + static int type_set_name(PyTypeObject *type, PyObject *value, void *context) { @@ -286,6 +299,25 @@ type_set_name(PyTypeObject *type, PyObject *value, void *context) return 0; } +static int +type_set_qualname(PyTypeObject *type, PyObject *value, void *context) +{ + PyHeapTypeObject* et; + + if (!PyUnicode_Check(value)) { + PyErr_Format(PyExc_TypeError, + "can only assign string to %s.__qualname__, not '%s'", + type->tp_name, Py_TYPE(value)->tp_name); + return -1; + } + + et = (PyHeapTypeObject*)type; + Py_INCREF(value); + Py_DECREF(et->ht_qualname); + et->ht_qualname = value; + return 0; +} + static PyObject * type_module(PyTypeObject *type, void *context) { @@ -631,6 +663,7 @@ type___subclasscheck__(PyObject *type, PyObject *inst) static PyGetSetDef type_getsets[] = { {"__name__", (getter)type_name, (setter)type_set_name, NULL}, + {"__qualname__", (getter)type_qualname, (setter)type_set_qualname, NULL}, {"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL}, {"__module__", (getter)type_module, (setter)type_set_module, NULL}, {"__abstractmethods__", (getter)type_abstractmethods, @@ -652,7 +685,7 @@ type_repr(PyTypeObject *type) Py_DECREF(mod); mod = NULL; } - name = type_name(type, NULL); + name = type_qualname(type, NULL); if (name == NULL) return NULL; @@ -1955,7 +1988,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) { PyObject *name, *bases, *dict; static char *kwlist[] = {"name", "bases", "dict", 0}; - PyObject *slots, *tmp, *newslots; + PyObject *qualname, *slots, *tmp, *newslots; PyTypeObject *type, *base, *tmptype, *winner; PyHeapTypeObject *et; PyMemberDef *mp; @@ -2032,6 +2065,18 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) return NULL; } + /* Check for a __qualname__ variable in dict */ + qualname = PyDict_GetItemString(dict, "__qualname__"); + if (qualname == NULL) { + qualname = name; + } + else { + if (PyDict_DelItemString(dict, "__qualname__") < 0) { + Py_DECREF(bases); + return NULL; + } + } + /* Check for a __slots__ sequence variable in dict, and count it */ slots = PyDict_GetItemString(dict, "__slots__"); nslots = 0; @@ -2185,7 +2230,9 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) /* Keep name and slots alive in the extended type object */ et = (PyHeapTypeObject *)type; Py_INCREF(name); + Py_INCREF(qualname); et->ht_name = name; + et->ht_qualname = qualname; et->ht_slots = slots; /* Initialize tp_flags */ @@ -2369,6 +2416,8 @@ PyObject* PyType_FromSpec(PyType_Spec *spec) res->ht_name = PyUnicode_FromString(spec->name); if (!res->ht_name) goto fail; + res->ht_qualname = res->ht_name; + Py_INCREF(res->ht_qualname); res->ht_type.tp_name = _PyUnicode_AsString(res->ht_name); if (!res->ht_type.tp_name) goto fail; @@ -2568,6 +2617,7 @@ type_dealloc(PyTypeObject *type) */ PyObject_Free((char *)type->tp_doc); Py_XDECREF(et->ht_name); + Py_XDECREF(et->ht_qualname); Py_XDECREF(et->ht_slots); Py_TYPE(type)->tp_free((PyObject *)type); } @@ -2983,7 +3033,7 @@ object_repr(PyObject *self) Py_DECREF(mod); mod = NULL; } - name = type_name(type, NULL); + name = type_qualname(type, NULL); if (name == NULL) return NULL; if (mod != NULL && PyUnicode_CompareWithASCIIString(mod, "builtins")) diff --git a/Python/ceval.c b/Python/ceval.c index 8d980fd..ed82b94 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2687,9 +2687,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) int kwdefaults = (oparg>>8) & 0xff; int num_annotations = (oparg >> 16) & 0x7fff; + w = POP(); /* qualname */ v = POP(); /* code object */ - x = PyFunction_New(v, f->f_globals); + x = PyFunction_NewWithQualName(v, f->f_globals, w); Py_DECREF(v); + Py_DECREF(w); if (x != NULL && opcode == MAKE_CLOSURE) { v = POP(); diff --git a/Python/compile.c b/Python/compile.c index 9067722..849f487 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -90,6 +90,13 @@ struct fblockinfo { basicblock *fb_block; }; +enum { + COMPILER_SCOPE_MODULE, + COMPILER_SCOPE_CLASS, + COMPILER_SCOPE_FUNCTION, + COMPILER_SCOPE_COMPREHENSION, +}; + /* The following items change on entry and exit of code blocks. They must be saved and restored when returning to a block. */ @@ -97,6 +104,9 @@ struct compiler_unit { PySTEntryObject *u_ste; PyObject *u_name; + PyObject *u_qualname; /* dot-separated qualified name (lazy) */ + int u_scope_type; + /* The following fields are dicts that map objects to the index of them in co_XXX. The index is used as the argument for opcodes that refer to those collections. @@ -149,7 +159,7 @@ struct compiler { PyArena *c_arena; /* pointer to memory allocation arena */ }; -static int compiler_enter_scope(struct compiler *, identifier, void *, int); +static int compiler_enter_scope(struct compiler *, identifier, int, void *, int); static void compiler_free(struct compiler *); static basicblock *compiler_new_block(struct compiler *); static int compiler_next_instr(struct compiler *, basicblock *); @@ -457,6 +467,7 @@ compiler_unit_free(struct compiler_unit *u) } Py_CLEAR(u->u_ste); Py_CLEAR(u->u_name); + Py_CLEAR(u->u_qualname); Py_CLEAR(u->u_consts); Py_CLEAR(u->u_names); Py_CLEAR(u->u_varnames); @@ -467,8 +478,8 @@ compiler_unit_free(struct compiler_unit *u) } static int -compiler_enter_scope(struct compiler *c, identifier name, void *key, - int lineno) +compiler_enter_scope(struct compiler *c, identifier name, + int scope_type, void *key, int lineno) { struct compiler_unit *u; @@ -479,6 +490,7 @@ compiler_enter_scope(struct compiler *c, identifier name, void *key, return 0; } memset(u, 0, sizeof(struct compiler_unit)); + u->u_scope_type = scope_type; u->u_argcount = 0; u->u_kwonlyargcount = 0; u->u_ste = PySymtable_Lookup(c->c_st, key); @@ -566,6 +578,59 @@ compiler_exit_scope(struct compiler *c) } +static PyObject * +compiler_scope_qualname(struct compiler *c) +{ + Py_ssize_t stack_size, i; + _Py_static_string(dot, "."); + _Py_static_string(locals, "<locals>"); + struct compiler_unit *u; + PyObject *capsule, *name, *seq, *dot_str, *locals_str; + + u = c->u; + if (u->u_qualname != NULL) { + Py_INCREF(u->u_qualname); + return u->u_qualname; + } + + seq = PyList_New(0); + if (seq == NULL) + return NULL; + + stack_size = PyList_GET_SIZE(c->c_stack); + for (i = 0; i < stack_size; i++) { + capsule = PyList_GET_ITEM(c->c_stack, i); + u = (struct compiler_unit *)PyCapsule_GetPointer(capsule, COMPILER_CAPSULE_NAME_COMPILER_UNIT); + assert(u); + if (u->u_scope_type == COMPILER_SCOPE_MODULE) + continue; + if (PyList_Append(seq, u->u_name)) + goto _error; + if (u->u_scope_type == COMPILER_SCOPE_FUNCTION) { + locals_str = _PyUnicode_FromId(&locals); + if (locals_str == NULL) + goto _error; + if (PyList_Append(seq, locals_str)) + goto _error; + } + } + u = c->u; + if (PyList_Append(seq, u->u_name)) + goto _error; + dot_str = _PyUnicode_FromId(&dot); + if (dot_str == NULL) + goto _error; + name = PyUnicode_Join(dot_str, seq); + Py_DECREF(seq); + u->u_qualname = name; + Py_XINCREF(name); + return name; + +_error: + Py_XDECREF(seq); + return NULL; +} + /* Allocate a new block and return a pointer to it. Returns NULL on error. */ @@ -862,9 +927,9 @@ opcode_stack_effect(int opcode, int oparg) case CALL_FUNCTION_VAR_KW: return -NARGS(oparg)-2; case MAKE_FUNCTION: - return -NARGS(oparg) - ((oparg >> 16) & 0xffff); + return -1 -NARGS(oparg) - ((oparg >> 16) & 0xffff); case MAKE_CLOSURE: - return -1 - NARGS(oparg) - ((oparg >> 16) & 0xffff); + return -2 - NARGS(oparg) - ((oparg >> 16) & 0xffff); #undef NARGS case BUILD_SLICE: if (oparg == 3) @@ -1194,7 +1259,7 @@ compiler_mod(struct compiler *c, mod_ty mod) return NULL; } /* Use 0 for firstlineno initially, will fixup in assemble(). */ - if (!compiler_enter_scope(c, module, mod, 0)) + if (!compiler_enter_scope(c, module, COMPILER_SCOPE_MODULE, mod, 0)) return NULL; switch (mod->kind) { case Module_kind: @@ -1270,11 +1335,15 @@ compiler_lookup_arg(PyObject *dict, PyObject *name) } static int -compiler_make_closure(struct compiler *c, PyCodeObject *co, int args) +compiler_make_closure(struct compiler *c, PyCodeObject *co, int args, PyObject *qualname) { int i, free = PyCode_GetNumFree(co); + if (qualname == NULL) + qualname = co->co_name; + if (free == 0) { ADDOP_O(c, LOAD_CONST, (PyObject*)co, consts); + ADDOP_O(c, LOAD_CONST, qualname, consts); ADDOP_I(c, MAKE_FUNCTION, args); return 1; } @@ -1311,6 +1380,7 @@ compiler_make_closure(struct compiler *c, PyCodeObject *co, int args) } ADDOP_I(c, BUILD_TUPLE, free); ADDOP_O(c, LOAD_CONST, (PyObject*)co, consts); + ADDOP_O(c, LOAD_CONST, qualname, consts); ADDOP_I(c, MAKE_CLOSURE, args); return 1; } @@ -1452,7 +1522,7 @@ static int compiler_function(struct compiler *c, stmt_ty s) { PyCodeObject *co; - PyObject *first_const = Py_None; + PyObject *qualname, *first_const = Py_None; arguments_ty args = s->v.FunctionDef.args; expr_ty returns = s->v.FunctionDef.returns; asdl_seq* decos = s->v.FunctionDef.decorator_list; @@ -1478,7 +1548,8 @@ compiler_function(struct compiler *c, stmt_ty s) return 0; assert((num_annotations & 0xFFFF) == num_annotations); - if (!compiler_enter_scope(c, s->v.FunctionDef.name, (void *)s, + if (!compiler_enter_scope(c, s->v.FunctionDef.name, + COMPILER_SCOPE_FUNCTION, (void *)s, s->lineno)) return 0; @@ -1500,14 +1571,19 @@ compiler_function(struct compiler *c, stmt_ty s) VISIT_IN_SCOPE(c, stmt, st); } co = assemble(c, 1); + qualname = compiler_scope_qualname(c); compiler_exit_scope(c); - if (co == NULL) + if (qualname == NULL || co == NULL) { + Py_XDECREF(qualname); + Py_XDECREF(co); return 0; + } arglength = asdl_seq_LEN(args->defaults); arglength |= kw_default_count << 8; arglength |= num_annotations << 16; - compiler_make_closure(c, co, arglength); + compiler_make_closure(c, co, arglength, qualname); + Py_DECREF(qualname); Py_DECREF(co); /* decorators */ @@ -1542,7 +1618,8 @@ compiler_class(struct compiler *c, stmt_ty s) */ /* 1. compile the class body into a code object */ - if (!compiler_enter_scope(c, s->v.ClassDef.name, (void *)s, s->lineno)) + if (!compiler_enter_scope(c, s->v.ClassDef.name, + COMPILER_SCOPE_CLASS, (void *)s, s->lineno)) return 0; /* this block represents what we do in the new scope */ { @@ -1572,6 +1649,21 @@ compiler_class(struct compiler *c, stmt_ty s) return 0; } Py_DECREF(str); + /* store the __qualname__ */ + str = compiler_scope_qualname(c); + if (!str) { + compiler_exit_scope(c); + return 0; + } + ADDOP_O(c, LOAD_CONST, str, consts); + Py_DECREF(str); + str = PyUnicode_InternFromString("__qualname__"); + if (!str || !compiler_nameop(c, str, Store)) { + Py_XDECREF(str); + compiler_exit_scope(c); + return 0; + } + Py_DECREF(str); /* compile the body proper */ if (!compiler_body(c, s->v.ClassDef.body)) { compiler_exit_scope(c); @@ -1608,7 +1700,7 @@ compiler_class(struct compiler *c, stmt_ty s) ADDOP(c, LOAD_BUILD_CLASS); /* 3. load a function (or closure) made from the code object */ - compiler_make_closure(c, co, 0); + compiler_make_closure(c, co, 0, NULL); Py_DECREF(co); /* 4. load class name */ @@ -1659,6 +1751,7 @@ static int compiler_lambda(struct compiler *c, expr_ty e) { PyCodeObject *co; + PyObject *qualname; static identifier name; int kw_default_count = 0, arglength; arguments_ty args = e->v.Lambda.args; @@ -1678,7 +1771,8 @@ compiler_lambda(struct compiler *c, expr_ty e) } if (args->defaults) VISIT_SEQ(c, expr, args->defaults); - if (!compiler_enter_scope(c, name, (void *)e, e->lineno)) + if (!compiler_enter_scope(c, name, COMPILER_SCOPE_FUNCTION, + (void *)e, e->lineno)) return 0; /* Make None the first constant, so the lambda can't have a @@ -1696,13 +1790,15 @@ compiler_lambda(struct compiler *c, expr_ty e) ADDOP_IN_SCOPE(c, RETURN_VALUE); } co = assemble(c, 1); + qualname = compiler_scope_qualname(c); compiler_exit_scope(c); - if (co == NULL) + if (qualname == NULL || co == NULL) return 0; arglength = asdl_seq_LEN(args->defaults); arglength |= kw_default_count << 8; - compiler_make_closure(c, co, arglength); + compiler_make_closure(c, co, arglength, qualname); + Py_DECREF(qualname); Py_DECREF(co); return 1; @@ -2916,11 +3012,13 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name, { PyCodeObject *co = NULL; expr_ty outermost_iter; + PyObject *qualname = NULL; outermost_iter = ((comprehension_ty) asdl_seq_GET(generators, 0))->iter; - if (!compiler_enter_scope(c, name, (void *)e, e->lineno)) + if (!compiler_enter_scope(c, name, COMPILER_SCOPE_COMPREHENSION, + (void *)e, e->lineno)) goto error; if (type != COMP_GENEXP) { @@ -2953,12 +3051,14 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name, } co = assemble(c, 1); + qualname = compiler_scope_qualname(c); compiler_exit_scope(c); - if (co == NULL) + if (qualname == NULL || co == NULL) goto error; - if (!compiler_make_closure(c, co, 0)) + if (!compiler_make_closure(c, co, 0, qualname)) goto error; + Py_DECREF(qualname); Py_DECREF(co); VISIT(c, expr, outermost_iter); @@ -2968,6 +3068,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name, error_in_scope: compiler_exit_scope(c); error: + Py_XDECREF(qualname); Py_XDECREF(co); return 0; } diff --git a/Python/import.c b/Python/import.c index 6c6d1f6..7199230 100644 --- a/Python/import.c +++ b/Python/import.c @@ -103,6 +103,7 @@ typedef unsigned short mode_t; tag: cpython-32 Python 3.2a2 3180 (add DELETE_DEREF) Python 3.3a0 3190 __class__ super closure changed + Python 3.3a0 3200 (__qualname__ added) */ /* MAGIC must change whenever the bytecode emitted by the compiler may no @@ -115,7 +116,7 @@ typedef unsigned short mode_t; #define STRIFY(name) QUOTE(name) #define MAJOR STRIFY(PY_MAJOR_VERSION) #define MINOR STRIFY(PY_MINOR_VERSION) -#define MAGIC (3190 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (3200 | ((long)'\r'<<16) | ((long)'\n'<<24)) #define TAG "cpython-" MAJOR MINOR; #define CACHEDIR "__pycache__" /* Current magic word and string tag as globals. */ |