From b363f41bbf11fc07ece97866a4e299076255a9a8 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 19 Nov 2010 08:07:04 +0100 Subject: [PATCH] optimisation: do not build and test metaclass kwargs if we know the metaclass at compile time --- Cython/Compiler/Nodes.py | 18 ++++++++++++++---- Cython/Compiler/Options.py | 4 ++-- tests/run/metaclass.pyx | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 17a28f7f..a25033ce 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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, ) diff --git a/Cython/Compiler/Options.py b/Cython/Compiler/Options.py index a1f77717..45da9ffb 100644 --- a/Cython/Compiler/Options.py +++ b/Cython/Compiler/Options.py @@ -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): diff --git a/tests/run/metaclass.pyx b/tests/run/metaclass.pyx index f7fb8749..764c37fb 100644 --- a/tests/run/metaclass.pyx +++ b/tests/run/metaclass.pyx @@ -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 -- 2.26.2