optimisation: do not build and test metaclass kwargs if we know the metaclass at...
authorStefan Behnel <scoder@users.berlios.de>
Fri, 19 Nov 2010 07:07:04 +0000 (08:07 +0100)
committerStefan Behnel <scoder@users.berlios.de>
Fri, 19 Nov 2010 07:07:04 +0000 (08:07 +0100)
Cython/Compiler/Nodes.py
Cython/Compiler/Options.py
tests/run/metaclass.pyx

index 17a28f7f6cf471e634667573b520c975eb418679..a25033cea2f4c3062d971c7931f6488a1fd027c6 100644 (file)
@@ -2952,16 +2952,26 @@ class PyClassDefNode(ClassDefNode):
         import ExprNodes
         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
         if keyword_args or starstar_arg:
             self.py3_style_class = True
             self.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.metaclass = None
+            if keyword_args and not starstar_arg and len(keyword_args.key_value_pairs) == 1:
+                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,
                         doc = doc_node, metaclass = self.metaclass, bases = self.bases,
                         mkw = self.mkw, )
index a1f777173cea2a28e63359c421da71a645016005..45da9ffb674c50d2eca689b37e573e646aa51c63 100644 (file)
@@ -102,8 +102,8 @@ directive_scopes = { # defaults to available everywhere
     'autotestdict' : ('module',),
     'autotestdict.all' : ('module',),
     'autotestdict.cdef' : ('module',),
-    'test_assert_path_exists' : ('function',),
-    'test_fail_if_path_exists' : ('function',),
+    'test_assert_path_exists' : ('function', 'class', 'cclass'),
+    'test_fail_if_path_exists' : ('function', 'class', 'cclass'),
 }
 
 def parse_directive_value(name, value, relaxed_bool=False):
index f7fb874988505d650e902064b37752892e6ea642..764c37fb7980634c9e9779185780fc92ae00cbf8 100644 (file)
@@ -1,9 +1,12 @@
 
+cimport cython
+
 class Base(type):
     def __new__(cls, name, bases, attrs):
         attrs['metaclass_was_here'] = True
         return type.__new__(cls, name, bases, attrs)
 
+@cython.test_fail_if_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
 class Foo(object):
     """
     >>> obj = Foo()
@@ -22,6 +25,34 @@ class ODict(dict):
         dict.__setitem__(self, key, value)
         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):
     def __new__(cls, name, bases, attrs, **kwargs):
         for key, value in kwargs.items():
@@ -35,6 +66,7 @@ class Py3Base(type):
     def __prepare__(*args, **kwargs):
         return ODict()
 
+@cython.test_assert_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
 class Py3Foo(object, metaclass=Py3Base, foo=123):
     """
     >>> obj = Py3Foo()
@@ -49,6 +81,7 @@ class Py3Foo(object, metaclass=Py3Base, foo=123):
 
 kwargs = {'foo': 123, 'bar': 456}
 
+@cython.test_assert_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
 class Py3Mixed(metaclass=Py3Base, **kwargs):
     """
     >>> Py3Mixed.foo
@@ -59,6 +92,7 @@ class Py3Mixed(metaclass=Py3Base, **kwargs):
 
 kwargs['metaclass'] = Py3Base
 
+@cython.test_assert_path_exists("//PyClassMetaclassNode")
 class Py3Kwargs(**kwargs):
     """
     >>> Py3Kwargs.foo
@@ -81,6 +115,8 @@ class Base3(type):
         return {}
 
 kwargs = {'c': 0}
+
+@cython.test_assert_path_exists("//PyClassMetaclassNode", "//Py3ClassNode")
 class Foo3(metaclass=Base3, a=0, b=0, **kwargs):
     """
     >>> Foo3.kwargs