# body StatNode Attribute definition code
# entry Symtab.Entry
# scope PyClassScope
+ # decorators [DecoratorNode] list of decorators or None
#
# The following subnodes are constructed internally:
#
# 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:
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)
# 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
api = False
objstruct_name = None
typeobj_name = None
+ decorators = None
def analyse_declarations(self, env):
#print "CClassDefNode.analyse_declarations:", self.class_name
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):
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)
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)
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:
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")
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()
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'