3 cython.declare(PyrexTypes=object, Naming=object, ExprNodes=object, Nodes=object,
4 Options=object, UtilNodes=object, ModuleNode=object,
5 LetNode=object, LetRefNode=object, TreeFragment=object,
6 TemplateTransform=object, EncodedString=object,
7 error=object, warning=object, copy=object)
15 from Cython.Compiler.Visitor import VisitorTransform, TreeVisitor
16 from Cython.Compiler.Visitor import CythonTransform, EnvTransform, ScopeTrackingTransform
17 from Cython.Compiler.ModuleNode import ModuleNode
18 from Cython.Compiler.UtilNodes import LetNode, LetRefNode
19 from Cython.Compiler.TreeFragment import TreeFragment, TemplateTransform
20 from Cython.Compiler.StringEncoding import EncodedString
21 from Cython.Compiler.Errors import error, warning, CompileError, InternalError
26 class NameNodeCollector(TreeVisitor):
27 """Collect all NameNodes of a (sub-)tree in the ``name_nodes``
31 super(NameNodeCollector, self).__init__()
34 def visit_NameNode(self, node):
35 self.name_nodes.append(node)
37 def visit_Node(self, node):
38 self._visitchildren(node, None)
41 class SkipDeclarations(object):
43 Variable and function declarations can often have a deep tree structure,
44 and yet most transformations don't need to descend to this depth.
46 Declaration nodes are removed after AnalyseDeclarationsTransform, so there
47 is no need to use this for transformations after that point.
49 def visit_CTypeDefNode(self, node):
52 def visit_CVarDefNode(self, node):
55 def visit_CDeclaratorNode(self, node):
58 def visit_CBaseTypeNode(self, node):
61 def visit_CEnumDefNode(self, node):
64 def visit_CStructOrUnionDefNode(self, node):
68 class NormalizeTree(CythonTransform):
70 This transform fixes up a few things after parsing
71 in order to make the parse tree more suitable for
74 a) After parsing, blocks with only one statement will
75 be represented by that statement, not by a StatListNode.
76 When doing transforms this is annoying and inconsistent,
77 as one cannot in general remove a statement in a consistent
78 way and so on. This transform wraps any single statements
79 in a StatListNode containing a single statement.
81 b) The PassStatNode is a noop and serves no purpose beyond
82 plugging such one-statement blocks; i.e., once parsed a
83 ` "pass" can just as well be represented using an empty
84 StatListNode. This means less special cases to worry about
85 in subsequent transforms (one always checks to see if a
86 StatListNode has no children to see if the block is empty).
89 def __init__(self, context):
90 super(NormalizeTree, self).__init__(context)
91 self.is_in_statlist = False
92 self.is_in_expr = False
94 def visit_ExprNode(self, node):
95 stacktmp = self.is_in_expr
96 self.is_in_expr = True
97 self.visitchildren(node)
98 self.is_in_expr = stacktmp
101 def visit_StatNode(self, node, is_listcontainer=False):
102 stacktmp = self.is_in_statlist
103 self.is_in_statlist = is_listcontainer
104 self.visitchildren(node)
105 self.is_in_statlist = stacktmp
106 if not self.is_in_statlist and not self.is_in_expr:
107 return Nodes.StatListNode(pos=node.pos, stats=[node])
111 def visit_StatListNode(self, node):
112 self.is_in_statlist = True
113 self.visitchildren(node)
114 self.is_in_statlist = False
117 def visit_ParallelAssignmentNode(self, node):
118 return self.visit_StatNode(node, True)
120 def visit_CEnumDefNode(self, node):
121 return self.visit_StatNode(node, True)
123 def visit_CStructOrUnionDefNode(self, node):
124 return self.visit_StatNode(node, True)
126 # Eliminate PassStatNode
127 def visit_PassStatNode(self, node):
128 if not self.is_in_statlist:
129 return Nodes.StatListNode(pos=node.pos, stats=[])
133 def visit_CDeclaratorNode(self, node):
137 class PostParseError(CompileError): pass
139 # error strings checked by unit tests, so define them
140 ERR_CDEF_INCLASS = 'Cannot assign default value to fields in cdef classes, structs or unions'
141 ERR_BUF_DEFAULTS = 'Invalid buffer defaults specification (see docs)'
142 ERR_INVALID_SPECIALATTR_TYPE = 'Special attributes must not have a type declared'
143 class PostParse(ScopeTrackingTransform):
145 Basic interpretation of the parse tree, as well as validity
146 checking that can be done on a very basic level on the parse
147 tree (while still not being a problem with the basic syntax,
151 - Default values to cdef assignments are turned into single
152 assignments following the declaration (everywhere but in class
153 bodies, where they raise a compile error)
155 - Interpret some node structures into Python runtime values.
156 Some nodes take compile-time arguments (currently:
157 TemplatedTypeNode[args] and __cythonbufferdefaults__ = {args}),
158 which should be interpreted. This happens in a general way
159 and other steps should be taken to ensure validity.
161 Type arguments cannot be interpreted in this way.
163 - For __cythonbufferdefaults__ the arguments are checked for
166 TemplatedTypeNode has its directives interpreted:
167 Any first positional argument goes into the "dtype" attribute,
168 any "ndim" keyword argument goes into the "ndim" attribute and
169 so on. Also it is checked that the directive combination is valid.
170 - __cythonbufferdefaults__ attributes are parsed and put into the
173 Note: Currently Parsing.py does a lot of interpretation and
174 reorganization that can be refactored into this transform
175 if a more pure Abstract Syntax Tree is wanted.
178 def __init__(self, context):
179 super(PostParse, self).__init__(context)
180 self.specialattribute_handlers = {
181 '__cythonbufferdefaults__' : self.handle_bufferdefaults
184 def visit_ModuleNode(self, node):
185 self.lambda_counter = 1
186 return super(PostParse, self).visit_ModuleNode(node)
188 def visit_LambdaNode(self, node):
189 # unpack a lambda expression into the corresponding DefNode
190 lambda_id = self.lambda_counter
191 self.lambda_counter += 1
192 node.lambda_name = EncodedString(u'lambda%d' % lambda_id)
194 body = Nodes.ReturnStatNode(
195 node.result_expr.pos, value = node.result_expr)
196 node.def_node = Nodes.DefNode(
197 node.pos, name=node.name, lambda_name=node.lambda_name,
198 args=node.args, star_arg=node.star_arg,
199 starstar_arg=node.starstar_arg,
201 self.visitchildren(node)
205 def handle_bufferdefaults(self, decl):
206 if not isinstance(decl.default, ExprNodes.DictNode):
207 raise PostParseError(decl.pos, ERR_BUF_DEFAULTS)
208 self.scope_node.buffer_defaults_node = decl.default
209 self.scope_node.buffer_defaults_pos = decl.pos
211 def visit_CVarDefNode(self, node):
212 # This assumes only plain names and pointers are assignable on
213 # declaration. Also, it makes use of the fact that a cdef decl
214 # must appear before the first use, so we don't have to deal with
215 # "i = 3; cdef int i = i" and can simply move the nodes around.
217 self.visitchildren(node)
220 for decl in node.declarators:
222 while isinstance(declbase, Nodes.CPtrDeclaratorNode):
223 declbase = declbase.base
224 if isinstance(declbase, Nodes.CNameDeclaratorNode):
225 if declbase.default is not None:
226 if self.scope_type in ('cclass', 'pyclass', 'struct'):
227 if isinstance(self.scope_node, Nodes.CClassDefNode):
228 handler = self.specialattribute_handlers.get(decl.name)
230 if decl is not declbase:
231 raise PostParseError(decl.pos, ERR_INVALID_SPECIALATTR_TYPE)
233 continue # Remove declaration
234 raise PostParseError(decl.pos, ERR_CDEF_INCLASS)
235 first_assignment = self.scope_type != 'module'
236 stats.append(Nodes.SingleAssignmentNode(node.pos,
237 lhs=ExprNodes.NameNode(node.pos, name=declbase.name),
238 rhs=declbase.default, first=first_assignment))
239 declbase.default = None
240 newdecls.append(decl)
241 node.declarators = newdecls
243 except PostParseError, e:
244 # An error in a cdef clause is ok, simply remove the declaration
245 # and try to move on to report more errors
246 self.context.nonfatal_error(e)
249 # Split parallel assignments (a,b = b,a) into separate partial
250 # assignments that are executed rhs-first using temps. This
251 # restructuring must be applied before type analysis so that known
252 # types on rhs and lhs can be matched directly. It is required in
253 # the case that the types cannot be coerced to a Python type in
254 # order to assign from a tuple.
256 def visit_SingleAssignmentNode(self, node):
257 self.visitchildren(node)
258 return self._visit_assignment_node(node, [node.lhs, node.rhs])
260 def visit_CascadedAssignmentNode(self, node):
261 self.visitchildren(node)
262 return self._visit_assignment_node(node, node.lhs_list + [node.rhs])
264 def _visit_assignment_node(self, node, expr_list):
265 """Flatten parallel assignments into separate single
266 assignments or cascaded assignments.
268 if sum([ 1 for expr in expr_list if expr.is_sequence_constructor ]) < 2:
269 # no parallel assignments => nothing to do
273 flatten_parallel_assignments(expr_list, expr_list_list)
275 eliminate_rhs_duplicates(expr_list_list, temp_refs)
278 for expr_list in expr_list_list:
279 lhs_list = expr_list[:-1]
281 if len(lhs_list) == 1:
282 node = Nodes.SingleAssignmentNode(rhs.pos,
283 lhs = lhs_list[0], rhs = rhs)
285 node = Nodes.CascadedAssignmentNode(rhs.pos,
286 lhs_list = lhs_list, rhs = rhs)
290 assign_node = nodes[0]
292 assign_node = Nodes.ParallelAssignmentNode(nodes[0].pos, stats = nodes)
295 duplicates_and_temps = [ (temp.expression, temp)
296 for temp in temp_refs ]
297 sort_common_subsequences(duplicates_and_temps)
298 for _, temp_ref in duplicates_and_temps[::-1]:
299 assign_node = LetNode(temp_ref, assign_node)
303 def eliminate_rhs_duplicates(expr_list_list, ref_node_sequence):
304 """Replace rhs items by LetRefNodes if they appear more than once.
305 Creates a sequence of LetRefNodes that set up the required temps
306 and appends them to ref_node_sequence. The input list is modified
309 seen_nodes = cython.set()
311 def find_duplicates(node):
312 if node.is_literal or node.is_name:
313 # no need to replace those; can't include attributes here
314 # as their access is not necessarily side-effect free
316 if node in seen_nodes:
317 if node not in ref_nodes:
318 ref_node = LetRefNode(node)
319 ref_nodes[node] = ref_node
320 ref_node_sequence.append(ref_node)
323 if node.is_sequence_constructor:
324 for item in node.args:
325 find_duplicates(item)
327 for expr_list in expr_list_list:
333 def substitute_nodes(node):
334 if node in ref_nodes:
335 return ref_nodes[node]
336 elif node.is_sequence_constructor:
337 node.args = list(map(substitute_nodes, node.args))
340 # replace nodes inside of the common subexpressions
341 for node in ref_nodes:
342 if node.is_sequence_constructor:
343 node.args = list(map(substitute_nodes, node.args))
345 # replace common subexpressions on all rhs items
346 for expr_list in expr_list_list:
347 expr_list[-1] = substitute_nodes(expr_list[-1])
349 def sort_common_subsequences(items):
350 """Sort items/subsequences so that all items and subsequences that
351 an item contains appear before the item itself. This is needed
352 because each rhs item must only be evaluated once, so its value
353 must be evaluated first and then reused when packing sequences
356 This implies a partial order, and the sort must be stable to
357 preserve the original order as much as possible, so we use a
358 simple insertion sort (which is very fast for short sequences, the
359 normal case in practice).
361 def contains(seq, x):
365 elif item.is_sequence_constructor and contains(item.args, x):
369 return b.is_sequence_constructor and contains(b.args, a)
371 for pos, item in enumerate(items):
372 key = item[1] # the ResultRefNode which has already been injected into the sequences
374 for i in xrange(pos-1, -1, -1):
375 if lower_than(key, items[i][0]):
378 for i in xrange(pos, new_pos, -1):
379 items[i] = items[i-1]
380 items[new_pos] = item
382 def flatten_parallel_assignments(input, output):
383 # The input is a list of expression nodes, representing the LHSs
384 # and RHS of one (possibly cascaded) assignment statement. For
385 # sequence constructors, rearranges the matching parts of both
386 # sides into a list of equivalent assignments between the
387 # individual elements. This transformation is applied
388 # recursively, so that nested structures get matched as well.
390 if not rhs.is_sequence_constructor or not sum([lhs.is_sequence_constructor for lhs in input[:-1]]):
394 complete_assignments = []
396 rhs_size = len(rhs.args)
397 lhs_targets = [ [] for _ in xrange(rhs_size) ]
398 starred_assignments = []
399 for lhs in input[:-1]:
400 if not lhs.is_sequence_constructor:
402 error(lhs.pos, "starred assignment target must be in a list or tuple")
403 complete_assignments.append(lhs)
405 lhs_size = len(lhs.args)
406 starred_targets = sum([1 for expr in lhs.args if expr.is_starred])
407 if starred_targets > 1:
408 error(lhs.pos, "more than 1 starred expression in assignment")
409 output.append([lhs,rhs])
411 elif lhs_size - starred_targets > rhs_size:
412 error(lhs.pos, "need more than %d value%s to unpack"
413 % (rhs_size, (rhs_size != 1) and 's' or ''))
414 output.append([lhs,rhs])
416 elif starred_targets:
417 map_starred_assignment(lhs_targets, starred_assignments,
419 elif lhs_size < rhs_size:
420 error(lhs.pos, "too many values to unpack (expected %d, got %d)"
421 % (lhs_size, rhs_size))
422 output.append([lhs,rhs])
425 for targets, expr in zip(lhs_targets, lhs.args):
428 if complete_assignments:
429 complete_assignments.append(rhs)
430 output.append(complete_assignments)
432 # recursively flatten partial assignments
433 for cascade, rhs in zip(lhs_targets, rhs.args):
436 flatten_parallel_assignments(cascade, output)
438 # recursively flatten starred assignments
439 for cascade in starred_assignments:
440 if cascade[0].is_sequence_constructor:
441 flatten_parallel_assignments(cascade, output)
443 output.append(cascade)
445 def map_starred_assignment(lhs_targets, starred_assignments, lhs_args, rhs_args):
446 # Appends the fixed-position LHS targets to the target list that
447 # appear left and right of the starred argument.
449 # The starred_assignments list receives a new tuple
450 # (lhs_target, rhs_values_list) that maps the remaining arguments
451 # (those that match the starred target) to a list.
453 # left side of the starred target
454 for i, (targets, expr) in enumerate(zip(lhs_targets, lhs_args)):
457 lhs_remaining = len(lhs_args) - i - 1
461 raise InternalError("no starred arg found when splitting starred assignment")
463 # right side of the starred target
464 for i, (targets, expr) in enumerate(zip(lhs_targets[-lhs_remaining:],
465 lhs_args[-lhs_remaining:])):
468 # the starred target itself, must be assigned a (potentially empty) list
469 target = lhs_args[starred].target # unpack starred node
470 starred_rhs = rhs_args[starred:]
472 starred_rhs = starred_rhs[:-lhs_remaining]
474 pos = starred_rhs[0].pos
477 starred_assignments.append([
478 target, ExprNodes.ListNode(pos=pos, args=starred_rhs)])
481 class PxdPostParse(CythonTransform, SkipDeclarations):
483 Basic interpretation/validity checking that should only be
486 A lot of this checking currently happens in the parser; but
487 what is listed below happens here.
489 - "def" functions are let through only if they fill the
490 getbuffer/releasebuffer slots
492 - cdef functions are let through only if they are on the
493 top level and are declared "inline"
495 ERR_INLINE_ONLY = "function definition in pxd file must be declared 'cdef inline'"
496 ERR_NOGO_WITH_INLINE = "inline function definition in pxd file cannot be '%s'"
498 def __call__(self, node):
499 self.scope_type = 'pxd'
500 return super(PxdPostParse, self).__call__(node)
502 def visit_CClassDefNode(self, node):
503 old = self.scope_type
504 self.scope_type = 'cclass'
505 self.visitchildren(node)
506 self.scope_type = old
509 def visit_FuncDefNode(self, node):
510 # FuncDefNode always come with an implementation (without
511 # an imp they are CVarDefNodes..)
512 err = self.ERR_INLINE_ONLY
514 if (isinstance(node, Nodes.DefNode) and self.scope_type == 'cclass'
515 and node.name in ('__getbuffer__', '__releasebuffer__')):
516 err = None # allow these slots
518 if isinstance(node, Nodes.CFuncDefNode):
519 if u'inline' in node.modifiers and self.scope_type == 'pxd':
520 node.inline_in_pxd = True
521 if node.visibility != 'private':
522 err = self.ERR_NOGO_WITH_INLINE % node.visibility
524 err = self.ERR_NOGO_WITH_INLINE % 'api'
526 err = None # allow inline function
528 err = self.ERR_INLINE_ONLY
531 self.context.nonfatal_error(PostParseError(node.pos, err))
536 class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
538 After parsing, directives can be stored in a number of places:
539 - #cython-comments at the top of the file (stored in ModuleNode)
540 - Command-line arguments overriding these
541 - @cython.directivename decorators
542 - with cython.directivename: statements
544 This transform is responsible for interpreting these various sources
545 and store the directive in two ways:
546 - Set the directives attribute of the ModuleNode for global directives.
547 - Use a CompilerDirectivesNode to override directives for a subtree.
549 (The first one is primarily to not have to modify with the tree
550 structure, so that ModuleNode stay on top.)
552 The directives are stored in dictionaries from name to value in effect.
553 Each such dictionary is always filled in for all possible directives,
554 using default values where no value is given by the user.
556 The available directives are controlled in Options.py.
558 Note that we have to run this prior to analysis, and so some minor
559 duplication of functionality has to occur: We manually track cimports
560 and which names the "cython" module may have been imported to.
562 unop_method_nodes = {
563 'typeof': ExprNodes.TypeofNode,
565 'operator.address': ExprNodes.AmpersandNode,
566 'operator.dereference': ExprNodes.DereferenceNode,
567 'operator.preincrement' : ExprNodes.inc_dec_constructor(True, '++'),
568 'operator.predecrement' : ExprNodes.inc_dec_constructor(True, '--'),
569 'operator.postincrement': ExprNodes.inc_dec_constructor(False, '++'),
570 'operator.postdecrement': ExprNodes.inc_dec_constructor(False, '--'),
572 # For backwards compatability.
573 'address': ExprNodes.AmpersandNode,
576 binop_method_nodes = {
577 'operator.comma' : ExprNodes.c_binop_constructor(','),
580 special_methods = cython.set(['declare', 'union', 'struct', 'typedef', 'sizeof',
581 'cast', 'pointer', 'compiled', 'NULL'])
582 special_methods.update(unop_method_nodes.keys())
584 def __init__(self, context, compilation_directive_defaults):
585 super(InterpretCompilerDirectives, self).__init__(context)
586 self.compilation_directive_defaults = {}
587 for key, value in compilation_directive_defaults.items():
588 self.compilation_directive_defaults[unicode(key)] = value
589 self.cython_module_names = cython.set()
590 self.directive_names = {}
592 def check_directive_scope(self, pos, directive, scope):
593 legal_scopes = Options.directive_scopes.get(directive, None)
594 if legal_scopes and scope not in legal_scopes:
595 self.context.nonfatal_error(PostParseError(pos, 'The %s compiler directive '
596 'is not allowed in %s scope' % (directive, scope)))
601 # Set up processing and handle the cython: comments.
602 def visit_ModuleNode(self, node):
603 for key, value in node.directive_comments.items():
604 if not self.check_directive_scope(node.pos, key, 'module'):
605 self.wrong_scope_error(node.pos, key, 'module')
606 del node.directive_comments[key]
608 directives = copy.copy(Options.directive_defaults)
609 directives.update(self.compilation_directive_defaults)
610 directives.update(node.directive_comments)
611 self.directives = directives
612 node.directives = directives
613 self.visitchildren(node)
614 node.cython_module_names = self.cython_module_names
617 # The following four functions track imports and cimports that
618 # begin with "cython"
619 def is_cython_directive(self, name):
620 return (name in Options.directive_types or
621 name in self.special_methods or
622 PyrexTypes.parse_basic_type(name))
624 def visit_CImportStatNode(self, node):
625 if node.module_name == u"cython":
626 self.cython_module_names.add(node.as_name or u"cython")
627 elif node.module_name.startswith(u"cython."):
629 self.directive_names[node.as_name] = node.module_name[7:]
631 self.cython_module_names.add(u"cython")
632 # if this cimport was a compiler directive, we don't
633 # want to leave the cimport node sitting in the tree
637 def visit_FromCImportStatNode(self, node):
638 if (node.module_name == u"cython") or \
639 node.module_name.startswith(u"cython."):
640 submodule = (node.module_name + u".")[7:]
642 for pos, name, as_name, kind in node.imported_names:
643 full_name = submodule + name
644 if self.is_cython_directive(full_name):
647 self.directive_names[as_name] = full_name
649 self.context.nonfatal_error(PostParseError(pos,
650 "Compiler directive imports must be plain imports"))
652 newimp.append((pos, name, as_name, kind))
655 node.imported_names = newimp
658 def visit_FromImportStatNode(self, node):
659 if (node.module.module_name.value == u"cython") or \
660 node.module.module_name.value.startswith(u"cython."):
661 submodule = (node.module.module_name.value + u".")[7:]
663 for name, name_node in node.items:
664 full_name = submodule + name
665 if self.is_cython_directive(full_name):
666 self.directive_names[name_node.name] = full_name
668 newimp.append((name, name_node))
674 def visit_SingleAssignmentNode(self, node):
675 if (isinstance(node.rhs, ExprNodes.ImportNode) and
676 node.rhs.module_name.value == u'cython'):
677 node = Nodes.CImportStatNode(node.pos,
678 module_name = u'cython',
679 as_name = node.lhs.name)
680 self.visit_CImportStatNode(node)
682 self.visitchildren(node)
685 def visit_NameNode(self, node):
686 if node.name in self.cython_module_names:
687 node.is_cython_module = True
689 node.cython_attribute = self.directive_names.get(node.name)
692 def try_to_parse_directives(self, node):
693 # If node is the contents of an directive (in a with statement or
694 # decorator), returns a list of (directivename, value) pairs.
695 # Otherwise, returns None
696 if isinstance(node, ExprNodes.CallNode):
697 self.visit(node.function)
698 optname = node.function.as_cython_attribute()
700 directivetype = Options.directive_types.get(optname)
702 args, kwds = node.explicit_args_kwds()
705 if kwds is not None and directivetype is not dict:
706 for keyvalue in kwds.key_value_pairs:
707 key, value = keyvalue
708 sub_optname = "%s.%s" % (optname, key.value)
709 if Options.directive_types.get(sub_optname):
710 directives.append(self.try_to_parse_directive(sub_optname, [value], None, keyvalue.pos))
712 key_value_pairs.append(keyvalue)
713 if not key_value_pairs:
716 kwds.key_value_pairs = key_value_pairs
717 if directives and not kwds and not args:
719 directives.append(self.try_to_parse_directive(optname, args, kwds, node.function.pos))
721 elif isinstance(node, (ExprNodes.AttributeNode, ExprNodes.NameNode)):
723 optname = node.as_cython_attribute()
725 directivetype = Options.directive_types.get(optname)
726 if directivetype is bool:
727 return [(optname, True)]
728 elif directivetype is None:
729 return [(optname, None)]
731 raise PostParseError(
732 node.pos, "The '%s' directive should be used as a function call." % optname)
735 def try_to_parse_directive(self, optname, args, kwds, pos):
736 directivetype = Options.directive_types.get(optname)
737 if len(args) == 1 and isinstance(args[0], ExprNodes.NoneNode):
738 return optname, Options.directive_defaults[optname]
739 elif directivetype is bool:
740 if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.BoolNode):
741 raise PostParseError(pos,
742 'The %s directive takes one compile-time boolean argument' % optname)
743 return (optname, args[0].value)
744 elif directivetype is str:
745 if kwds is not None or len(args) != 1 or not isinstance(args[0], (ExprNodes.StringNode,
746 ExprNodes.UnicodeNode)):
747 raise PostParseError(pos,
748 'The %s directive takes one compile-time string argument' % optname)
749 return (optname, str(args[0].value))
750 elif directivetype is dict:
752 raise PostParseError(pos,
753 'The %s directive takes no prepositional arguments' % optname)
754 return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
755 elif directivetype is list:
756 if kwds and len(kwds) != 0:
757 raise PostParseError(pos,
758 'The %s directive takes no keyword arguments' % optname)
759 return optname, [ str(arg.value) for arg in args ]
763 def visit_with_directives(self, body, directives):
764 olddirectives = self.directives
765 newdirectives = copy.copy(olddirectives)
766 newdirectives.update(directives)
767 self.directives = newdirectives
768 assert isinstance(body, Nodes.StatListNode), body
769 retbody = self.visit_Node(body)
770 directive = Nodes.CompilerDirectivesNode(pos=retbody.pos, body=retbody,
771 directives=newdirectives)
772 self.directives = olddirectives
776 def visit_FuncDefNode(self, node):
777 directives = self._extract_directives(node, 'function')
779 return self.visit_Node(node)
780 body = Nodes.StatListNode(node.pos, stats=[node])
781 return self.visit_with_directives(body, directives)
783 def visit_CVarDefNode(self, node):
784 if not node.decorators:
786 for dec in node.decorators:
787 for directive in self.try_to_parse_directives(dec.decorator) or ():
788 if directive is not None and directive[0] == u'locals':
789 node.directive_locals = directive[1]
791 self.context.nonfatal_error(PostParseError(dec.pos,
792 "Cdef functions can only take cython.locals() decorator."))
795 def visit_CClassDefNode(self, node):
796 directives = self._extract_directives(node, 'cclass')
798 return self.visit_Node(node)
799 body = Nodes.StatListNode(node.pos, stats=[node])
800 return self.visit_with_directives(body, directives)
802 def visit_PyClassDefNode(self, node):
803 directives = self._extract_directives(node, 'class')
805 return self.visit_Node(node)
806 body = Nodes.StatListNode(node.pos, stats=[node])
807 return self.visit_with_directives(body, directives)
809 def _extract_directives(self, node, scope_name):
810 if not node.decorators:
812 # Split the decorators into two lists -- real decorators and directives
815 for dec in node.decorators:
816 new_directives = self.try_to_parse_directives(dec.decorator)
817 if new_directives is not None:
818 for directive in new_directives:
819 if self.check_directive_scope(node.pos, directive[0], scope_name):
820 directives.append(directive)
823 if realdecs and isinstance(node, (Nodes.CFuncDefNode, Nodes.CClassDefNode)):
824 raise PostParseError(realdecs[0].pos, "Cdef functions/classes cannot take arbitrary decorators.")
826 node.decorators = realdecs
827 # merge or override repeated directives
829 directives.reverse() # Decorators coming first take precedence
830 for directive in directives:
831 name, value = directive
833 old_value = optdict[name]
834 # keywords and arg lists can be merged, everything
835 # else overrides completely
836 if isinstance(old_value, dict):
837 old_value.update(value)
838 elif isinstance(old_value, list):
839 old_value.extend(value)
841 optdict[name] = value
843 optdict[name] = value
846 # Handle with statements
847 def visit_WithStatNode(self, node):
849 for directive in self.try_to_parse_directives(node.manager) or []:
850 if directive is not None:
851 if node.target is not None:
852 self.context.nonfatal_error(
853 PostParseError(node.pos, "Compiler directive with statements cannot contain 'as'"))
855 name, value = directive
857 # special case: in pure mode, "with nogil" spells "with cython.nogil"
858 node = Nodes.GILStatNode(node.pos, state = "nogil", body = node.body)
859 return self.visit_Node(node)
860 if self.check_directive_scope(node.pos, name, 'with statement'):
861 directive_dict[name] = value
863 return self.visit_with_directives(node.body, directive_dict)
864 return self.visit_Node(node)
866 class WithTransform(CythonTransform, SkipDeclarations):
868 # EXCINFO is manually set to a variable that contains
869 # the exc_info() tuple that can be generated by the enclosing except
871 template_without_target = TreeFragment(u"""
882 if not EXIT(*EXCINFO):
886 EXIT(None, None, None)
887 """, temps=[u'MGR', u'EXC', u"EXIT"],
888 pipeline=[NormalizeTree(None)])
890 template_with_target = TreeFragment(u"""
893 VALUE = MGR.__enter__()
902 if not EXIT(*EXCINFO):
906 EXIT(None, None, None)
907 MGR = EXIT = VALUE = EXC = None
909 """, temps=[u'MGR', u'EXC', u"EXIT", u"VALUE"],
910 pipeline=[NormalizeTree(None)])
912 def visit_WithStatNode(self, node):
913 # TODO: Cleanup badly needed
914 TemplateTransform.temp_name_counter += 1
915 handle = "__tmpvar_%d" % TemplateTransform.temp_name_counter
917 self.visitchildren(node, ['body'])
918 excinfo_temp = ExprNodes.NameNode(node.pos, name=handle)#TempHandle(Builtin.tuple_type)
919 if node.target is not None:
920 result = self.template_with_target.substitute({
921 u'EXPR' : node.manager,
923 u'TARGET' : node.target,
924 u'EXCINFO' : excinfo_temp
927 result = self.template_without_target.substitute({
928 u'EXPR' : node.manager,
930 u'EXCINFO' : excinfo_temp
933 # Set except excinfo target to EXCINFO
934 try_except = result.stats[-1].body.stats[-1]
935 try_except.except_clauses[0].excinfo_target = ExprNodes.NameNode(node.pos, name=handle)
936 # excinfo_temp.ref(node.pos))
938 # result.stats[-1].body.stats[-1] = TempsBlockNode(
939 # node.pos, temps=[excinfo_temp], body=try_except)
943 def visit_ExprNode(self, node):
944 # With statements are never inside expressions.
948 class DecoratorTransform(CythonTransform, SkipDeclarations):
950 def visit_DefNode(self, func_node):
951 self.visitchildren(func_node)
952 if not func_node.decorators:
954 return self._handle_decorators(
955 func_node, func_node.name)
957 def visit_CClassDefNode(self, class_node):
958 # This doesn't currently work, so it's disabled.
960 # Problem: assignments to cdef class names do not work. They
961 # would require an additional check anyway, as the extension
962 # type must not change its C type, so decorators cannot
963 # replace an extension type, just alter it and return it.
965 self.visitchildren(class_node)
966 if not class_node.decorators:
968 error(class_node.pos,
969 "Decorators not allowed on cdef classes (used on type '%s')" % class_node.class_name)
971 #return self._handle_decorators(
972 # class_node, class_node.class_name)
974 def visit_ClassDefNode(self, class_node):
975 self.visitchildren(class_node)
976 if not class_node.decorators:
978 return self._handle_decorators(
979 class_node, class_node.name)
981 def _handle_decorators(self, node, name):
982 decorator_result = ExprNodes.NameNode(node.pos, name = name)
983 for decorator in node.decorators[::-1]:
984 decorator_result = ExprNodes.SimpleCallNode(
986 function = decorator.decorator,
987 args = [decorator_result])
989 name_node = ExprNodes.NameNode(node.pos, name = name)
990 reassignment = Nodes.SingleAssignmentNode(
993 rhs = decorator_result)
994 return [node, reassignment]
997 class AnalyseDeclarationsTransform(CythonTransform):
999 basic_property = TreeFragment(u"""
1003 def __set__(self, value):
1005 """, level='c_class')
1006 basic_pyobject_property = TreeFragment(u"""
1010 def __set__(self, value):
1014 """, level='c_class')
1015 basic_property_ro = TreeFragment(u"""
1019 """, level='c_class')
1021 def __call__(self, root):
1022 self.env_stack = [root.scope]
1023 # needed to determine if a cdef var is declared after it's used.
1024 self.seen_vars_stack = []
1025 return super(AnalyseDeclarationsTransform, self).__call__(root)
1027 def visit_NameNode(self, node):
1028 self.seen_vars_stack[-1].add(node.name)
1031 def visit_ModuleNode(self, node):
1032 self.seen_vars_stack.append(cython.set())
1033 node.analyse_declarations(self.env_stack[-1])
1034 self.visitchildren(node)
1035 self.seen_vars_stack.pop()
1038 def visit_LambdaNode(self, node):
1039 node.analyse_declarations(self.env_stack[-1])
1040 self.visitchildren(node)
1043 def visit_ClassDefNode(self, node):
1044 self.env_stack.append(node.scope)
1045 self.visitchildren(node)
1046 self.env_stack.pop()
1049 def visit_CClassDefNode(self, node):
1050 node = self.visit_ClassDefNode(node)
1051 if node.scope and node.scope.implemented:
1053 for entry in node.scope.var_entries:
1054 if entry.needs_property:
1055 property = self.create_Property(entry)
1056 property.analyse_declarations(node.scope)
1057 self.visit(property)
1058 stats.append(property)
1060 node.body.stats += stats
1063 def visit_FuncDefNode(self, node):
1064 self.seen_vars_stack.append(cython.set())
1065 lenv = node.local_scope
1066 node.body.analyse_control_flow(lenv) # this will be totally refactored
1067 node.declare_arguments(lenv)
1068 for var, type_node in node.directive_locals.items():
1069 if not lenv.lookup_here(var): # don't redeclare args
1070 type = type_node.analyse_as_type(lenv)
1072 lenv.declare_var(var, type, type_node.pos)
1074 error(type_node.pos, "Not a type")
1075 node.body.analyse_declarations(lenv)
1076 self.env_stack.append(lenv)
1077 self.visitchildren(node)
1078 self.env_stack.pop()
1079 self.seen_vars_stack.pop()
1082 def visit_ScopedExprNode(self, node):
1083 env = self.env_stack[-1]
1084 node.analyse_declarations(env)
1085 # the node may or may not have a local scope
1086 if node.has_local_scope:
1087 self.seen_vars_stack.append(cython.set(self.seen_vars_stack[-1]))
1088 self.env_stack.append(node.expr_scope)
1089 node.analyse_scoped_declarations(node.expr_scope)
1090 self.visitchildren(node)
1091 self.env_stack.pop()
1092 self.seen_vars_stack.pop()
1094 node.analyse_scoped_declarations(env)
1095 self.visitchildren(node)
1098 def visit_TempResultFromStatNode(self, node):
1099 self.visitchildren(node)
1100 node.analyse_declarations(self.env_stack[-1])
1103 # Some nodes are no longer needed after declaration
1104 # analysis and can be dropped. The analysis was performed
1105 # on these nodes in a seperate recursive process from the
1106 # enclosing function or module, so we can simply drop them.
1107 def visit_CDeclaratorNode(self, node):
1108 # necessary to ensure that all CNameDeclaratorNodes are visited.
1109 self.visitchildren(node)
1112 def visit_CTypeDefNode(self, node):
1115 def visit_CBaseTypeNode(self, node):
1118 def visit_CEnumDefNode(self, node):
1119 if node.visibility == 'public':
1124 def visit_CStructOrUnionDefNode(self, node):
1127 def visit_CNameDeclaratorNode(self, node):
1128 if node.name in self.seen_vars_stack[-1]:
1129 entry = self.env_stack[-1].lookup(node.name)
1130 if entry is None or not entry.c_source.extern:
1131 warning(node.pos, "cdef variable '%s' declared after it is used" % node.name, 2)
1132 self.visitchildren(node)
1135 def visit_CVarDefNode(self, node):
1136 # to ensure all CNameDeclaratorNodes are visited.
1137 self.visitchildren(node)
1140 def create_Property(self, entry):
1141 if entry.python_binding.visibility == 'public':
1142 if entry.type.is_pyobject:
1143 template = self.basic_pyobject_property
1145 template = self.basic_property
1146 elif entry.python_binding.visibility == 'readonly':
1147 template = self.basic_property_ro
1149 raise NotImplementedError('private python methods')
1150 property = template.substitute({
1151 u"ATTR": ExprNodes.AttributeNode(
1153 obj=ExprNodes.NameNode(pos=entry.pos, name="self"),
1154 attribute=entry.python_binding.name),
1155 }, pos=entry.pos).stats[0]
1156 property.name = entry.python_binding.name
1157 # ---------------------------------------
1158 # XXX This should go to AutoDocTransforms
1159 # ---------------------------------------
1160 if (Options.docstrings and
1161 self.current_directives['embedsignature']):
1162 attr_name = entry.python_binding.name
1163 type_name = entry.type.declaration_code("", for_display=1)
1165 if not entry.type.is_pyobject:
1166 type_name = "'%s'" % type_name
1167 elif entry.type.is_extension_type:
1168 type_name = entry.type.module_name + '.' + type_name
1169 if entry.init is not None:
1170 default_value = ' = ' + entry.init
1171 elif entry.init_to_none:
1172 default_value = ' = ' + repr(None)
1173 docstring = attr_name + ': ' + type_name + default_value
1174 property.doc = EncodedString(docstring)
1175 # ---------------------------------------
1178 class AnalyseExpressionsTransform(CythonTransform):
1180 def visit_ModuleNode(self, node):
1181 node.scope.infer_types()
1182 node.body.analyse_expressions(node.scope)
1183 self.visitchildren(node)
1186 def visit_FuncDefNode(self, node):
1187 node.local_scope.infer_types()
1188 node.body.analyse_expressions(node.local_scope)
1189 self.visitchildren(node)
1192 def visit_ScopedExprNode(self, node):
1193 if node.has_local_scope:
1194 node.expr_scope.infer_types()
1195 node.analyse_scoped_expressions(node.expr_scope)
1196 self.visitchildren(node)
1199 class ExpandInplaceOperators(EnvTransform):
1201 def visit_InPlaceAssignmentNode(self, node):
1204 if lhs.type.is_cpp_class:
1205 # No getting around this exact operator here.
1207 if isinstance(lhs, ExprNodes.IndexNode) and lhs.is_buffer_access:
1208 # There is code to handle this case.
1211 env = self.current_env()
1212 def side_effect_free_reference(node, setting=False):
1213 if isinstance(node, ExprNodes.NameNode):
1215 elif node.type.is_pyobject and not setting:
1216 node = LetRefNode(node)
1218 elif isinstance(node, ExprNodes.IndexNode):
1219 if node.is_buffer_access:
1220 raise ValueError, "Buffer access"
1221 base, temps = side_effect_free_reference(node.base)
1222 index = LetRefNode(node.index)
1223 return ExprNodes.IndexNode(node.pos, base=base, index=index), temps + [index]
1224 elif isinstance(node, ExprNodes.AttributeNode):
1225 obj, temps = side_effect_free_reference(node.obj)
1226 return ExprNodes.AttributeNode(node.pos, obj=obj, attribute=node.attribute), temps
1228 node = LetRefNode(node)
1231 lhs, let_ref_nodes = side_effect_free_reference(lhs, setting=True)
1234 dup = lhs.__class__(**lhs.__dict__)
1235 binop = ExprNodes.binop_node(node.pos,
1236 operator = node.operator,
1240 # Manually analyse types for new node.
1241 lhs.analyse_target_types(env)
1242 dup.analyse_types(env)
1243 binop.analyse_operation(env)
1244 node = Nodes.SingleAssignmentNode(
1247 rhs=binop.coerce_to(lhs.type, env))
1248 # Use LetRefNode to avoid side effects.
1249 let_ref_nodes.reverse()
1250 for t in let_ref_nodes:
1251 node = LetNode(t, node)
1254 def visit_ExprNode(self, node):
1255 # In-place assignments can't happen within an expression.
1259 class AlignFunctionDefinitions(CythonTransform):
1261 This class takes the signatures from a .pxd file and applies them to
1262 the def methods in a .py file.
1265 def visit_ModuleNode(self, node):
1266 self.scope = node.scope
1267 self.directives = node.directives
1268 self.visitchildren(node)
1271 def visit_PyClassDefNode(self, node):
1272 pxd_def = self.scope.lookup(node.name)
1274 if pxd_def.is_cclass:
1275 return self.visit_CClassDefNode(node.as_cclass(), pxd_def)
1277 error(node.pos, "'%s' redeclared" % node.name)
1278 error(pxd_def.pos, "previous declaration here")
1283 def visit_CClassDefNode(self, node, pxd_def=None):
1285 pxd_def = self.scope.lookup(node.class_name)
1287 outer_scope = self.scope
1288 self.scope = pxd_def.type.scope
1289 self.visitchildren(node)
1291 self.scope = outer_scope
1294 def visit_DefNode(self, node):
1295 pxd_def = self.scope.lookup(node.name)
1297 if not pxd_def.is_cfunction:
1298 error(node.pos, "'%s' redeclared" % node.name)
1299 error(pxd_def.pos, "previous declaration here")
1301 node = node.as_cfunction(pxd_def)
1302 elif self.scope.is_module_scope and self.directives['auto_cpdef']:
1303 node = node.as_cfunction(scope=self.scope)
1304 # Enable this when internal def functions are allowed.
1305 # self.visitchildren(node)
1309 class MarkClosureVisitor(CythonTransform):
1311 def visit_ModuleNode(self, node):
1312 self.needs_closure = False
1313 self.visitchildren(node)
1316 def visit_FuncDefNode(self, node):
1317 self.needs_closure = False
1318 self.visitchildren(node)
1319 node.needs_closure = self.needs_closure
1320 self.needs_closure = True
1323 def visit_CFuncDefNode(self, node):
1324 self.visit_FuncDefNode(node)
1325 if node.needs_closure:
1326 error(node.pos, "closures inside cdef functions not yet supported")
1329 def visit_LambdaNode(self, node):
1330 self.needs_closure = False
1331 self.visitchildren(node)
1332 node.needs_closure = self.needs_closure
1333 self.needs_closure = True
1336 def visit_ClassDefNode(self, node):
1337 self.visitchildren(node)
1338 self.needs_closure = True
1342 class CreateClosureClasses(CythonTransform):
1343 # Output closure classes in module scope for all functions
1344 # that really need it.
1346 def __init__(self, context):
1347 super(CreateClosureClasses, self).__init__(context)
1349 self.in_lambda = False
1351 def visit_ModuleNode(self, node):
1352 self.module_scope = node.scope
1353 self.visitchildren(node)
1356 def get_scope_use(self, node):
1359 for name, entry in node.local_scope.entries.items():
1360 if entry.from_closure:
1361 from_closure.append((name, entry))
1362 elif entry.in_closure and not entry.from_closure:
1363 in_closure.append((name, entry))
1364 return from_closure, in_closure
1366 def create_class_from_scope(self, node, target_module_scope, inner_node=None):
1367 from_closure, in_closure = self.get_scope_use(node)
1370 # Now from the begining
1371 node.needs_closure = False
1372 node.needs_outer_scope = False
1374 func_scope = node.local_scope
1375 cscope = node.entry.scope
1376 while cscope.is_py_class_scope or cscope.is_c_class_scope:
1377 cscope = cscope.outer_scope
1379 if not from_closure and (self.path or inner_node):
1382 raise InternalError, "DefNode does not have assignment node"
1383 inner_node = node.assmt.rhs
1384 inner_node.needs_self_code = False
1385 node.needs_outer_scope = False
1387 if not in_closure and not from_closure:
1389 elif not in_closure:
1390 func_scope.is_passthrough = True
1391 func_scope.scope_class = cscope.scope_class
1392 node.needs_outer_scope = True
1395 as_name = '%s_%s' % (
1396 target_module_scope.next_id(Naming.closure_class_prefix),
1397 node.entry.c_binding.name)
1399 entry = target_module_scope.declare_c_class(name = as_name,
1400 pos = node.pos, defining = True, implementing = True)
1401 func_scope.scope_class = entry
1402 class_scope = entry.type.scope
1403 class_scope.is_internal = True
1404 class_scope.directives = {'final': True}
1407 assert cscope.is_closure_scope
1408 class_scope.declare_var(pos=node.pos,
1409 name=Naming.outer_scope_cname,
1410 cname=Naming.outer_scope_cname,
1411 type=cscope.scope_class.type,
1413 node.needs_outer_scope = True
1414 for name, entry in in_closure:
1415 class_scope.declare_var(pos=entry.pos,
1416 name=entry.python_binding.name,
1417 cname=entry.c_binding.name,
1420 node.needs_closure = True
1421 # Do it here because other classes are already checked
1422 target_module_scope.check_c_class(func_scope.scope_class)
1424 def visit_LambdaNode(self, node):
1425 was_in_lambda = self.in_lambda
1426 self.in_lambda = True
1427 self.create_class_from_scope(node.def_node, self.module_scope, node)
1428 self.visitchildren(node)
1429 self.in_lambda = was_in_lambda
1432 def visit_FuncDefNode(self, node):
1434 self.visitchildren(node)
1436 if node.needs_closure or self.path:
1437 self.create_class_from_scope(node, self.module_scope)
1438 self.path.append(node)
1439 self.visitchildren(node)
1444 class GilCheck(VisitorTransform):
1446 Call `node.gil_check(env)` on each node to make sure we hold the
1447 GIL when we need it. Raise an error when on Python operations
1448 inside a `nogil` environment.
1450 def __call__(self, root):
1451 self.env_stack = [root.scope]
1453 return super(GilCheck, self).__call__(root)
1455 def visit_FuncDefNode(self, node):
1456 self.env_stack.append(node.local_scope)
1457 was_nogil = self.nogil
1458 self.nogil = node.local_scope.nogil
1459 if self.nogil and node.nogil_check:
1460 node.nogil_check(node.local_scope)
1461 self.visitchildren(node)
1462 self.env_stack.pop()
1463 self.nogil = was_nogil
1466 def visit_GILStatNode(self, node):
1467 env = self.env_stack[-1]
1468 if self.nogil and node.nogil_check: node.nogil_check()
1469 was_nogil = self.nogil
1470 self.nogil = (node.state == 'nogil')
1471 self.visitchildren(node)
1472 self.nogil = was_nogil
1475 def visit_Node(self, node):
1476 if self.env_stack and self.nogil and node.nogil_check:
1477 node.nogil_check(self.env_stack[-1])
1478 self.visitchildren(node)
1482 class TransformBuiltinMethods(EnvTransform):
1484 def visit_SingleAssignmentNode(self, node):
1485 if node.declaration_only:
1488 self.visitchildren(node)
1491 def visit_AttributeNode(self, node):
1492 self.visitchildren(node)
1493 return self.visit_cython_attribute(node)
1495 def visit_NameNode(self, node):
1496 return self.visit_cython_attribute(node)
1498 def visit_cython_attribute(self, node):
1499 attribute = node.as_cython_attribute()
1501 if attribute == u'compiled':
1502 node = ExprNodes.BoolNode(node.pos, value=True)
1503 elif attribute == u'NULL':
1504 node = ExprNodes.NullNode(node.pos)
1505 elif attribute in (u'set', u'frozenset'):
1506 node = ExprNodes.NameNode(node.pos, name=EncodedString(attribute),
1507 entry=self.current_env().builtin_scope().lookup_here(attribute))
1508 elif not PyrexTypes.parse_basic_type(attribute):
1509 error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute)
1512 def visit_SimpleCallNode(self, node):
1515 if isinstance(node.function, ExprNodes.NameNode):
1516 if node.function.name == 'locals':
1517 lenv = self.current_env()
1518 entry = lenv.lookup_here('locals')
1520 # not the builtin 'locals'
1522 if len(node.args) > 0:
1523 error(self.pos, "Builtin 'locals()' called with wrong number of args, expected 0, got %d" % len(node.args))
1526 items = [ ExprNodes.DictItemNode(pos,
1527 key=ExprNodes.StringNode(pos, value=var),
1528 value=ExprNodes.NameNode(pos, name=var))
1529 for var in lenv.entries ]
1530 return ExprNodes.DictNode(pos, key_value_pairs=items)
1533 function = node.function.as_cython_attribute()
1535 if function in InterpretCompilerDirectives.unop_method_nodes:
1536 if len(node.args) != 1:
1537 error(node.function.pos, u"%s() takes exactly one argument" % function)
1539 node = InterpretCompilerDirectives.unop_method_nodes[function](node.function.pos, operand=node.args[0])
1540 elif function in InterpretCompilerDirectives.binop_method_nodes:
1541 if len(node.args) != 2:
1542 error(node.function.pos, u"%s() takes exactly two arguments" % function)
1544 node = InterpretCompilerDirectives.binop_method_nodes[function](node.function.pos, operand1=node.args[0], operand2=node.args[1])
1545 elif function == u'cast':
1546 if len(node.args) != 2:
1547 error(node.function.pos, u"cast() takes exactly two arguments")
1549 type = node.args[0].analyse_as_type(self.current_env())
1551 node = ExprNodes.TypecastNode(node.function.pos, type=type, operand=node.args[1])
1553 error(node.args[0].pos, "Not a type")
1554 elif function == u'sizeof':
1555 if len(node.args) != 1:
1556 error(node.function.pos, u"sizeof() takes exactly one argument")
1558 type = node.args[0].analyse_as_type(self.current_env())
1560 node = ExprNodes.SizeofTypeNode(node.function.pos, arg_type=type)
1562 node = ExprNodes.SizeofVarNode(node.function.pos, operand=node.args[0])
1563 elif function == 'cmod':
1564 if len(node.args) != 2:
1565 error(node.function.pos, u"cmod() takes exactly two arguments")
1567 node = ExprNodes.binop_node(node.function.pos, '%', node.args[0], node.args[1])
1568 node.cdivision = True
1569 elif function == 'cdiv':
1570 if len(node.args) != 2:
1571 error(node.function.pos, u"cdiv() takes exactly two arguments")
1573 node = ExprNodes.binop_node(node.function.pos, '/', node.args[0], node.args[1])
1574 node.cdivision = True
1575 elif function == u'set':
1576 node.function = ExprNodes.NameNode(node.pos, name=EncodedString('set'))
1578 error(node.function.pos, u"'%s' not a valid cython language construct" % function)
1580 self.visitchildren(node)
1584 class DebugTransform(CythonTransform):
1586 Create debug information and all functions' visibility to extern in order
1587 to enable debugging.
1590 def __init__(self, context, options, result):
1591 super(DebugTransform, self).__init__(context)
1592 self.visited = cython.set()
1593 # our treebuilder and debug output writer
1594 # (see Cython.Debugger.debug_output.CythonDebugWriter)
1595 self.tb = self.context.gdb_debug_outputwriter
1596 #self.c_output_file = options.output_file
1597 self.c_output_file = result.c_file
1599 # Closure support, basically treat nested functions as if the AST were
1601 self.nested_funcdefs = []
1603 # tells visit_NameNode whether it should register step-into functions
1604 self.register_stepinto = False
1606 def visit_ModuleNode(self, node):
1607 self.tb.module_name = node.full_module_name
1609 module_name=node.full_module_name,
1610 filename=node.pos[0].filename,
1611 c_filename=self.c_output_file)
1613 self.tb.start('Module', attrs)
1615 # serialize functions
1616 self.tb.start('Functions')
1617 # First, serialize functions normally...
1618 self.visitchildren(node)
1620 # ... then, serialize nested functions
1621 for nested_funcdef in self.nested_funcdefs:
1622 self.visit_FuncDefNode(nested_funcdef)
1624 self.register_stepinto = True
1625 self.serialize_modulenode_as_function(node)
1626 self.register_stepinto = False
1627 self.tb.end('Functions')
1629 # 2.3 compatibility. Serialize global variables
1630 self.tb.start('Globals')
1633 for k, v in node.scope.entries.iteritems():
1634 if (v.qualified_name not in self.visited and not
1635 v.name.startswith('__pyx_') and not
1636 v.type.is_cfunction and not
1637 v.type.is_extension_type):
1640 self.serialize_local_variables(entries)
1641 self.tb.end('Globals')
1642 # self.tb.end('Module') # end Module after the line number mapping in
1643 # Cython.Compiler.ModuleNode.ModuleNode._serialize_lineno_map
1646 def visit_FuncDefNode(self, node):
1647 self.visited.add(node.local_scope.qualified_name)
1649 if getattr(node, 'is_wrapper', False):
1652 if self.register_stepinto:
1653 self.nested_funcdefs.append(node)
1656 # node.entry.c_source.extern
1657 if node.py_func is None:
1660 pf_cname = node.py_func.entry.func_cname
1663 name=node.entry.python_binding.name,
1664 cname=node.entry.func_cname,
1666 qualified_name=node.local_scope.qualified_name,
1667 lineno=str(node.pos[1]))
1669 self.tb.start('Function', attrs=attrs)
1671 self.tb.start('Locals')
1672 self.serialize_local_variables(node.local_scope.entries)
1673 self.tb.end('Locals')
1675 self.tb.start('Arguments')
1676 for arg in node.local_scope.arg_entries:
1677 self.tb.start(arg.name)
1678 self.tb.end(arg.name)
1679 self.tb.end('Arguments')
1681 self.tb.start('StepIntoFunctions')
1682 self.register_stepinto = True
1683 self.visitchildren(node)
1684 self.register_stepinto = False
1685 self.tb.end('StepIntoFunctions')
1686 self.tb.end('Function')
1690 def visit_NameNode(self, node):
1691 if (self.register_stepinto and
1692 node.type.is_cfunction and
1693 getattr(node, 'is_called', False) and
1694 node.entry.func_cname is not None):
1695 # don't check node.entry.in_cinclude, as 'cdef extern: ...'
1696 # declared functions are not 'in_cinclude'.
1697 # This means we will list called 'cdef' functions as
1698 # "step into functions", but this is not an issue as they will be
1699 # recognized as Cython functions anyway.
1700 attrs = dict(name=node.entry.func_cname)
1701 self.tb.start('StepIntoFunction', attrs=attrs)
1702 self.tb.end('StepIntoFunction')
1704 self.visitchildren(node)
1707 def serialize_modulenode_as_function(self, node):
1709 Serialize the module-level code as a function so the debugger will know
1710 it's a "relevant frame" and it will know where to set the breakpoint
1711 for 'break modulename'.
1713 name = node.full_module_name.rpartition('.')[-1]
1715 cname_py2 = 'init' + name
1716 cname_py3 = 'PyInit_' + name
1722 # Ignore the qualified_name, breakpoints should be set using
1723 # `cy break modulename:lineno` for module-level breakpoints.
1726 is_initmodule_function="True",
1729 py3_attrs = dict(py2_attrs, cname=cname_py3)
1731 self._serialize_modulenode_as_function(node, py2_attrs)
1732 self._serialize_modulenode_as_function(node, py3_attrs)
1734 def _serialize_modulenode_as_function(self, node, attrs):
1735 self.tb.start('Function', attrs=attrs)
1737 self.tb.start('Locals')
1738 self.serialize_local_variables(node.scope.entries)
1739 self.tb.end('Locals')
1741 self.tb.start('Arguments')
1742 self.tb.end('Arguments')
1744 self.tb.start('StepIntoFunctions')
1745 self.register_stepinto = True
1746 self.visitchildren(node)
1747 self.register_stepinto = False
1748 self.tb.end('StepIntoFunctions')
1750 self.tb.end('Function')
1752 def serialize_local_variables(self, entries):
1753 for entry in entries.values():
1754 if entry.type.is_pyobject:
1755 vartype = 'PythonObject'
1759 if entry.from_closure:
1760 # We're dealing with a closure where a variable from an outer
1761 # scope is accessed, get it from the scope object.
1762 cname = '%s->%s' % (Naming.cur_scope_cname,
1763 entry.outer_entry.c_binding.name)
1765 qname = '%s.%s.%s' % (entry.scope.outer_scope.qualified_name,
1767 entry.python_binding.name)
1768 elif entry.in_closure:
1769 cname = '%s->%s' % (Naming.cur_scope_cname,
1770 entry.c_binding.name)
1771 qname = entry.qualified_name
1773 cname = entry.c_binding.name
1774 qname = entry.qualified_name
1777 # this happens for variables that are not in the user's code,
1778 # e.g. for the global __builtins__, __doc__, etc. We can just
1779 # set the lineno to 0 for those.
1782 lineno = str(entry.pos[1])
1785 name=entry.python_binding.name,
1787 qualified_name=qname,
1791 self.tb.start('LocalVar', attrs)
1792 self.tb.end('LocalVar')