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.
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*.
......
......@@ -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:
......
......@@ -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
......
......@@ -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
......
......@@ -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. | |
......
......@@ -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 *);
......
......@@ -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;
......
......@@ -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
......
......@@ -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
......
......@@ -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):
......
......@@ -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
......
......@@ -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
......
......@@ -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')]
>>>
......
......@@ -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
......
......@@ -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.
......
......@@ -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;
......@@ -61,6 +66,12 @@ PyFunction_New(PyObject *code, PyObject *globals)
return (PyObject *)op;
}
PyObject *
PyFunction_New(PyObject *code, PyObject *globals)
{
return PyFunction_NewWithQualName(code, globals, NULL);
}
PyObject *
PyFunction_GetCode(PyObject *op)
{
......@@ -333,6 +344,32 @@ func_set_name(PyFunctionObject *op, PyObject *value)
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 *
func_get_defaults(PyFunctionObject *op)
{
......@@ -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;
}
......
......@@ -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"))
......
......@@ -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();
......
This diff is collapsed.
......@@ -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. */
......
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