From 3a955f5c57e7d2c413b0714b2806b9e51eacf44a Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 26 Jun 2009 22:30:11 +0200 Subject: [PATCH] class decorators for Python classes - disabled for cdef classes (see transform) --- Cython/Compiler/Nodes.py | 8 +++++- Cython/Compiler/ParseTreeTransforms.py | 40 +++++++++++++++++++++----- Cython/Compiler/Parsing.pxd | 2 +- Cython/Compiler/Parsing.py | 12 ++++---- 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 998d7054..df202910 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2479,6 +2479,7 @@ class PyClassDefNode(ClassDefNode): # body StatNode Attribute definition code # entry Symtab.Entry # scope PyClassScope + # decorators [DecoratorNode] list of decorators or None # # The following subnodes are constructed internally: # @@ -2487,12 +2488,14 @@ class PyClassDefNode(ClassDefNode): # target NameNode Variable to assign class object to child_attrs = ["body", "dict", "classobj", "target"] + decorators = None - def __init__(self, pos, name, bases, doc, body): + def __init__(self, pos, name, bases, doc, body, decorators = None): StatNode.__init__(self, pos) self.name = name self.doc = doc self.body = body + self.decorators = decorators import ExprNodes self.dict = ExprNodes.DictNode(pos, key_value_pairs = []) if self.doc and Options.docstrings: @@ -2538,6 +2541,7 @@ class PyClassDefNode(ClassDefNode): class_name = self.name, base_class_module = base_class_module, base_class_name = base_class_name, + decorators = self.decorators, body = self.body, in_pxd = False, doc = self.doc) @@ -2594,6 +2598,7 @@ class CClassDefNode(ClassDefNode): # objstruct_name string or None Specified C name of object struct # typeobj_name string or None Specified C name of type object # in_pxd boolean Is in a .pxd file + # decorators [DecoratorNode] list of decorators or None # doc string or None # body StatNode or None # entry Symtab.Entry @@ -2608,6 +2613,7 @@ class CClassDefNode(ClassDefNode): api = False objstruct_name = None typeobj_name = None + decorators = None def analyse_declarations(self, env): #print "CClassDefNode.analyse_declarations:", self.class_name diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py index 9e159f75..c1fb5288 100644 --- a/Cython/Compiler/ParseTreeTransforms.py +++ b/Cython/Compiler/ParseTreeTransforms.py @@ -597,22 +597,48 @@ class WithTransform(CythonTransform, SkipDeclarations): class DecoratorTransform(CythonTransform, SkipDeclarations): def visit_DefNode(self, func_node): + self.visitchildren(func_node) if not func_node.decorators: return func_node - - decorator_result = NameNode(func_node.pos, name = func_node.name) - for decorator in func_node.decorators[::-1]: + return self._handle_decorators( + func_node, func_node.name) + + def _visit_CClassDefNode(self, class_node): + # This doesn't currently work, so it's disabled (also in the + # parser). + # + # Problem: assignments to cdef class names do not work. They + # would require an additional check anyway, as the extension + # type must not change its C type, so decorators cannot + # replace an extension type, just alter it and return it. + + self.visitchildren(class_node) + if not class_node.decorators: + return class_node + return self._handle_decorators( + class_node, class_node.class_name) + + def visit_ClassDefNode(self, class_node): + self.visitchildren(class_node) + if not class_node.decorators: + return class_node + return self._handle_decorators( + class_node, class_node.name) + + def _handle_decorators(self, node, name): + decorator_result = NameNode(node.pos, name = name) + for decorator in node.decorators[::-1]: decorator_result = SimpleCallNode( decorator.pos, function = decorator.decorator, args = [decorator_result]) - func_name_node = NameNode(func_node.pos, name = func_node.name) + name_node = NameNode(node.pos, name = name) reassignment = SingleAssignmentNode( - func_node.pos, - lhs = func_name_node, + node.pos, + lhs = name_node, rhs = decorator_result) - return [func_node, reassignment] + return [node, reassignment] class AnalyseDeclarationsTransform(CythonTransform): diff --git a/Cython/Compiler/Parsing.pxd b/Cython/Compiler/Parsing.pxd index c2e68c22..2d271c14 100644 --- a/Cython/Compiler/Parsing.pxd +++ b/Cython/Compiler/Parsing.pxd @@ -141,7 +141,7 @@ cpdef p_ctypedef_statement(PyrexScanner s, ctx) cpdef p_decorators(PyrexScanner s) cpdef p_def_statement(PyrexScanner s, decorators = *) cpdef p_py_arg_decl(PyrexScanner s) -cpdef p_class_statement(PyrexScanner s) +cpdef p_class_statement(PyrexScanner s, decorators = *) cpdef p_c_class_definition(PyrexScanner s, pos, ctx) cpdef p_c_class_options(PyrexScanner s) cpdef p_property_decl(PyrexScanner s) diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 3d04b841..3566347c 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -1574,8 +1574,8 @@ def p_statement(s, ctx, first_statement = 0): s.error('decorator not allowed here') s.level = ctx.level decorators = p_decorators(s) - if s.sy not in ('def', 'cdef', 'cpdef'): - s.error("Decorators can only be followed by functions ") + if s.sy not in ('def', 'cdef', 'cpdef', 'class'): + s.error("Decorators can only be followed by functions or classes") elif s.sy == 'pass' and cdef_flag: # empty cdef block return p_pass_statement(s, with_newline = 1) @@ -1595,7 +1595,7 @@ def p_statement(s, ctx, first_statement = 0): node = p_cdef_statement(s, ctx(overridable = overridable)) if decorators is not None: if not isinstance(node, (Nodes.CFuncDefNode, Nodes.CVarDefNode)): - s.error("Decorators can only be followed by functions ") + s.error("Decorators can only be followed by functions or Python classes") node.decorators = decorators return node else: @@ -1609,7 +1609,7 @@ def p_statement(s, ctx, first_statement = 0): elif s.sy == 'class': if ctx.level != 'module': s.error("class definition not allowed here") - return p_class_statement(s) + return p_class_statement(s, decorators) elif s.sy == 'include': if ctx.level not in ('module', 'module_pxd'): s.error("include statement not allowed here") @@ -2426,7 +2426,7 @@ def p_py_arg_decl(s): name = p_ident(s) return Nodes.PyArgDeclNode(pos, name = name) -def p_class_statement(s): +def p_class_statement(s, decorators): # s.sy == 'class' pos = s.position() s.next() @@ -2442,7 +2442,7 @@ def p_class_statement(s): return Nodes.PyClassDefNode(pos, name = class_name, bases = ExprNodes.TupleNode(pos, args = base_list), - doc = doc, body = body) + doc = doc, body = body, decorators = decorators) def p_c_class_definition(s, pos, ctx): # s.sy == 'class' -- 2.26.2