give explicitly global functions and classes a global __qualname__ (closes #19301)

This commit is contained in:
Benjamin Peterson 2013-10-19 16:01:13 -04:00
parent 3586673703
commit 3d9e481ece
6 changed files with 3263 additions and 3453 deletions

View File

@ -369,12 +369,13 @@ def _call_with_frames_removed(f, *args, **kwds):
# free vars) # free vars)
# Python 3.4a1 3270 (various tweaks to the __class__ closure) # Python 3.4a1 3270 (various tweaks to the __class__ closure)
# Python 3.4a1 3280 (remove implicit class argument) # Python 3.4a1 3280 (remove implicit class argument)
# Python 3.4a4 3290 (changes to __qualname__ computation)
# #
# MAGIC must change whenever the bytecode emitted by the compiler may no # MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually # longer be understood by older implementations of the eval loop (usually
# due to the addition of new opcodes). # due to the addition of new opcodes).
MAGIC_NUMBER = (3280).to_bytes(2, 'little') + b'\r\n' MAGIC_NUMBER = (3290).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__' _PYCACHE = '__pycache__'

View File

@ -4517,6 +4517,11 @@ order (MRO) for bases """
self.assertRaises(TypeError, type.__dict__['__qualname__'].__set__, self.assertRaises(TypeError, type.__dict__['__qualname__'].__set__,
str, 'Oink') str, 'Oink')
global Y
class Y:
pass
self.assertEqual(Y.__qualname__, 'Y')
def test_qualname_dict(self): def test_qualname_dict(self):
ns = {'__qualname__': 'some.name'} ns = {'__qualname__': 'some.name'}
tp = type('Foo', (), ns) tp = type('Foo', (), ns)

View File

@ -7,6 +7,9 @@ def global_function():
def inner_function(): def inner_function():
class LocalClass: class LocalClass:
pass pass
global inner_global_function
def inner_global_function():
pass
return LocalClass return LocalClass
return lambda: inner_function return lambda: inner_function
@ -116,6 +119,7 @@ class FunctionPropertiesTest(FuncAttrsTest):
'global_function.<locals>.inner_function') 'global_function.<locals>.inner_function')
self.assertEqual(global_function()()().__qualname__, self.assertEqual(global_function()()().__qualname__,
'global_function.<locals>.inner_function.<locals>.LocalClass') 'global_function.<locals>.inner_function.<locals>.LocalClass')
self.assertEqual(inner_global_function.__qualname__, 'inner_global_function')
self.b.__qualname__ = 'c' self.b.__qualname__ = 'c'
self.assertEqual(self.b.__qualname__, 'c') self.assertEqual(self.b.__qualname__, 'c')
self.b.__qualname__ = 'd' self.b.__qualname__ = 'd'

View File

@ -10,6 +10,9 @@ Projected release date: 2013-10-20
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #19301: Give classes and functions that are explicitly marked global a
global qualname.
- Issue #19279: UTF-7 decoder no more produces illegal strings. - Issue #19279: UTF-7 decoder no more produces illegal strings.
- Issue #16612: Add "Argument Clinic", a compile-time preprocessor for - Issue #16612: Add "Argument Clinic", a compile-time preprocessor for

View File

@ -650,9 +650,10 @@ compiler_exit_scope(struct compiler *c)
} }
static PyObject * static PyObject *
compiler_scope_qualname(struct compiler *c) compiler_scope_qualname(struct compiler *c, identifier scope_name)
{ {
Py_ssize_t stack_size, i; Py_ssize_t stack_size;
int global_scope;
_Py_static_string(dot, "."); _Py_static_string(dot, ".");
_Py_static_string(locals, "<locals>"); _Py_static_string(locals, "<locals>");
struct compiler_unit *u; struct compiler_unit *u;
@ -669,22 +670,41 @@ compiler_scope_qualname(struct compiler *c)
return NULL; return NULL;
stack_size = PyList_GET_SIZE(c->c_stack); stack_size = PyList_GET_SIZE(c->c_stack);
for (i = 0; i < stack_size; i++) { global_scope = stack_size <= 1;
capsule = PyList_GET_ITEM(c->c_stack, i); if (scope_name != NULL && !global_scope) {
int scope;
PyObject *mangled;
capsule = PyList_GET_ITEM(c->c_stack, stack_size - 1);
u = (struct compiler_unit *)PyCapsule_GetPointer(capsule, COMPILER_CAPSULE_NAME_COMPILER_UNIT); u = (struct compiler_unit *)PyCapsule_GetPointer(capsule, COMPILER_CAPSULE_NAME_COMPILER_UNIT);
assert(u); assert(u);
if (u->u_scope_type == COMPILER_SCOPE_MODULE) mangled = _Py_Mangle(u->u_private, scope_name);
continue; if (!mangled)
if (PyList_Append(seq, u->u_name)) return NULL;
goto _error; scope = PyST_GetScope(u->u_ste, mangled);
if (u->u_scope_type == COMPILER_SCOPE_FUNCTION) { Py_DECREF(mangled);
locals_str = _PyUnicode_FromId(&locals); assert(scope != GLOBAL_IMPLICIT);
if (locals_str == NULL) if (scope == GLOBAL_EXPLICIT)
goto _error; global_scope = 1;
if (PyList_Append(seq, locals_str)) }
if (!global_scope) {
Py_ssize_t i;
for (i = 1; 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);
assert(u->u_scope_type != COMPILER_SCOPE_MODULE);
if (PyList_Append(seq, u->u_name))
goto _error; 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; u = c->u;
if (PyList_Append(seq, u->u_name)) if (PyList_Append(seq, u->u_name))
goto _error; goto _error;
@ -1649,7 +1669,7 @@ compiler_function(struct compiler *c, stmt_ty s)
VISIT_IN_SCOPE(c, stmt, st); VISIT_IN_SCOPE(c, stmt, st);
} }
co = assemble(c, 1); co = assemble(c, 1);
qualname = compiler_scope_qualname(c); qualname = compiler_scope_qualname(c, s->v.FunctionDef.name);
compiler_exit_scope(c); compiler_exit_scope(c);
if (qualname == NULL || co == NULL) { if (qualname == NULL || co == NULL) {
Py_XDECREF(qualname); Py_XDECREF(qualname);
@ -1722,7 +1742,7 @@ compiler_class(struct compiler *c, stmt_ty s)
} }
Py_DECREF(str); Py_DECREF(str);
/* store the __qualname__ */ /* store the __qualname__ */
str = compiler_scope_qualname(c); str = compiler_scope_qualname(c, s->v.ClassDef.name);
if (!str) { if (!str) {
compiler_exit_scope(c); compiler_exit_scope(c);
return 0; return 0;
@ -1862,7 +1882,7 @@ compiler_lambda(struct compiler *c, expr_ty e)
ADDOP_IN_SCOPE(c, RETURN_VALUE); ADDOP_IN_SCOPE(c, RETURN_VALUE);
} }
co = assemble(c, 1); co = assemble(c, 1);
qualname = compiler_scope_qualname(c); qualname = compiler_scope_qualname(c, NULL);
compiler_exit_scope(c); compiler_exit_scope(c);
if (qualname == NULL || co == NULL) if (qualname == NULL || co == NULL)
return 0; return 0;
@ -3139,7 +3159,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, identifier name,
} }
co = assemble(c, 1); co = assemble(c, 1);
qualname = compiler_scope_qualname(c); qualname = compiler_scope_qualname(c, NULL);
compiler_exit_scope(c); compiler_exit_scope(c);
if (qualname == NULL || co == NULL) if (qualname == NULL || co == NULL)
goto error; goto error;

File diff suppressed because it is too large Load Diff