Commit b363f41b authored by Stefan Behnel's avatar Stefan Behnel

optimisation: do not build and test metaclass kwargs if we know the metaclass at compile time

parent c9ff1344
...@@ -2952,16 +2952,26 @@ class PyClassDefNode(ClassDefNode): ...@@ -2952,16 +2952,26 @@ class PyClassDefNode(ClassDefNode):
import ExprNodes import ExprNodes
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?
doc_node = ExprNodes.StringNode(pos, value = doc) doc_node = ExprNodes.StringNode(pos, value = doc)
else: else:
doc_node = None doc_node = None
if keyword_args or starstar_arg: if keyword_args or starstar_arg:
self.py3_style_class = True self.py3_style_class = True
self.bases = bases self.bases = bases
self.mkw = ExprNodes.KeywordArgsNode(pos, self.metaclass = None
keyword_args = keyword_args, starstar_arg = starstar_arg) if keyword_args and not starstar_arg and len(keyword_args.key_value_pairs) == 1:
self.metaclass = ExprNodes.PyClassMetaclassNode(pos, mkw = self.mkw, bases = self.bases) item = keyword_args.key_value_pairs[0]
if item.key.value == 'metaclass':
# special case: we already know the metaclass and
# it's the only kwarg, so we don't need to do the
# "build kwargs, find metaclass" dance at runtime
self.metaclass = item.value
self.mkw = ExprNodes.NullNode(pos)
if self.metaclass is None:
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, self.dict = ExprNodes.PyClassNamespaceNode(pos, name = name,
doc = doc_node, metaclass = self.metaclass, bases = self.bases, doc = doc_node, metaclass = self.metaclass, bases = self.bases,
mkw = self.mkw, ) mkw = self.mkw, )
......
...@@ -102,8 +102,8 @@ directive_scopes = { # defaults to available everywhere ...@@ -102,8 +102,8 @@ directive_scopes = { # defaults to available everywhere
'autotestdict' : ('module',), 'autotestdict' : ('module',),
'autotestdict.all' : ('module',), 'autotestdict.all' : ('module',),
'autotestdict.cdef' : ('module',), 'autotestdict.cdef' : ('module',),
'test_assert_path_exists' : ('function',), 'test_assert_path_exists' : ('function', 'class', 'cclass'),
'test_fail_if_path_exists' : ('function',), 'test_fail_if_path_exists' : ('function', 'class', 'cclass'),
} }
def parse_directive_value(name, value, relaxed_bool=False): def parse_directive_value(name, value, relaxed_bool=False):
......
cimport cython
class Base(type): class Base(type):
def __new__(cls, name, bases, attrs): def __new__(cls, name, bases, attrs):
attrs['metaclass_was_here'] = True attrs['metaclass_was_here'] = True
return type.__new__(cls, name, bases, attrs) return type.__new__(cls, name, bases, attrs)
@cython.test_fail_if_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
class Foo(object): class Foo(object):
""" """
>>> obj = Foo() >>> obj = Foo()
...@@ -22,6 +25,34 @@ class ODict(dict): ...@@ -22,6 +25,34 @@ class ODict(dict):
dict.__setitem__(self, key, value) dict.__setitem__(self, key, value)
self._order.append(key) self._order.append(key)
class Py3MetaclassPlusAttr(type):
def __new__(cls, name, bases, attrs, **kwargs):
for key, value in kwargs.items():
attrs[key] = value
attrs['metaclass_was_here'] = True
return type.__new__(cls, name, bases, attrs)
def __init__(self, cls, attrs, obj, **kwargs):
pass
@staticmethod
def __prepare__(*args, **kwargs):
return ODict()
@cython.test_fail_if_path_exists("//PyClassMetaclassNode")
@cython.test_assert_path_exists("//Py3ClassNode")
class Py3ClassMCOnly(object, metaclass=Py3MetaclassPlusAttr):
"""
>>> obj = Py3ClassMCOnly()
>>> obj.bar
321
>>> obj.metaclass_was_here
True
>>> obj._order
['__module__', '__doc__', 'bar', 'metaclass_was_here']
"""
bar = 321
class Py3Base(type): class Py3Base(type):
def __new__(cls, name, bases, attrs, **kwargs): def __new__(cls, name, bases, attrs, **kwargs):
for key, value in kwargs.items(): for key, value in kwargs.items():
...@@ -35,6 +66,7 @@ class Py3Base(type): ...@@ -35,6 +66,7 @@ class Py3Base(type):
def __prepare__(*args, **kwargs): def __prepare__(*args, **kwargs):
return ODict() return ODict()
@cython.test_assert_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
class Py3Foo(object, metaclass=Py3Base, foo=123): class Py3Foo(object, metaclass=Py3Base, foo=123):
""" """
>>> obj = Py3Foo() >>> obj = Py3Foo()
...@@ -49,6 +81,7 @@ class Py3Foo(object, metaclass=Py3Base, foo=123): ...@@ -49,6 +81,7 @@ class Py3Foo(object, metaclass=Py3Base, foo=123):
kwargs = {'foo': 123, 'bar': 456} kwargs = {'foo': 123, 'bar': 456}
@cython.test_assert_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
class Py3Mixed(metaclass=Py3Base, **kwargs): class Py3Mixed(metaclass=Py3Base, **kwargs):
""" """
>>> Py3Mixed.foo >>> Py3Mixed.foo
...@@ -59,6 +92,7 @@ class Py3Mixed(metaclass=Py3Base, **kwargs): ...@@ -59,6 +92,7 @@ class Py3Mixed(metaclass=Py3Base, **kwargs):
kwargs['metaclass'] = Py3Base kwargs['metaclass'] = Py3Base
@cython.test_assert_path_exists("//PyClassMetaclassNode")
class Py3Kwargs(**kwargs): class Py3Kwargs(**kwargs):
""" """
>>> Py3Kwargs.foo >>> Py3Kwargs.foo
...@@ -81,6 +115,8 @@ class Base3(type): ...@@ -81,6 +115,8 @@ class Base3(type):
return {} return {}
kwargs = {'c': 0} kwargs = {'c': 0}
@cython.test_assert_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
class Foo3(metaclass=Base3, a=0, b=0, **kwargs): class Foo3(metaclass=Base3, a=0, b=0, **kwargs):
""" """
>>> Foo3.kwargs >>> Foo3.kwargs
......
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