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):
# 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
......
......@@ -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):
......
......@@ -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)
......
......@@ -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'
......
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