Bugs fixed
----------
+* The PEP 3155 ``__qualname__`` was incorrect for nested classes and
+ inner classes/functions declared as ``global``.
+
* The metaclass of a Python class was not inherited from its parent
class(es). It is now extracted from the list of base classes if not
provided explicitly using the Py3 ``metaclass`` keyword argument.
class ModuleNameMixin(object):
- def set_qualified_name(self, env, self_name):
- self.module_name = env.global_scope().qualified_name
- qualified_name = [self_name]
- entry = env.lookup(self_name)
- if not entry or not (entry.is_pyglobal and not entry.is_pyclass_attr):
- while env and not env.is_module_scope:
- if env.is_closure_scope:
- qualified_name.append('<locals>')
- qualified_name.append(env.name)
- env = env.parent_scope
- self.qualname = StringEncoding.EncodedString('.'.join(qualified_name[::-1]))
-
def get_py_mod_name(self, code):
return code.get_py_string_const(
- self.module_name, identifier=True)
+ self.module_name, identifier=True)
def get_py_qualified_name(self, code):
return code.get_py_string_const(
self.type = py_object_type
self.is_temp = 1
env.use_utility_code(UtilityCode.load_cached("CreateClass", "ObjectHandling.c"))
- #TODO(craig,haoyu) This should be moved to a better place
- self.set_qualified_name(env, self.name)
return self
def may_be_none(self):
self.doc = self.doc.coerce_to_pyobject(env)
self.type = py_object_type
self.is_temp = 1
- #TODO(craig,haoyu) This should be moved to a better place
- self.set_qualified_name(env, self.name)
return self
def may_be_none(self):
def analyse_types(self, env):
if self.binding:
self.analyse_default_args(env)
- #TODO(craig,haoyu) This should be moved to a better place
- self.set_qualified_name(env, self.def_node.name)
return self
def analyse_default_args(self, env):
return property
+class CalculateQualifiedNamesTransform(EnvTransform):
+ """
+ Calculate and store the '__qualname__' and the global
+ module name on some nodes.
+ """
+ def visit_ModuleNode(self, node):
+ self.module_name = self.global_scope().qualified_name
+ self.qualified_name = []
+ self._super = super(CalculateQualifiedNamesTransform, self)
+ self.visitchildren(node)
+ return node
+
+ def _set_qualname(self, node, name=None):
+ if name:
+ qualname = self.qualified_name[:]
+ qualname.append(name)
+ else:
+ qualname = self.qualified_name
+ node.qualname = EncodedString('.'.join(qualname))
+ node.module_name = self.module_name
+ self.visitchildren(node)
+ return node
+
+ def _append_name(self, name):
+ if name == '<lambda>':
+ self.qualified_name.append('<lambda>')
+ else:
+ entry = self.current_env().lookup_here(name)
+ if entry.is_pyglobal and not entry.is_pyclass_attr:
+ self.qualified_name = [name]
+ else:
+ self.qualified_name.append(name)
+
+ def visit_ClassNode(self, node):
+ return self._set_qualname(node, node.name)
+
+ def visit_PyClassNamespaceNode(self, node):
+ return self._set_qualname(node)
+
+ def visit_PyCFunctionNode(self, node):
+ return self._set_qualname(node, node.def_node.name)
+
+ def visit_FuncDefNode(self, node):
+ orig_qualified_name = self.qualified_name[:]
+ self._append_name(node.name)
+ self.qualified_name.append('<locals>')
+ self._super.visit_FuncDefNode(node)
+ self.qualified_name = orig_qualified_name
+ return node
+
+ def visit_ClassDefNode(self, node):
+ orig_qualified_name = self.qualified_name[:]
+ self._append_name(node.name)
+ self._super.visit_ClassDefNode(node)
+ self.qualified_name = orig_qualified_name
+ return node
+
+
class AnalyseExpressionsTransform(CythonTransform):
def visit_ModuleNode(self, node):
from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
from ParseTreeTransforms import InterpretCompilerDirectives, TransformBuiltinMethods
from ParseTreeTransforms import ExpandInplaceOperators, ParallelRangeTransform
+ from ParseTreeTransforms import CalculateQualifiedNamesTransform
from TypeInference import MarkParallelAssignments, MarkOverflowingArithmetic
from ParseTreeTransforms import AdjustDefByDirectives, AlignFunctionDefinitions
from ParseTreeTransforms import RemoveUnreachableCode, GilCheck
_check_c_declarations,
InlineDefNodeCalls(context),
AnalyseExpressionsTransform(context),
+ CalculateQualifiedNamesTransform(context),
FindInvalidUseOfFusedTypes(context),
CreateClosureClasses(context), ## After all lookups and type inference
ExpandInplaceOperators(context),
from TypeInference import PyObjectTypeInferer
PyObjectTypeInferer().infer_types(self)
+
class LocalScope(Scope):
# Does the function have a 'with gil:' block?
entry.original_cname = entry.cname
entry.cname = "%s->%s" % (Naming.cur_scope_cname, entry.cname)
+
class GeneratorExpressionScope(Scope):
"""Scope for generator expressions and comprehensions. As opposed
to generators, these can be easily inlined in some cases, so all
return self.declare_var(name, type, pos,
cname=cname, visibility=visibility)
+
class ClassScope(Scope):
# Abstract base class for namespace of
# Python class or extension type.
"in a property declaration")
return None
+
class CConstScope(Scope):
def __init__(self, const_base_type_scope):
def test_nested_qualname():
"""
- >>> func, lambda_func, XYZ = test_nested_qualname()
+ >>> outer, lambda_func, XYZ = test_nested_qualname()
- >>> func().__qualname__
+ >>> outer().__qualname__
'test_nested_qualname.<locals>.outer.<locals>.Test'
- >>> func().test.__qualname__
+ >>> outer().test.__qualname__
'test_nested_qualname.<locals>.outer.<locals>.Test.test'
- >>> func()().test.__qualname__
+ >>> outer()().test.__qualname__
'test_nested_qualname.<locals>.outer.<locals>.Test.test'
- >>> func()().test().__qualname__
+ >>> outer()().test().__qualname__
'XYZinner'
+ >>> outer()().test().Inner.__qualname__
+ 'XYZinner.Inner'
+ >>> outer()().test().Inner.inner.__qualname__
+ 'XYZinner.Inner.inner'
>>> lambda_func.__qualname__
'test_nested_qualname.<locals>.<lambda>'
>>> XYZ.__qualname__
'XYZ'
+ >>> XYZ.Inner.__qualname__
+ 'XYZ.Inner'
+ >>> XYZ.Inner.inner.__qualname__
+ 'XYZ.Inner.inner'
"""
def outer():
class Test(object):
def test(self):
global XYZinner
- class XYZinner(object): pass
+ class XYZinner:
+ class Inner:
+ def inner(self):
+ pass
return XYZinner
return Test
global XYZ
- class XYZ(object): pass
+ class XYZ(object):
+ class Inner(object):
+ def inner(self):
+ pass
return outer, lambda:None, XYZ