Commit 86a36b50 authored by Antoine Pitrou's avatar Antoine Pitrou

PEP 3155 / issue #13448: Qualified name for classes and functions.

parent 0e86a584
...@@ -38,6 +38,16 @@ There are a few functions specific to Python functions. ...@@ -38,6 +38,16 @@ There are a few functions specific to Python functions.
object, the argument defaults and closure are set to *NULL*. 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) .. c:function:: PyObject* PyFunction_GetCode(PyObject *op)
Return the code object associated with the function object *op*. Return the code object associated with the function object *op*.
......
...@@ -465,6 +465,11 @@ PyFunction_New:PyObject*::+1: ...@@ -465,6 +465,11 @@ PyFunction_New:PyObject*::+1:
PyFunction_New:PyObject*:code:+1: PyFunction_New:PyObject*:code:+1:
PyFunction_New:PyObject*:globals:+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:int:::
PyFunction_SetClosure:PyObject*:op:0: PyFunction_SetClosure:PyObject*:op:0:
PyFunction_SetClosure:PyObject*:closure:+1: PyFunction_SetClosure:PyObject*:closure:+1:
......
...@@ -544,6 +544,24 @@ Glossary ...@@ -544,6 +544,24 @@ Glossary
for piece in food: for piece in food:
print(piece) 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 reference count
The number of references to an object. When the reference count of an The number of references to an object. When the reference count of an
object drops to zero, it is deallocated. Reference counting is object drops to zero, it is deallocated. Reference counting is
......
...@@ -2824,6 +2824,13 @@ types, where they are relevant. Some of these are not reported by the ...@@ -2824,6 +2824,13 @@ types, where they are relevant. Some of these are not reported by the
The name of the class or type. 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__ .. attribute:: class.__mro__
This attribute is a tuple of classes that are considered when looking for This attribute is a tuple of classes that are considered when looking for
......
...@@ -448,6 +448,11 @@ Callable types ...@@ -448,6 +448,11 @@ Callable types
+-------------------------+-------------------------------+-----------+ +-------------------------+-------------------------------+-----------+
| :attr:`__name__` | The function's name | Writable | | :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 | | :attr:`__module__` | The name of the module the | Writable |
| | function was defined in, or | | | | function was defined in, or | |
| | ``None`` if unavailable. | | | | ``None`` if unavailable. | |
......
...@@ -31,6 +31,7 @@ typedef struct { ...@@ -31,6 +31,7 @@ typedef struct {
PyObject *func_weakreflist; /* List of weak references */ PyObject *func_weakreflist; /* List of weak references */
PyObject *func_module; /* The __module__ attribute, can be anything */ PyObject *func_module; /* The __module__ attribute, can be anything */
PyObject *func_annotations; /* Annotations, a dict or NULL */ PyObject *func_annotations; /* Annotations, a dict or NULL */
PyObject *func_qualname; /* The qualified name */
/* Invariant: /* Invariant:
* func_closure contains the bindings for func_code->co_freevars, so * func_closure contains the bindings for func_code->co_freevars, so
...@@ -44,6 +45,7 @@ PyAPI_DATA(PyTypeObject) PyFunction_Type; ...@@ -44,6 +45,7 @@ PyAPI_DATA(PyTypeObject) PyFunction_Type;
#define PyFunction_Check(op) (Py_TYPE(op) == &PyFunction_Type) #define PyFunction_Check(op) (Py_TYPE(op) == &PyFunction_Type)
PyAPI_FUNC(PyObject *) PyFunction_New(PyObject *, PyObject *); 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_GetCode(PyObject *);
PyAPI_FUNC(PyObject *) PyFunction_GetGlobals(PyObject *); PyAPI_FUNC(PyObject *) PyFunction_GetGlobals(PyObject *);
PyAPI_FUNC(PyObject *) PyFunction_GetModule(PyObject *); PyAPI_FUNC(PyObject *) PyFunction_GetModule(PyObject *);
......
...@@ -418,7 +418,7 @@ typedef struct _heaptypeobject { ...@@ -418,7 +418,7 @@ typedef struct _heaptypeobject {
a given operator (e.g. __getitem__). a given operator (e.g. __getitem__).
see add_operators() in typeobject.c . */ see add_operators() in typeobject.c . */
PyBufferProcs as_buffer; PyBufferProcs as_buffer;
PyObject *ht_name, *ht_slots; PyObject *ht_name, *ht_slots, *ht_qualname;
/* here are optional user slots, followed by the members. */ /* here are optional user slots, followed by the members. */
} PyHeapTypeObject; } PyHeapTypeObject;
......
...@@ -166,7 +166,7 @@ def visiblename(name, all=None, obj=None): ...@@ -166,7 +166,7 @@ def visiblename(name, all=None, obj=None):
if name in {'__builtins__', '__doc__', '__file__', '__path__', if name in {'__builtins__', '__doc__', '__file__', '__path__',
'__module__', '__name__', '__slots__', '__package__', '__module__', '__name__', '__slots__', '__package__',
'__cached__', '__author__', '__credits__', '__date__', '__cached__', '__author__', '__credits__', '__date__',
'__version__'}: '__version__', '__qualname__'}:
return 0 return 0
# Private names are hidden, but special names are displayed. # Private names are hidden, but special names are displayed.
if name.startswith('__') and name.endswith('__'): return 1 if name.startswith('__') and name.endswith('__'): return 1
......
...@@ -16,7 +16,7 @@ cellvars: ('x',) ...@@ -16,7 +16,7 @@ cellvars: ('x',)
freevars: () freevars: ()
nlocals: 2 nlocals: 2
flags: 3 flags: 3
consts: ('None', '<code object g>') consts: ('None', '<code object g>', "'f.<locals>.g'")
>>> dump(f(4).__code__) >>> dump(f(4).__code__)
name: g name: g
......
...@@ -4492,9 +4492,14 @@ class DictProxyTests(unittest.TestCase): ...@@ -4492,9 +4492,14 @@ class DictProxyTests(unittest.TestCase):
self.assertEqual(type(C.__dict__), type(B.__dict__)) self.assertEqual(type(C.__dict__), type(B.__dict__))
def test_repr(self): def test_repr(self):
# Testing dict_proxy.__repr__ # Testing dict_proxy.__repr__.
dict_ = {k: v for k, v in self.C.__dict__.items()} # We can't blindly compare with the repr of another dict as ordering
self.assertEqual(repr(self.C.__dict__), 'dict_proxy({!r})'.format(dict_)) # 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): class PTypesLongInitTest(unittest.TestCase):
......
...@@ -339,6 +339,7 @@ Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, GENERATOR ...@@ -339,6 +339,7 @@ Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, GENERATOR
Constants: Constants:
0: None 0: None
1: <code object f at (.*), file "(.*)", line (.*)> 1: <code object f at (.*), file "(.*)", line (.*)>
2: 'tricky.<locals>.f'
Variable names: Variable names:
0: x 0: x
1: y 1: y
......
...@@ -2,6 +2,15 @@ from test import support ...@@ -2,6 +2,15 @@ from test import support
import types import types
import unittest import unittest
def global_function():
def inner_function():
class LocalClass:
pass
return LocalClass
return lambda: inner_function
class FuncAttrsTest(unittest.TestCase): class FuncAttrsTest(unittest.TestCase):
def setUp(self): def setUp(self):
class F: class F:
...@@ -96,6 +105,24 @@ class FunctionPropertiesTest(FuncAttrsTest): ...@@ -96,6 +105,24 @@ class FunctionPropertiesTest(FuncAttrsTest):
self.assertEqual(self.fi.a.__name__, 'a') self.assertEqual(self.fi.a.__name__, 'a')
self.cannot_set_attr(self.fi.a, "__name__", 'a', AttributeError) 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): def test___code__(self):
num_one, num_two = 7, 8 num_one, num_two = 7, 8
def a(): pass def a(): pass
......
...@@ -159,6 +159,7 @@ Use a __prepare__ method that returns an instrumented dict. ...@@ -159,6 +159,7 @@ Use a __prepare__ method that returns an instrumented dict.
... bar = 123 ... bar = 123
... ...
d['__module__'] = 'test.test_metaclass' d['__module__'] = 'test.test_metaclass'
d['__qualname__'] = 'C'
d['foo'] = 4 d['foo'] = 4
d['foo'] = 42 d['foo'] = 42
d['bar'] = 123 d['bar'] = 123
...@@ -177,12 +178,12 @@ Use a metaclass that doesn't derive from type. ...@@ -177,12 +178,12 @@ Use a metaclass that doesn't derive from type.
... b = 24 ... b = 24
... ...
meta: C () meta: C ()
ns: [('__module__', 'test.test_metaclass'), ('a', 42), ('b', 24)] ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)]
kw: [] kw: []
>>> type(C) is dict >>> type(C) is dict
True True
>>> print(sorted(C.items())) >>> 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. And again, with a __prepare__ attribute.
...@@ -199,11 +200,12 @@ And again, with a __prepare__ attribute. ...@@ -199,11 +200,12 @@ And again, with a __prepare__ attribute.
... ...
prepare: C () [('other', 'booh')] prepare: C () [('other', 'booh')]
d['__module__'] = 'test.test_metaclass' d['__module__'] = 'test.test_metaclass'
d['__qualname__'] = 'C'
d['a'] = 1 d['a'] = 1
d['a'] = 2 d['a'] = 2
d['b'] = 3 d['b'] = 3
meta: C () 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')] kw: [('other', 'booh')]
>>> >>>
......
...@@ -129,8 +129,8 @@ class ReprTests(unittest.TestCase): ...@@ -129,8 +129,8 @@ class ReprTests(unittest.TestCase):
self.assertIn(s.find("..."), [12, 13]) self.assertIn(s.find("..."), [12, 13])
def test_lambda(self): def test_lambda(self):
self.assertTrue(repr(lambda x: x).startswith( r = repr(lambda x: x)
"<function <lambda")) self.assertTrue(r.startswith("<function ReprTests.test_lambda.<locals>.<lambda"), r)
# XXX anonymous functions? see func_repr # XXX anonymous functions? see func_repr
def test_builtin_function(self): def test_builtin_function(self):
...@@ -278,13 +278,14 @@ class aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ...@@ -278,13 +278,14 @@ class aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
''') ''')
from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import qux from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import qux
# Unbound methods first # Unbound methods first
self.assertTrue(repr(qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod).startswith( r = repr(qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod)
'<function amethod')) self.assertTrue(r.startswith('<function aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod'), r)
# Bound method next # Bound method next
iqux = qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa() 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' \ '<bound method aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod of <%s.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa object at 0x' \
% (qux.__name__,) )) % (qux.__name__,) ), r)
def test_builtin_function(self): def test_builtin_function(self):
# XXX test built-in functions and methods with really long names # XXX test built-in functions and methods with really long names
......
...@@ -730,7 +730,7 @@ class SizeofTest(unittest.TestCase): ...@@ -730,7 +730,7 @@ class SizeofTest(unittest.TestCase):
check(x, size(vh + '12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) check(x, size(vh + '12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
# function # function
def func(): pass def func(): pass
check(func, size(h + '11P')) check(func, size(h + '12P'))
class c(): class c():
@staticmethod @staticmethod
def foo(): def foo():
...@@ -828,7 +828,7 @@ class SizeofTest(unittest.TestCase): ...@@ -828,7 +828,7 @@ class SizeofTest(unittest.TestCase):
# type # type
# (PyTypeObject + PyNumberMethods + PyMappingMethods + # (PyTypeObject + PyNumberMethods + PyMappingMethods +
# PySequenceMethods + PyBufferProcs) # 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) check(int, s)
# class # class
class newstyleclass(object): pass class newstyleclass(object): pass
......
...@@ -10,6 +10,8 @@ What's New in Python 3.3 Alpha 1? ...@@ -10,6 +10,8 @@ What's New in Python 3.3 Alpha 1?
Core and Builtins 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 - Issue #13436: Fix a bogus error message when an AST object was passed
an invalid integer value. an invalid integer value.
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#include "structmember.h" #include "structmember.h"
PyObject * PyObject *
PyFunction_New(PyObject *code, PyObject *globals) PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)
{ {
PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, PyFunctionObject *op = PyObject_GC_New(PyFunctionObject,
&PyFunction_Type); &PyFunction_Type);
...@@ -54,6 +54,11 @@ PyFunction_New(PyObject *code, PyObject *globals) ...@@ -54,6 +54,11 @@ PyFunction_New(PyObject *code, PyObject *globals)
Py_INCREF(module); Py_INCREF(module);
op->func_module = module; op->func_module = module;
} }
if (qualname)
op->func_qualname = qualname;
else
op->func_qualname = op->func_name;
Py_INCREF(op->func_qualname);
} }
else else
return NULL; return NULL;
...@@ -61,6 +66,12 @@ PyFunction_New(PyObject *code, PyObject *globals) ...@@ -61,6 +66,12 @@ PyFunction_New(PyObject *code, PyObject *globals)
return (PyObject *)op; return (PyObject *)op;
} }
PyObject *
PyFunction_New(PyObject *code, PyObject *globals)
{
return PyFunction_NewWithQualName(code, globals, NULL);
}
PyObject * PyObject *
PyFunction_GetCode(PyObject *op) PyFunction_GetCode(PyObject *op)
{ {
...@@ -333,6 +344,32 @@ func_set_name(PyFunctionObject *op, PyObject *value) ...@@ -333,6 +344,32 @@ func_set_name(PyFunctionObject *op, PyObject *value)
return 0; return 0;
} }
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 * static PyObject *
func_get_defaults(PyFunctionObject *op) func_get_defaults(PyFunctionObject *op)
{ {
...@@ -441,6 +478,7 @@ static PyGetSetDef func_getsetlist[] = { ...@@ -441,6 +478,7 @@ static PyGetSetDef func_getsetlist[] = {
(setter)func_set_annotations}, (setter)func_set_annotations},
{"__dict__", (getter)func_get_dict, (setter)func_set_dict}, {"__dict__", (getter)func_get_dict, (setter)func_set_dict},
{"__name__", (getter)func_get_name, (setter)func_set_name}, {"__name__", (getter)func_get_name, (setter)func_set_name},
{"__qualname__", (getter)func_get_qualname, (setter)func_set_qualname},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
...@@ -561,6 +599,7 @@ func_dealloc(PyFunctionObject *op) ...@@ -561,6 +599,7 @@ func_dealloc(PyFunctionObject *op)
Py_XDECREF(op->func_dict); Py_XDECREF(op->func_dict);
Py_XDECREF(op->func_closure); Py_XDECREF(op->func_closure);
Py_XDECREF(op->func_annotations); Py_XDECREF(op->func_annotations);
Py_XDECREF(op->func_qualname);
PyObject_GC_Del(op); PyObject_GC_Del(op);
} }
...@@ -568,7 +607,7 @@ static PyObject* ...@@ -568,7 +607,7 @@ static PyObject*
func_repr(PyFunctionObject *op) func_repr(PyFunctionObject *op)
{ {
return PyUnicode_FromFormat("<function %U at %p>", return PyUnicode_FromFormat("<function %U at %p>",
op->func_name, op); op->func_qualname, op);
} }
static int static int
...@@ -584,6 +623,7 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg) ...@@ -584,6 +623,7 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg)
Py_VISIT(f->func_dict); Py_VISIT(f->func_dict);
Py_VISIT(f->func_closure); Py_VISIT(f->func_closure);
Py_VISIT(f->func_annotations); Py_VISIT(f->func_annotations);
Py_VISIT(f->func_qualname);
return 0; return 0;
} }
......
...@@ -242,6 +242,19 @@ type_name(PyTypeObject *type, void *context) ...@@ -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 static int
type_set_name(PyTypeObject *type, PyObject *value, void *context) type_set_name(PyTypeObject *type, PyObject *value, void *context)
{ {
...@@ -286,6 +299,25 @@ type_set_name(PyTypeObject *type, PyObject *value, void *context) ...@@ -286,6 +299,25 @@ type_set_name(PyTypeObject *type, PyObject *value, void *context)
return 0; 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 * static PyObject *
type_module(PyTypeObject *type, void *context) type_module(PyTypeObject *type, void *context)
{ {
...@@ -631,6 +663,7 @@ type___subclasscheck__(PyObject *type, PyObject *inst) ...@@ -631,6 +663,7 @@ type___subclasscheck__(PyObject *type, PyObject *inst)
static PyGetSetDef type_getsets[] = { static PyGetSetDef type_getsets[] = {
{"__name__", (getter)type_name, (setter)type_set_name, NULL}, {"__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}, {"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL},
{"__module__", (getter)type_module, (setter)type_set_module, NULL}, {"__module__", (getter)type_module, (setter)type_set_module, NULL},
{"__abstractmethods__", (getter)type_abstractmethods, {"__abstractmethods__", (getter)type_abstractmethods,
...@@ -652,7 +685,7 @@ type_repr(PyTypeObject *type) ...@@ -652,7 +685,7 @@ type_repr(PyTypeObject *type)
Py_DECREF(mod); Py_DECREF(mod);
mod = NULL; mod = NULL;
} }
name = type_name(type, NULL); name = type_qualname(type, NULL);
if (name == NULL) if (name == NULL)
return NULL; return NULL;
...@@ -1955,7 +1988,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) ...@@ -1955,7 +1988,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{ {
PyObject *name, *bases, *dict; PyObject *name, *bases, *dict;
static char *kwlist[] = {"name", "bases", "dict", 0}; static char *kwlist[] = {"name", "bases", "dict", 0};
PyObject *slots, *tmp, *newslots; PyObject *qualname, *slots, *tmp, *newslots;
PyTypeObject *type, *base, *tmptype, *winner; PyTypeObject *type, *base, *tmptype, *winner;
PyHeapTypeObject *et; PyHeapTypeObject *et;
PyMemberDef *mp; PyMemberDef *mp;
...@@ -2032,6 +2065,18 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) ...@@ -2032,6 +2065,18 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
return NULL; 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 */ /* Check for a __slots__ sequence variable in dict, and count it */
slots = PyDict_GetItemString(dict, "__slots__"); slots = PyDict_GetItemString(dict, "__slots__");
nslots = 0; nslots = 0;
...@@ -2185,7 +2230,9 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) ...@@ -2185,7 +2230,9 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
/* Keep name and slots alive in the extended type object */ /* Keep name and slots alive in the extended type object */
et = (PyHeapTypeObject *)type; et = (PyHeapTypeObject *)type;
Py_INCREF(name); Py_INCREF(name);
Py_INCREF(qualname);
et->ht_name = name; et->ht_name = name;
et->ht_qualname = qualname;
et->ht_slots = slots; et->ht_slots = slots;
/* Initialize tp_flags */ /* Initialize tp_flags */
...@@ -2369,6 +2416,8 @@ PyObject* PyType_FromSpec(PyType_Spec *spec) ...@@ -2369,6 +2416,8 @@ PyObject* PyType_FromSpec(PyType_Spec *spec)
res->ht_name = PyUnicode_FromString(spec->name); res->ht_name = PyUnicode_FromString(spec->name);
if (!res->ht_name) if (!res->ht_name)
goto fail; goto fail;
res->ht_qualname = res->ht_name;
Py_INCREF(res->ht_qualname);
res->ht_type.tp_name = _PyUnicode_AsString(res->ht_name); res->ht_type.tp_name = _PyUnicode_AsString(res->ht_name);
if (!res->ht_type.tp_name) if (!res->ht_type.tp_name)
goto fail; goto fail;
...@@ -2568,6 +2617,7 @@ type_dealloc(PyTypeObject *type) ...@@ -2568,6 +2617,7 @@ type_dealloc(PyTypeObject *type)
*/ */
PyObject_Free((char *)type->tp_doc); PyObject_Free((char *)type->tp_doc);
Py_XDECREF(et->ht_name); Py_XDECREF(et->ht_name);
Py_XDECREF(et->ht_qualname);
Py_XDECREF(et->ht_slots); Py_XDECREF(et->ht_slots);
Py_TYPE(type)->tp_free((PyObject *)type); Py_TYPE(type)->tp_free((PyObject *)type);
} }
...@@ -2983,7 +3033,7 @@ object_repr(PyObject *self) ...@@ -2983,7 +3033,7 @@ object_repr(PyObject *self)
Py_DECREF(mod); Py_DECREF(mod);
mod = NULL; mod = NULL;
} }
name = type_name(type, NULL); name = type_qualname(type, NULL);
if (name == NULL) if (name == NULL)
return NULL; return NULL;
if (mod != NULL && PyUnicode_CompareWithASCIIString(mod, "builtins")) if (mod != NULL && PyUnicode_CompareWithASCIIString(mod, "builtins"))
......
...@@ -2687,9 +2687,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) ...@@ -2687,9 +2687,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
int kwdefaults = (oparg>>8) & 0xff; int kwdefaults = (oparg>>8) & 0xff;
int num_annotations = (oparg >> 16) & 0x7fff; int num_annotations = (oparg >> 16) & 0x7fff;
w = POP(); /* qualname */
v = POP(); /* code object */ v = POP(); /* code object */
x = PyFunction_New(v, f->f_globals); x = PyFunction_NewWithQualName(v, f->f_globals, w);
Py_DECREF(v); Py_DECREF(v);
Py_DECREF(w);
if (x != NULL && opcode == MAKE_CLOSURE) { if (x != NULL && opcode == MAKE_CLOSURE) {
v = POP(); v = POP();
......
This diff is collapsed.
...@@ -103,6 +103,7 @@ typedef unsigned short mode_t; ...@@ -103,6 +103,7 @@ typedef unsigned short mode_t;
tag: cpython-32 tag: cpython-32
Python 3.2a2 3180 (add DELETE_DEREF) Python 3.2a2 3180 (add DELETE_DEREF)
Python 3.3a0 3190 __class__ super closure changed 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 /* MAGIC must change whenever the bytecode emitted by the compiler may no
...@@ -115,7 +116,7 @@ typedef unsigned short mode_t; ...@@ -115,7 +116,7 @@ typedef unsigned short mode_t;
#define STRIFY(name) QUOTE(name) #define STRIFY(name) QUOTE(name)
#define MAJOR STRIFY(PY_MAJOR_VERSION) #define MAJOR STRIFY(PY_MAJOR_VERSION)
#define MINOR STRIFY(PY_MINOR_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 TAG "cpython-" MAJOR MINOR;
#define CACHEDIR "__pycache__" #define CACHEDIR "__pycache__"
/* Current magic word and string tag as globals. */ /* Current magic word and string tag as globals. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment