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):
#
# The following subnodes are constructed internally:
#
# dict DictNode Class dictionary
# dict DictNode Class dictionary or Py3 namespace
# classobj ClassNode Class object
# 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
py3_style_class = False # Python3 style class
def __init__(self, pos, name, bases, doc, body, decorators = None,
keyword_args = None, starstar_arg = None):
......@@ -2949,22 +2950,40 @@ class PyClassDefNode(ClassDefNode):
self.body = body
self.decorators = decorators
import ExprNodes
self.dict = ExprNodes.DictNode(pos, key_value_pairs = [])
if self.doc and Options.docstrings:
doc = embed_position(self.pos, self.doc)
# FIXME: correct string node?
doc_node = ExprNodes.StringNode(pos, value = doc)
else:
doc_node = None
self.classobj = ExprNodes.ClassNode(pos, name = name,
bases = bases, dict = self.dict, doc = doc_node,
keyword_args = keyword_args, starstar_arg = starstar_arg)
if keyword_args or starstar_arg:
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)
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)
def as_cclass(self):
"""
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
if len(bases) == 0:
base_class_name = None
......@@ -3015,6 +3034,10 @@ class PyClassDefNode(ClassDefNode):
self.body.analyse_declarations(cenv)
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.classobj.analyse_expressions(env)
genv = env.global_scope()
......@@ -3028,6 +3051,10 @@ class PyClassDefNode(ClassDefNode):
def generate_execution_code(self, code):
code.pyclass_stack.append(self)
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)
cenv.namespace_cname = cenv.class_obj_cname = self.dict.result()
self.body.generate_execution_code(code)
......@@ -3036,9 +3063,15 @@ class PyClassDefNode(ClassDefNode):
self.target.generate_assignment_code(self.classobj, code)
self.dict.generate_disposal_code(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()
class CClassDefNode(ClassDefNode):
# An extension type definition.
#
......
......@@ -12,17 +12,28 @@ class Foo(object):
"""
__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):
def __new__(cls, name, bases, attrs, foo=None):
attrs['foo'] = foo
def __new__(cls, name, bases, attrs, **kwargs):
for key, value in kwargs.items():
attrs[key] = value
return type.__new__(cls, name, bases, attrs)
def __init__(self, cls, attrs, obj, foo=None):
def __init__(self, cls, attrs, obj, **kwargs):
pass
@staticmethod
def __prepare__(name, bases, **kwargs):
return {'bar': 666, 'dirty': True}
def __prepare__(*args, **kwargs):
return ODict()
class Py3Foo(object, metaclass=Py3Base, foo=123):
"""
......@@ -30,8 +41,48 @@ class Py3Foo(object, metaclass=Py3Base, foo=123):
>>> obj.foo
123
>>> obj.bar
666
>>> obj.dirty
False
321
>>> obj._order
['__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