Commit 2f58a1f9 authored by Vitja Makarov's avatar Vitja Makarov

Actually implement py3 style metaclasses with support for __prepare__ and namespaces

parent d61c95b0
This diff is collapsed.
...@@ -2934,12 +2934,13 @@ class PyClassDefNode(ClassDefNode): ...@@ -2934,12 +2934,13 @@ class PyClassDefNode(ClassDefNode):
# #
# The following subnodes are constructed internally: # The following subnodes are constructed internally:
# #
# dict DictNode Class dictionary # dict DictNode Class dictionary or Py3 namespace
# classobj ClassNode Class object # classobj ClassNode Class object
# target NameNode Variable to assign class object to # target NameNode Variable to assign class object to
child_attrs = ["body", "dict", "classobj", "target"] child_attrs = ["body", "dict", "metaclass", "mkw", "bases", "classobj", "target"]
decorators = None decorators = None
py3_style_class = False # Python3 style class
def __init__(self, pos, name, bases, doc, body, decorators = None, def __init__(self, pos, name, bases, doc, body, decorators = None,
keyword_args = None, starstar_arg = None): keyword_args = None, starstar_arg = None):
...@@ -2949,22 +2950,40 @@ class PyClassDefNode(ClassDefNode): ...@@ -2949,22 +2950,40 @@ class PyClassDefNode(ClassDefNode):
self.body = body self.body = body
self.decorators = decorators self.decorators = decorators
import ExprNodes import ExprNodes
self.dict = ExprNodes.DictNode(pos, key_value_pairs = [])
if self.doc and Options.docstrings: if self.doc and Options.docstrings:
doc = embed_position(self.pos, self.doc) doc = embed_position(self.pos, self.doc)
# FIXME: correct string node? # FIXME: correct string node?
doc_node = ExprNodes.StringNode(pos, value = doc) doc_node = ExprNodes.StringNode(pos, value = doc)
else: else:
doc_node = None doc_node = None
self.classobj = ExprNodes.ClassNode(pos, name = name, if keyword_args or starstar_arg:
bases = bases, dict = self.dict, doc = doc_node, self.py3_style_class = True
self.bases = ExprNodes.PyClassBasesNode(pos, bases = bases)
self.mkw = ExprNodes.KeywordArgsNode(pos,
keyword_args = keyword_args, starstar_arg = starstar_arg) keyword_args = keyword_args, starstar_arg = starstar_arg)
self.metaclass = ExprNodes.PyClassMetaclassNode(pos, mkw = self.mkw, bases = self.bases)
self.dict = ExprNodes.PyClassNamespaceNode(pos, name = name,
doc = doc_node, metaclass = self.metaclass, bases = self.bases,
mkw = self.mkw, )
self.classobj = ExprNodes.Py3ClassNode(pos, name = name,
bases = self.bases, dict = self.dict, doc = doc_node,
metaclass = self.metaclass, mkw = self.mkw)
else:
self.dict = ExprNodes.DictNode(pos, key_value_pairs = [])
self.metaclass = None
self.mkw = None
self.bases = None
self.classobj = ExprNodes.ClassNode(pos, name = name,
bases = bases, dict = self.dict, doc = doc_node)
self.target = ExprNodes.NameNode(pos, name = name) self.target = ExprNodes.NameNode(pos, name = name)
def as_cclass(self): def as_cclass(self):
""" """
Return this node as if it were declared as an extension class Return this node as if it were declared as an extension class
""" """
if self.py3_style_class:
error(self.classobj.pos, "Python3 style class could not be represented as C class")
return
bases = self.classobj.bases.args bases = self.classobj.bases.args
if len(bases) == 0: if len(bases) == 0:
base_class_name = None base_class_name = None
...@@ -3015,6 +3034,10 @@ class PyClassDefNode(ClassDefNode): ...@@ -3015,6 +3034,10 @@ class PyClassDefNode(ClassDefNode):
self.body.analyse_declarations(cenv) self.body.analyse_declarations(cenv)
def analyse_expressions(self, env): def analyse_expressions(self, env):
if self.py3_style_class:
self.bases.analyse_expressions(env)
self.metaclass.analyse_expressions(env)
self.mkw.analyse_expressions(env)
self.dict.analyse_expressions(env) self.dict.analyse_expressions(env)
self.classobj.analyse_expressions(env) self.classobj.analyse_expressions(env)
genv = env.global_scope() genv = env.global_scope()
...@@ -3028,6 +3051,10 @@ class PyClassDefNode(ClassDefNode): ...@@ -3028,6 +3051,10 @@ class PyClassDefNode(ClassDefNode):
def generate_execution_code(self, code): def generate_execution_code(self, code):
code.pyclass_stack.append(self) code.pyclass_stack.append(self)
cenv = self.scope cenv = self.scope
if self.py3_style_class:
self.bases.generate_evaluation_code(code)
self.mkw.generate_evaluation_code(code)
self.metaclass.generate_evaluation_code(code)
self.dict.generate_evaluation_code(code) self.dict.generate_evaluation_code(code)
cenv.namespace_cname = cenv.class_obj_cname = self.dict.result() cenv.namespace_cname = cenv.class_obj_cname = self.dict.result()
self.body.generate_execution_code(code) self.body.generate_execution_code(code)
...@@ -3036,9 +3063,15 @@ class PyClassDefNode(ClassDefNode): ...@@ -3036,9 +3063,15 @@ class PyClassDefNode(ClassDefNode):
self.target.generate_assignment_code(self.classobj, code) self.target.generate_assignment_code(self.classobj, code)
self.dict.generate_disposal_code(code) self.dict.generate_disposal_code(code)
self.dict.free_temps(code) self.dict.free_temps(code)
if self.py3_style_class:
self.mkw.generate_disposal_code(code)
self.mkw.free_temps(code)
self.metaclass.generate_disposal_code(code)
self.metaclass.free_temps(code)
self.bases.generate_disposal_code(code)
self.bases.free_temps(code)
code.pyclass_stack.pop() code.pyclass_stack.pop()
class CClassDefNode(ClassDefNode): class CClassDefNode(ClassDefNode):
# An extension type definition. # An extension type definition.
# #
......
...@@ -12,17 +12,28 @@ class Foo(object): ...@@ -12,17 +12,28 @@ class Foo(object):
""" """
__metaclass__ = Base __metaclass__ = Base
class ODict(dict):
def __init__(self):
dict.__init__(self)
self._order = []
dict.__setitem__(self, '_order', self._order)
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self._order.append(key)
class Py3Base(type): class Py3Base(type):
def __new__(cls, name, bases, attrs, foo=None): def __new__(cls, name, bases, attrs, **kwargs):
attrs['foo'] = foo for key, value in kwargs.items():
attrs[key] = value
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
def __init__(self, cls, attrs, obj, foo=None): def __init__(self, cls, attrs, obj, **kwargs):
pass pass
@staticmethod @staticmethod
def __prepare__(name, bases, **kwargs): def __prepare__(*args, **kwargs):
return {'bar': 666, 'dirty': True} return ODict()
class Py3Foo(object, metaclass=Py3Base, foo=123): class Py3Foo(object, metaclass=Py3Base, foo=123):
""" """
...@@ -30,8 +41,48 @@ class Py3Foo(object, metaclass=Py3Base, foo=123): ...@@ -30,8 +41,48 @@ class Py3Foo(object, metaclass=Py3Base, foo=123):
>>> obj.foo >>> obj.foo
123 123
>>> obj.bar >>> obj.bar
666 321
>>> obj.dirty >>> obj._order
False ['__module__', '__doc__', 'bar', 'foo']
"""
bar = 321
kwargs = {'foo': 123, 'bar': 456}
class Py3Mixed(metaclass=Py3Base, **kwargs):
"""
>>> Py3Mixed.foo
123
>>> Py3Mixed.bar
456
"""
kwargs['metaclass'] = Py3Base
class Py3Kwargs(**kwargs):
"""
>>> Py3Kwargs.foo
123
>>> Py3Kwargs.bar
456
"""
class Base3(type):
def __new__(cls, name, bases, attrs, **kwargs):
kwargs['b'] = 2
return type.__new__(cls, name, bases, attrs)
def __init__(self, *args, **kwargs):
self.kwargs = kwargs
@staticmethod
def __prepare__(*args, **kwargs):
kwargs['a'] = 1
return {}
kwargs = {'c': 0}
class Foo3(metaclass=Base3, a=0, b=0, **kwargs):
"""
>>> Foo3.kwargs
{'a': 0, 'c': 0, 'b': 0}
""" """
dirty = False
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