Commit 3a955f5c authored by Stefan Behnel's avatar Stefan Behnel

class decorators for Python classes - disabled for cdef classes (see transform)

parent a554c993
...@@ -2479,6 +2479,7 @@ class PyClassDefNode(ClassDefNode): ...@@ -2479,6 +2479,7 @@ class PyClassDefNode(ClassDefNode):
# body StatNode Attribute definition code # body StatNode Attribute definition code
# entry Symtab.Entry # entry Symtab.Entry
# scope PyClassScope # scope PyClassScope
# decorators [DecoratorNode] list of decorators or None
# #
# The following subnodes are constructed internally: # The following subnodes are constructed internally:
# #
...@@ -2487,12 +2488,14 @@ class PyClassDefNode(ClassDefNode): ...@@ -2487,12 +2488,14 @@ class PyClassDefNode(ClassDefNode):
# target NameNode Variable to assign class object to # target NameNode Variable to assign class object to
child_attrs = ["body", "dict", "classobj", "target"] 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) StatNode.__init__(self, pos)
self.name = name self.name = name
self.doc = doc self.doc = doc
self.body = body self.body = body
self.decorators = decorators
import ExprNodes import ExprNodes
self.dict = ExprNodes.DictNode(pos, key_value_pairs = []) self.dict = ExprNodes.DictNode(pos, key_value_pairs = [])
if self.doc and Options.docstrings: if self.doc and Options.docstrings:
...@@ -2538,6 +2541,7 @@ class PyClassDefNode(ClassDefNode): ...@@ -2538,6 +2541,7 @@ class PyClassDefNode(ClassDefNode):
class_name = self.name, class_name = self.name,
base_class_module = base_class_module, base_class_module = base_class_module,
base_class_name = base_class_name, base_class_name = base_class_name,
decorators = self.decorators,
body = self.body, body = self.body,
in_pxd = False, in_pxd = False,
doc = self.doc) doc = self.doc)
...@@ -2594,6 +2598,7 @@ class CClassDefNode(ClassDefNode): ...@@ -2594,6 +2598,7 @@ class CClassDefNode(ClassDefNode):
# objstruct_name string or None Specified C name of object struct # objstruct_name string or None Specified C name of object struct
# typeobj_name string or None Specified C name of type object # typeobj_name string or None Specified C name of type object
# in_pxd boolean Is in a .pxd file # in_pxd boolean Is in a .pxd file
# decorators [DecoratorNode] list of decorators or None
# doc string or None # doc string or None
# body StatNode or None # body StatNode or None
# entry Symtab.Entry # entry Symtab.Entry
...@@ -2608,6 +2613,7 @@ class CClassDefNode(ClassDefNode): ...@@ -2608,6 +2613,7 @@ class CClassDefNode(ClassDefNode):
api = False api = False
objstruct_name = None objstruct_name = None
typeobj_name = None typeobj_name = None
decorators = None
def analyse_declarations(self, env): def analyse_declarations(self, env):
#print "CClassDefNode.analyse_declarations:", self.class_name #print "CClassDefNode.analyse_declarations:", self.class_name
......
...@@ -597,22 +597,48 @@ class WithTransform(CythonTransform, SkipDeclarations): ...@@ -597,22 +597,48 @@ class WithTransform(CythonTransform, SkipDeclarations):
class DecoratorTransform(CythonTransform, SkipDeclarations): class DecoratorTransform(CythonTransform, SkipDeclarations):
def visit_DefNode(self, func_node): def visit_DefNode(self, func_node):
self.visitchildren(func_node)
if not func_node.decorators: if not func_node.decorators:
return func_node return func_node
return self._handle_decorators(
decorator_result = NameNode(func_node.pos, name = func_node.name) func_node, func_node.name)
for decorator in func_node.decorators[::-1]:
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_result = SimpleCallNode(
decorator.pos, decorator.pos,
function = decorator.decorator, function = decorator.decorator,
args = [decorator_result]) args = [decorator_result])
func_name_node = NameNode(func_node.pos, name = func_node.name) name_node = NameNode(node.pos, name = name)
reassignment = SingleAssignmentNode( reassignment = SingleAssignmentNode(
func_node.pos, node.pos,
lhs = func_name_node, lhs = name_node,
rhs = decorator_result) rhs = decorator_result)
return [func_node, reassignment] return [node, reassignment]
class AnalyseDeclarationsTransform(CythonTransform): class AnalyseDeclarationsTransform(CythonTransform):
......
...@@ -141,7 +141,7 @@ cpdef p_ctypedef_statement(PyrexScanner s, ctx) ...@@ -141,7 +141,7 @@ cpdef p_ctypedef_statement(PyrexScanner s, ctx)
cpdef p_decorators(PyrexScanner s) cpdef p_decorators(PyrexScanner s)
cpdef p_def_statement(PyrexScanner s, decorators = *) cpdef p_def_statement(PyrexScanner s, decorators = *)
cpdef p_py_arg_decl(PyrexScanner s) 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_definition(PyrexScanner s, pos, ctx)
cpdef p_c_class_options(PyrexScanner s) cpdef p_c_class_options(PyrexScanner s)
cpdef p_property_decl(PyrexScanner s) cpdef p_property_decl(PyrexScanner s)
......
...@@ -1574,8 +1574,8 @@ def p_statement(s, ctx, first_statement = 0): ...@@ -1574,8 +1574,8 @@ def p_statement(s, ctx, first_statement = 0):
s.error('decorator not allowed here') s.error('decorator not allowed here')
s.level = ctx.level s.level = ctx.level
decorators = p_decorators(s) decorators = p_decorators(s)
if s.sy not in ('def', 'cdef', 'cpdef'): if s.sy not in ('def', 'cdef', 'cpdef', 'class'):
s.error("Decorators can only be followed by functions ") s.error("Decorators can only be followed by functions or classes")
elif s.sy == 'pass' and cdef_flag: elif s.sy == 'pass' and cdef_flag:
# empty cdef block # empty cdef block
return p_pass_statement(s, with_newline = 1) return p_pass_statement(s, with_newline = 1)
...@@ -1595,7 +1595,7 @@ def p_statement(s, ctx, first_statement = 0): ...@@ -1595,7 +1595,7 @@ def p_statement(s, ctx, first_statement = 0):
node = p_cdef_statement(s, ctx(overridable = overridable)) node = p_cdef_statement(s, ctx(overridable = overridable))
if decorators is not None: if decorators is not None:
if not isinstance(node, (Nodes.CFuncDefNode, Nodes.CVarDefNode)): 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 node.decorators = decorators
return node return node
else: else:
...@@ -1609,7 +1609,7 @@ def p_statement(s, ctx, first_statement = 0): ...@@ -1609,7 +1609,7 @@ def p_statement(s, ctx, first_statement = 0):
elif s.sy == 'class': elif s.sy == 'class':
if ctx.level != 'module': if ctx.level != 'module':
s.error("class definition not allowed here") s.error("class definition not allowed here")
return p_class_statement(s) return p_class_statement(s, decorators)
elif s.sy == 'include': elif s.sy == 'include':
if ctx.level not in ('module', 'module_pxd'): if ctx.level not in ('module', 'module_pxd'):
s.error("include statement not allowed here") s.error("include statement not allowed here")
...@@ -2426,7 +2426,7 @@ def p_py_arg_decl(s): ...@@ -2426,7 +2426,7 @@ def p_py_arg_decl(s):
name = p_ident(s) name = p_ident(s)
return Nodes.PyArgDeclNode(pos, name = name) return Nodes.PyArgDeclNode(pos, name = name)
def p_class_statement(s): def p_class_statement(s, decorators):
# s.sy == 'class' # s.sy == 'class'
pos = s.position() pos = s.position()
s.next() s.next()
...@@ -2442,7 +2442,7 @@ def p_class_statement(s): ...@@ -2442,7 +2442,7 @@ def p_class_statement(s):
return Nodes.PyClassDefNode(pos, return Nodes.PyClassDefNode(pos,
name = class_name, name = class_name,
bases = ExprNodes.TupleNode(pos, args = base_list), 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): def p_c_class_definition(s, pos, ctx):
# s.sy == 'class' # s.sy == 'class'
......
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