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):
67 class NormalizeTree(CythonTransform):
69 This transform fixes up a few things after parsing
70 in order to make the parse tree more suitable for
73 a) After parsing, blocks with only one statement will
74 be represented by that statement, not by a StatListNode.
75 When doing transforms this is annoying and inconsistent,
76 as one cannot in general remove a statement in a consistent
77 way and so on. This transform wraps any single statements
78 in a StatListNode containing a single statement.
80 b) The PassStatNode is a noop and serves no purpose beyond
81 plugging such one-statement blocks; i.e., once parsed a
82 ` "pass" can just as well be represented using an empty
83 StatListNode. This means less special cases to worry about
84 in subsequent transforms (one always checks to see if a
85 StatListNode has no children to see if the block is empty).
88 def __init__(self, context):
89 super(NormalizeTree, self).__init__(context)
90 self.is_in_statlist = False
91 self.is_in_expr = False
93 def visit_ExprNode(self, node):
94 stacktmp = self.is_in_expr
95 self.is_in_expr = True
96 self.visitchildren(node)
97 self.is_in_expr = stacktmp
100 def visit_StatNode(self, node, is_listcontainer=False):
101 stacktmp = self.is_in_statlist
102 self.is_in_statlist = is_listcontainer
103 self.visitchildren(node)
104 self.is_in_statlist = stacktmp
105 if not self.is_in_statlist and not self.is_in_expr:
106 return Nodes.StatListNode(pos=node.pos, stats=[node])
110 def visit_StatListNode(self, node):
111 self.is_in_statlist = True
112 self.visitchildren(node)
113 self.is_in_statlist = False
116 def visit_ParallelAssignmentNode(self, node):
117 return self.visit_StatNode(node, True)
119 def visit_CEnumDefNode(self, node):
120 return self.visit_StatNode(node, True)
122 def visit_CStructOrUnionDefNode(self, node):
123 return self.visit_StatNode(node, True)
125 # Eliminate PassStatNode
126 def visit_PassStatNode(self, node):
127 if not self.is_in_statlist:
128 return Nodes.StatListNode(pos=node.pos, stats=[])
132 def visit_CDeclaratorNode(self, node):
136 class PostParseError(CompileError): pass
138 # error strings checked by unit tests, so define them
139 ERR_CDEF_INCLASS = 'Cannot assign default value to fields in cdef classes, structs or unions'
140 ERR_BUF_DEFAULTS = 'Invalid buffer defaults specification (see docs)'
141 ERR_INVALID_SPECIALATTR_TYPE = 'Special attributes must not have a type declared'
142 class PostParse(ScopeTrackingTransform):
144 Basic interpretation of the parse tree, as well as validity
145 checking that can be done on a very basic level on the parse
146 tree (while still not being a problem with the basic syntax,
150 - Default values to cdef assignments are turned into single
151 assignments following the declaration (everywhere but in class
152 bodies, where they raise a compile error)
154 - Interpret some node structures into Python runtime values.
155 Some nodes take compile-time arguments (currently:
156 TemplatedTypeNode[args] and __cythonbufferdefaults__ = {args}),
157 which should be interpreted. This happens in a general way
158 and other steps should be taken to ensure validity.
160 Type arguments cannot be interpreted in this way.
162 - For __cythonbufferdefaults__ the arguments are checked for
165 TemplatedTypeNode has its directives interpreted:
166 Any first positional argument goes into the "dtype" attribute,
167 any "ndim" keyword argument goes into the "ndim" attribute and
168 so on. Also it is checked that the directive combination is valid.
169 - __cythonbufferdefaults__ attributes are parsed and put into the
172 Note: Currently Parsing.py does a lot of interpretation and
173 reorganization that can be refactored into this transform
174 if a more pure Abstract Syntax Tree is wanted.
177 def __init__(self, context):
178 super(PostParse, self).__init__(context)
179 self.specialattribute_handlers = {
180 '__cythonbufferdefaults__' : self.handle_bufferdefaults
183 def visit_ModuleNode(self, node):
184 self.lambda_counter = 1
185 self.genexpr_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)
193 collector = YieldNodeCollector()
194 collector.visitchildren(node.result_expr)
195 if collector.yields or isinstance(node.result_expr, ExprNodes.YieldExprNode):
196 body = Nodes.ExprStatNode(
197 node.result_expr.pos, expr=node.result_expr)
199 body = Nodes.ReturnStatNode(
200 node.result_expr.pos, value=node.result_expr)
201 node.def_node = Nodes.DefNode(
202 node.pos, name=node.name, lambda_name=node.lambda_name,
203 args=node.args, star_arg=node.star_arg,
204 starstar_arg=node.starstar_arg,
206 self.visitchildren(node)
209 def visit_GeneratorExpressionNode(self, node):
210 # unpack a generator expression into the corresponding DefNode
211 genexpr_id = self.genexpr_counter
212 self.genexpr_counter += 1
213 node.genexpr_name = EncodedString(u'genexpr%d' % genexpr_id)
215 node.def_node = Nodes.DefNode(node.pos, name=node.name,
217 args=[], star_arg=None,
220 self.visitchildren(node)
224 def handle_bufferdefaults(self, decl):
225 if not isinstance(decl.default, ExprNodes.DictNode):
226 raise PostParseError(decl.pos, ERR_BUF_DEFAULTS)
227 self.scope_node.buffer_defaults_node = decl.default
228 self.scope_node.buffer_defaults_pos = decl.pos
230 def visit_CVarDefNode(self, node):
231 # This assumes only plain names and pointers are assignable on
232 # declaration. Also, it makes use of the fact that a cdef decl
233 # must appear before the first use, so we don't have to deal with
234 # "i = 3; cdef int i = i" and can simply move the nodes around.
236 self.visitchildren(node)
239 for decl in node.declarators:
241 while isinstance(declbase, Nodes.CPtrDeclaratorNode):
242 declbase = declbase.base
243 if isinstance(declbase, Nodes.CNameDeclaratorNode):
244 if declbase.default is not None:
245 if self.scope_type in ('cclass', 'pyclass', 'struct'):
246 if isinstance(self.scope_node, Nodes.CClassDefNode):
247 handler = self.specialattribute_handlers.get(decl.name)
249 if decl is not declbase:
250 raise PostParseError(decl.pos, ERR_INVALID_SPECIALATTR_TYPE)
252 continue # Remove declaration
253 raise PostParseError(decl.pos, ERR_CDEF_INCLASS)
254 first_assignment = self.scope_type != 'module'
255 stats.append(Nodes.SingleAssignmentNode(node.pos,
256 lhs=ExprNodes.NameNode(node.pos, name=declbase.name),
257 rhs=declbase.default, first=first_assignment))
258 declbase.default = None
259 newdecls.append(decl)
260 node.declarators = newdecls
262 except PostParseError, e:
263 # An error in a cdef clause is ok, simply remove the declaration
264 # and try to move on to report more errors
265 self.context.nonfatal_error(e)
268 # Split parallel assignments (a,b = b,a) into separate partial
269 # assignments that are executed rhs-first using temps. This
270 # restructuring must be applied before type analysis so that known
271 # types on rhs and lhs can be matched directly. It is required in
272 # the case that the types cannot be coerced to a Python type in
273 # order to assign from a tuple.
275 def visit_SingleAssignmentNode(self, node):
276 self.visitchildren(node)
277 return self._visit_assignment_node(node, [node.lhs, node.rhs])
279 def visit_CascadedAssignmentNode(self, node):
280 self.visitchildren(node)
281 return self._visit_assignment_node(node, node.lhs_list + [node.rhs])
283 def _visit_assignment_node(self, node, expr_list):
284 """Flatten parallel assignments into separate single
285 assignments or cascaded assignments.
287 if sum([ 1 for expr in expr_list if expr.is_sequence_constructor ]) < 2:
288 # no parallel assignments => nothing to do
292 flatten_parallel_assignments(expr_list, expr_list_list)
294 eliminate_rhs_duplicates(expr_list_list, temp_refs)
297 for expr_list in expr_list_list:
298 lhs_list = expr_list[:-1]
300 if len(lhs_list) == 1:
301 node = Nodes.SingleAssignmentNode(rhs.pos,
302 lhs = lhs_list[0], rhs = rhs)
304 node = Nodes.CascadedAssignmentNode(rhs.pos,
305 lhs_list = lhs_list, rhs = rhs)
309 assign_node = nodes[0]
311 assign_node = Nodes.ParallelAssignmentNode(nodes[0].pos, stats = nodes)
314 duplicates_and_temps = [ (temp.expression, temp)
315 for temp in temp_refs ]
316 sort_common_subsequences(duplicates_and_temps)
317 for _, temp_ref in duplicates_and_temps[::-1]:
318 assign_node = LetNode(temp_ref, assign_node)
322 def _flatten_sequence(self, seq, result):
324 if arg.is_sequence_constructor:
325 self._flatten_sequence(arg, result)
330 def visit_DelStatNode(self, node):
331 self.visitchildren(node)
332 node.args = self._flatten_sequence(node, [])
336 def eliminate_rhs_duplicates(expr_list_list, ref_node_sequence):
337 """Replace rhs items by LetRefNodes if they appear more than once.
338 Creates a sequence of LetRefNodes that set up the required temps
339 and appends them to ref_node_sequence. The input list is modified
342 seen_nodes = cython.set()
344 def find_duplicates(node):
345 if node.is_literal or node.is_name:
346 # no need to replace those; can't include attributes here
347 # as their access is not necessarily side-effect free
349 if node in seen_nodes:
350 if node not in ref_nodes:
351 ref_node = LetRefNode(node)
352 ref_nodes[node] = ref_node
353 ref_node_sequence.append(ref_node)
356 if node.is_sequence_constructor:
357 for item in node.args:
358 find_duplicates(item)
360 for expr_list in expr_list_list:
366 def substitute_nodes(node):
367 if node in ref_nodes:
368 return ref_nodes[node]
369 elif node.is_sequence_constructor:
370 node.args = list(map(substitute_nodes, node.args))
373 # replace nodes inside of the common subexpressions
374 for node in ref_nodes:
375 if node.is_sequence_constructor:
376 node.args = list(map(substitute_nodes, node.args))
378 # replace common subexpressions on all rhs items
379 for expr_list in expr_list_list:
380 expr_list[-1] = substitute_nodes(expr_list[-1])
382 def sort_common_subsequences(items):
383 """Sort items/subsequences so that all items and subsequences that
384 an item contains appear before the item itself. This is needed
385 because each rhs item must only be evaluated once, so its value
386 must be evaluated first and then reused when packing sequences
389 This implies a partial order, and the sort must be stable to
390 preserve the original order as much as possible, so we use a
391 simple insertion sort (which is very fast for short sequences, the
392 normal case in practice).
394 def contains(seq, x):
398 elif item.is_sequence_constructor and contains(item.args, x):
402 return b.is_sequence_constructor and contains(b.args, a)
404 for pos, item in enumerate(items):
405 key = item[1] # the ResultRefNode which has already been injected into the sequences
407 for i in xrange(pos-1, -1, -1):
408 if lower_than(key, items[i][0]):
411 for i in xrange(pos, new_pos, -1):
412 items[i] = items[i-1]
413 items[new_pos] = item
415 def flatten_parallel_assignments(input, output):
416 # The input is a list of expression nodes, representing the LHSs
417 # and RHS of one (possibly cascaded) assignment statement. For
418 # sequence constructors, rearranges the matching parts of both
419 # sides into a list of equivalent assignments between the
420 # individual elements. This transformation is applied
421 # recursively, so that nested structures get matched as well.
423 if not rhs.is_sequence_constructor or not sum([lhs.is_sequence_constructor for lhs in input[:-1]]):
427 complete_assignments = []
429 rhs_size = len(rhs.args)
430 lhs_targets = [ [] for _ in xrange(rhs_size) ]
431 starred_assignments = []
432 for lhs in input[:-1]:
433 if not lhs.is_sequence_constructor:
435 error(lhs.pos, "starred assignment target must be in a list or tuple")
436 complete_assignments.append(lhs)
438 lhs_size = len(lhs.args)
439 starred_targets = sum([1 for expr in lhs.args if expr.is_starred])
440 if starred_targets > 1:
441 error(lhs.pos, "more than 1 starred expression in assignment")
442 output.append([lhs,rhs])
444 elif lhs_size - starred_targets > rhs_size:
445 error(lhs.pos, "need more than %d value%s to unpack"
446 % (rhs_size, (rhs_size != 1) and 's' or ''))
447 output.append([lhs,rhs])
449 elif starred_targets:
450 map_starred_assignment(lhs_targets, starred_assignments,
452 elif lhs_size < rhs_size:
453 error(lhs.pos, "too many values to unpack (expected %d, got %d)"
454 % (lhs_size, rhs_size))
455 output.append([lhs,rhs])
458 for targets, expr in zip(lhs_targets, lhs.args):
461 if complete_assignments:
462 complete_assignments.append(rhs)
463 output.append(complete_assignments)
465 # recursively flatten partial assignments
466 for cascade, rhs in zip(lhs_targets, rhs.args):
469 flatten_parallel_assignments(cascade, output)
471 # recursively flatten starred assignments
472 for cascade in starred_assignments:
473 if cascade[0].is_sequence_constructor:
474 flatten_parallel_assignments(cascade, output)
476 output.append(cascade)
478 def map_starred_assignment(lhs_targets, starred_assignments, lhs_args, rhs_args):
479 # Appends the fixed-position LHS targets to the target list that
480 # appear left and right of the starred argument.
482 # The starred_assignments list receives a new tuple
483 # (lhs_target, rhs_values_list) that maps the remaining arguments
484 # (those that match the starred target) to a list.
486 # left side of the starred target
487 for i, (targets, expr) in enumerate(zip(lhs_targets, lhs_args)):
490 lhs_remaining = len(lhs_args) - i - 1
494 raise InternalError("no starred arg found when splitting starred assignment")
496 # right side of the starred target
497 for i, (targets, expr) in enumerate(zip(lhs_targets[-lhs_remaining:],
498 lhs_args[starred + 1:])):
501 # the starred target itself, must be assigned a (potentially empty) list
502 target = lhs_args[starred].target # unpack starred node
503 starred_rhs = rhs_args[starred:]
505 starred_rhs = starred_rhs[:-lhs_remaining]
507 pos = starred_rhs[0].pos
510 starred_assignments.append([
511 target, ExprNodes.ListNode(pos=pos, args=starred_rhs)])
514 class PxdPostParse(CythonTransform, SkipDeclarations):
516 Basic interpretation/validity checking that should only be
519 A lot of this checking currently happens in the parser; but
520 what is listed below happens here.
522 - "def" functions are let through only if they fill the
523 getbuffer/releasebuffer slots
525 - cdef functions are let through only if they are on the
526 top level and are declared "inline"
528 ERR_INLINE_ONLY = "function definition in pxd file must be declared 'cdef inline'"
529 ERR_NOGO_WITH_INLINE = "inline function definition in pxd file cannot be '%s'"
531 def __call__(self, node):
532 self.scope_type = 'pxd'
533 return super(PxdPostParse, self).__call__(node)
535 def visit_CClassDefNode(self, node):
536 old = self.scope_type
537 self.scope_type = 'cclass'
538 self.visitchildren(node)
539 self.scope_type = old
542 def visit_FuncDefNode(self, node):
543 # FuncDefNode always come with an implementation (without
544 # an imp they are CVarDefNodes..)
545 err = self.ERR_INLINE_ONLY
547 if (isinstance(node, Nodes.DefNode) and self.scope_type == 'cclass'
548 and node.name in ('__getbuffer__', '__releasebuffer__')):
549 err = None # allow these slots
551 if isinstance(node, Nodes.CFuncDefNode):
552 if u'inline' in node.modifiers and self.scope_type == 'pxd':
553 node.inline_in_pxd = True
554 if node.visibility != 'private':
555 err = self.ERR_NOGO_WITH_INLINE % node.visibility
557 err = self.ERR_NOGO_WITH_INLINE % 'api'
559 err = None # allow inline function
561 err = self.ERR_INLINE_ONLY
564 self.context.nonfatal_error(PostParseError(node.pos, err))
569 class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
571 After parsing, directives can be stored in a number of places:
572 - #cython-comments at the top of the file (stored in ModuleNode)
573 - Command-line arguments overriding these
574 - @cython.directivename decorators
575 - with cython.directivename: statements
577 This transform is responsible for interpreting these various sources
578 and store the directive in two ways:
579 - Set the directives attribute of the ModuleNode for global directives.
580 - Use a CompilerDirectivesNode to override directives for a subtree.
582 (The first one is primarily to not have to modify with the tree
583 structure, so that ModuleNode stay on top.)
585 The directives are stored in dictionaries from name to value in effect.
586 Each such dictionary is always filled in for all possible directives,
587 using default values where no value is given by the user.
589 The available directives are controlled in Options.py.
591 Note that we have to run this prior to analysis, and so some minor
592 duplication of functionality has to occur: We manually track cimports
593 and which names the "cython" module may have been imported to.
595 unop_method_nodes = {
596 'typeof': ExprNodes.TypeofNode,
598 'operator.address': ExprNodes.AmpersandNode,
599 'operator.dereference': ExprNodes.DereferenceNode,
600 'operator.preincrement' : ExprNodes.inc_dec_constructor(True, '++'),
601 'operator.predecrement' : ExprNodes.inc_dec_constructor(True, '--'),
602 'operator.postincrement': ExprNodes.inc_dec_constructor(False, '++'),
603 'operator.postdecrement': ExprNodes.inc_dec_constructor(False, '--'),
605 # For backwards compatability.
606 'address': ExprNodes.AmpersandNode,
609 binop_method_nodes = {
610 'operator.comma' : ExprNodes.c_binop_constructor(','),
613 special_methods = cython.set(['declare', 'union', 'struct', 'typedef', 'sizeof',
614 'cast', 'pointer', 'compiled', 'NULL'])
615 special_methods.update(unop_method_nodes.keys())
617 def __init__(self, context, compilation_directive_defaults):
618 super(InterpretCompilerDirectives, self).__init__(context)
619 self.compilation_directive_defaults = {}
620 for key, value in compilation_directive_defaults.items():
621 self.compilation_directive_defaults[unicode(key)] = copy.deepcopy(value)
622 self.cython_module_names = cython.set()
623 self.directive_names = {}
625 def check_directive_scope(self, pos, directive, scope):
626 legal_scopes = Options.directive_scopes.get(directive, None)
627 if legal_scopes and scope not in legal_scopes:
628 self.context.nonfatal_error(PostParseError(pos, 'The %s compiler directive '
629 'is not allowed in %s scope' % (directive, scope)))
634 # Set up processing and handle the cython: comments.
635 def visit_ModuleNode(self, node):
636 for key, value in node.directive_comments.items():
637 if not self.check_directive_scope(node.pos, key, 'module'):
638 self.wrong_scope_error(node.pos, key, 'module')
639 del node.directive_comments[key]
641 directives = copy.deepcopy(Options.directive_defaults)
642 directives.update(copy.deepcopy(self.compilation_directive_defaults))
643 directives.update(node.directive_comments)
644 self.directives = directives
645 node.directives = directives
646 self.visitchildren(node)
647 node.cython_module_names = self.cython_module_names
650 # The following four functions track imports and cimports that
651 # begin with "cython"
652 def is_cython_directive(self, name):
653 return (name in Options.directive_types or
654 name in self.special_methods or
655 PyrexTypes.parse_basic_type(name))
657 def visit_CImportStatNode(self, node):
658 if node.module_name == u"cython":
659 self.cython_module_names.add(node.as_name or u"cython")
660 elif node.module_name.startswith(u"cython."):
662 self.directive_names[node.as_name] = node.module_name[7:]
664 self.cython_module_names.add(u"cython")
665 # if this cimport was a compiler directive, we don't
666 # want to leave the cimport node sitting in the tree
670 def visit_FromCImportStatNode(self, node):
671 if (node.module_name == u"cython") or \
672 node.module_name.startswith(u"cython."):
673 submodule = (node.module_name + u".")[7:]
675 for pos, name, as_name, kind in node.imported_names:
676 full_name = submodule + name
677 if self.is_cython_directive(full_name):
680 self.directive_names[as_name] = full_name
682 self.context.nonfatal_error(PostParseError(pos,
683 "Compiler directive imports must be plain imports"))
685 newimp.append((pos, name, as_name, kind))
688 node.imported_names = newimp
691 def visit_FromImportStatNode(self, node):
692 if (node.module.module_name.value == u"cython") or \
693 node.module.module_name.value.startswith(u"cython."):
694 submodule = (node.module.module_name.value + u".")[7:]
696 for name, name_node in node.items:
697 full_name = submodule + name
698 if self.is_cython_directive(full_name):
699 self.directive_names[name_node.name] = full_name
701 newimp.append((name, name_node))
707 def visit_SingleAssignmentNode(self, node):
708 if (isinstance(node.rhs, ExprNodes.ImportNode) and
709 node.rhs.module_name.value == u'cython'):
710 node = Nodes.CImportStatNode(node.pos,
711 module_name = u'cython',
712 as_name = node.lhs.name)
713 self.visit_CImportStatNode(node)
715 self.visitchildren(node)
718 def visit_NameNode(self, node):
719 if node.name in self.cython_module_names:
720 node.is_cython_module = True
722 node.cython_attribute = self.directive_names.get(node.name)
725 def try_to_parse_directives(self, node):
726 # If node is the contents of an directive (in a with statement or
727 # decorator), returns a list of (directivename, value) pairs.
728 # Otherwise, returns None
729 if isinstance(node, ExprNodes.CallNode):
730 self.visit(node.function)
731 optname = node.function.as_cython_attribute()
733 directivetype = Options.directive_types.get(optname)
735 args, kwds = node.explicit_args_kwds()
738 if kwds is not None and directivetype is not dict:
739 for keyvalue in kwds.key_value_pairs:
740 key, value = keyvalue
741 sub_optname = "%s.%s" % (optname, key.value)
742 if Options.directive_types.get(sub_optname):
743 directives.append(self.try_to_parse_directive(sub_optname, [value], None, keyvalue.pos))
745 key_value_pairs.append(keyvalue)
746 if not key_value_pairs:
749 kwds.key_value_pairs = key_value_pairs
750 if directives and not kwds and not args:
752 directives.append(self.try_to_parse_directive(optname, args, kwds, node.function.pos))
754 elif isinstance(node, (ExprNodes.AttributeNode, ExprNodes.NameNode)):
756 optname = node.as_cython_attribute()
758 directivetype = Options.directive_types.get(optname)
759 if directivetype is bool:
760 return [(optname, True)]
761 elif directivetype is None:
762 return [(optname, None)]
764 raise PostParseError(
765 node.pos, "The '%s' directive should be used as a function call." % optname)
768 def try_to_parse_directive(self, optname, args, kwds, pos):
769 directivetype = Options.directive_types.get(optname)
770 if len(args) == 1 and isinstance(args[0], ExprNodes.NoneNode):
771 return optname, Options.directive_defaults[optname]
772 elif directivetype is bool:
773 if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.BoolNode):
774 raise PostParseError(pos,
775 'The %s directive takes one compile-time boolean argument' % optname)
776 return (optname, args[0].value)
777 elif directivetype is str:
778 if kwds is not None or len(args) != 1 or not isinstance(args[0], (ExprNodes.StringNode,
779 ExprNodes.UnicodeNode)):
780 raise PostParseError(pos,
781 'The %s directive takes one compile-time string argument' % optname)
782 return (optname, str(args[0].value))
783 elif directivetype is dict:
785 raise PostParseError(pos,
786 'The %s directive takes no prepositional arguments' % optname)
787 return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
788 elif directivetype is list:
789 if kwds and len(kwds) != 0:
790 raise PostParseError(pos,
791 'The %s directive takes no keyword arguments' % optname)
792 return optname, [ str(arg.value) for arg in args ]
796 def visit_with_directives(self, body, directives):
797 olddirectives = self.directives
798 newdirectives = copy.copy(olddirectives)
799 newdirectives.update(directives)
800 self.directives = newdirectives
801 assert isinstance(body, Nodes.StatListNode), body
802 retbody = self.visit_Node(body)
803 directive = Nodes.CompilerDirectivesNode(pos=retbody.pos, body=retbody,
804 directives=newdirectives)
805 self.directives = olddirectives
809 def visit_FuncDefNode(self, node):
810 directives = self._extract_directives(node, 'function')
812 return self.visit_Node(node)
813 body = Nodes.StatListNode(node.pos, stats=[node])
814 return self.visit_with_directives(body, directives)
816 def visit_CVarDefNode(self, node):
817 if not node.decorators:
819 for dec in node.decorators:
820 for directive in self.try_to_parse_directives(dec.decorator) or ():
821 if directive is not None and directive[0] == u'locals':
822 node.directive_locals = directive[1]
824 self.context.nonfatal_error(PostParseError(dec.pos,
825 "Cdef functions can only take cython.locals() decorator."))
828 def visit_CClassDefNode(self, node):
829 directives = self._extract_directives(node, 'cclass')
831 return self.visit_Node(node)
832 body = Nodes.StatListNode(node.pos, stats=[node])
833 return self.visit_with_directives(body, directives)
835 def visit_PyClassDefNode(self, node):
836 directives = self._extract_directives(node, 'class')
838 return self.visit_Node(node)
839 body = Nodes.StatListNode(node.pos, stats=[node])
840 return self.visit_with_directives(body, directives)
842 def _extract_directives(self, node, scope_name):
843 if not node.decorators:
845 # Split the decorators into two lists -- real decorators and directives
848 for dec in node.decorators:
849 new_directives = self.try_to_parse_directives(dec.decorator)
850 if new_directives is not None:
851 for directive in new_directives:
852 if self.check_directive_scope(node.pos, directive[0], scope_name):
853 directives.append(directive)
856 if realdecs and isinstance(node, (Nodes.CFuncDefNode, Nodes.CClassDefNode)):
857 raise PostParseError(realdecs[0].pos, "Cdef functions/classes cannot take arbitrary decorators.")
859 node.decorators = realdecs
860 # merge or override repeated directives
862 directives.reverse() # Decorators coming first take precedence
863 for directive in directives:
864 name, value = directive
866 old_value = optdict[name]
867 # keywords and arg lists can be merged, everything
868 # else overrides completely
869 if isinstance(old_value, dict):
870 old_value.update(value)
871 elif isinstance(old_value, list):
872 old_value.extend(value)
874 optdict[name] = value
876 optdict[name] = value
879 # Handle with statements
880 def visit_WithStatNode(self, node):
882 for directive in self.try_to_parse_directives(node.manager) or []:
883 if directive is not None:
884 if node.target is not None:
885 self.context.nonfatal_error(
886 PostParseError(node.pos, "Compiler directive with statements cannot contain 'as'"))
888 name, value = directive
890 # special case: in pure mode, "with nogil" spells "with cython.nogil"
891 node = Nodes.GILStatNode(node.pos, state = "nogil", body = node.body)
892 return self.visit_Node(node)
893 if self.check_directive_scope(node.pos, name, 'with statement'):
894 directive_dict[name] = value
896 return self.visit_with_directives(node.body, directive_dict)
897 return self.visit_Node(node)
899 class WithTransform(CythonTransform, SkipDeclarations):
901 # EXCINFO is manually set to a variable that contains
902 # the exc_info() tuple that can be generated by the enclosing except
904 template_without_target = TreeFragment(u"""
915 if not EXIT(*EXCINFO):
919 EXIT(None, None, None)
920 """, temps=[u'MGR', u'EXC', u"EXIT"],
921 pipeline=[NormalizeTree(None)])
923 template_with_target = TreeFragment(u"""
926 VALUE = MGR.__enter__()
935 if not EXIT(*EXCINFO):
939 EXIT(None, None, None)
940 MGR = EXIT = VALUE = EXC = None
942 """, temps=[u'MGR', u'EXC', u"EXIT", u"VALUE"],
943 pipeline=[NormalizeTree(None)])
945 def visit_WithStatNode(self, node):
946 # TODO: Cleanup badly needed
947 TemplateTransform.temp_name_counter += 1
948 handle = "__tmpvar_%d" % TemplateTransform.temp_name_counter
950 self.visitchildren(node, ['body'])
951 excinfo_temp = ExprNodes.NameNode(node.pos, name=handle)#TempHandle(Builtin.tuple_type)
952 if node.target is not None:
953 result = self.template_with_target.substitute({
954 u'EXPR' : node.manager,
956 u'TARGET' : node.target,
957 u'EXCINFO' : excinfo_temp
960 result = self.template_without_target.substitute({
961 u'EXPR' : node.manager,
963 u'EXCINFO' : excinfo_temp
966 # Set except excinfo target to EXCINFO
967 try_except = result.stats[-1].body.stats[-1]
968 try_except.except_clauses[0].excinfo_target = ExprNodes.NameNode(node.pos, name=handle)
969 # excinfo_temp.ref(node.pos))
971 # result.stats[-1].body.stats[-1] = TempsBlockNode(
972 # node.pos, temps=[excinfo_temp], body=try_except)
976 def visit_ExprNode(self, node):
977 # With statements are never inside expressions.
981 class DecoratorTransform(CythonTransform, SkipDeclarations):
983 def visit_DefNode(self, func_node):
984 self.visitchildren(func_node)
985 if not func_node.decorators:
987 return self._handle_decorators(
988 func_node, func_node.name)
990 def visit_CClassDefNode(self, class_node):
991 # This doesn't currently work, so it's disabled.
993 # Problem: assignments to cdef class names do not work. They
994 # would require an additional check anyway, as the extension
995 # type must not change its C type, so decorators cannot
996 # replace an extension type, just alter it and return it.
998 self.visitchildren(class_node)
999 if not class_node.decorators:
1001 error(class_node.pos,
1002 "Decorators not allowed on cdef classes (used on type '%s')" % class_node.class_name)
1004 #return self._handle_decorators(
1005 # class_node, class_node.class_name)
1007 def visit_ClassDefNode(self, class_node):
1008 self.visitchildren(class_node)
1009 if not class_node.decorators:
1011 return self._handle_decorators(
1012 class_node, class_node.name)
1014 def _handle_decorators(self, node, name):
1015 decorator_result = ExprNodes.NameNode(node.pos, name = name)
1016 for decorator in node.decorators[::-1]:
1017 decorator_result = ExprNodes.SimpleCallNode(
1019 function = decorator.decorator,
1020 args = [decorator_result])
1022 name_node = ExprNodes.NameNode(node.pos, name = name)
1023 reassignment = Nodes.SingleAssignmentNode(
1026 rhs = decorator_result)
1027 return [node, reassignment]
1030 class AnalyseDeclarationsTransform(CythonTransform):
1032 basic_property = TreeFragment(u"""
1036 def __set__(self, value):
1038 """, level='c_class')
1039 basic_pyobject_property = TreeFragment(u"""
1043 def __set__(self, value):
1047 """, level='c_class')
1048 basic_property_ro = TreeFragment(u"""
1052 """, level='c_class')
1054 struct_or_union_wrapper = TreeFragment(u"""
1057 def __init__(self, MEMBER=None):
1061 if IS_UNION and count > 1:
1062 raise ValueError, "At most one union member should be specified."
1064 return STR_FORMAT % MEMBER_TUPLE
1066 return REPR_FORMAT % MEMBER_TUPLE
1069 init_assignment = TreeFragment(u"""
1070 if VALUE is not None:
1075 def __call__(self, root):
1076 self.env_stack = [root.scope]
1077 # needed to determine if a cdef var is declared after it's used.
1078 self.seen_vars_stack = []
1079 return super(AnalyseDeclarationsTransform, self).__call__(root)
1081 def visit_NameNode(self, node):
1082 self.seen_vars_stack[-1].add(node.name)
1085 def visit_ModuleNode(self, node):
1086 self.seen_vars_stack.append(cython.set())
1087 node.analyse_declarations(self.env_stack[-1])
1088 self.visitchildren(node)
1089 self.seen_vars_stack.pop()
1092 def visit_LambdaNode(self, node):
1093 node.analyse_declarations(self.env_stack[-1])
1094 self.visitchildren(node)
1097 def visit_ClassDefNode(self, node):
1098 self.env_stack.append(node.scope)
1099 self.visitchildren(node)
1100 self.env_stack.pop()
1103 def visit_CClassDefNode(self, node):
1104 node = self.visit_ClassDefNode(node)
1105 if node.scope and node.scope.implemented:
1107 for entry in node.scope.var_entries:
1108 if entry.needs_property:
1109 property = self.create_Property(entry)
1110 property.analyse_declarations(node.scope)
1111 self.visit(property)
1112 stats.append(property)
1114 node.body.stats += stats
1117 def visit_FuncDefNode(self, node):
1118 self.seen_vars_stack.append(cython.set())
1119 lenv = node.local_scope
1120 node.body.analyse_control_flow(lenv) # this will be totally refactored
1121 node.declare_arguments(lenv)
1122 for var, type_node in node.directive_locals.items():
1123 if not lenv.lookup_here(var): # don't redeclare args
1124 type = type_node.analyse_as_type(lenv)
1126 lenv.declare_var(var, type, type_node.pos)
1128 error(type_node.pos, "Not a type")
1129 node.body.analyse_declarations(lenv)
1130 self.env_stack.append(lenv)
1131 self.visitchildren(node)
1132 self.env_stack.pop()
1133 self.seen_vars_stack.pop()
1136 def visit_ScopedExprNode(self, node):
1137 env = self.env_stack[-1]
1138 node.analyse_declarations(env)
1139 # the node may or may not have a local scope
1140 if node.has_local_scope:
1141 self.seen_vars_stack.append(cython.set(self.seen_vars_stack[-1]))
1142 self.env_stack.append(node.expr_scope)
1143 node.analyse_scoped_declarations(node.expr_scope)
1144 self.visitchildren(node)
1145 self.env_stack.pop()
1146 self.seen_vars_stack.pop()
1148 node.analyse_scoped_declarations(env)
1149 self.visitchildren(node)
1152 def visit_TempResultFromStatNode(self, node):
1153 self.visitchildren(node)
1154 node.analyse_declarations(self.env_stack[-1])
1157 def visit_CStructOrUnionDefNode(self, node):
1158 # Create a wrapper node if needed.
1159 # We want to use the struct type information (so it can't happen
1160 # before this phase) but also create new objects to be declared
1161 # (so it can't happen later).
1162 # Note that we don't return the original node, as it is
1163 # never used after this phase.
1164 if True: # private (default)
1167 self_value = ExprNodes.AttributeNode(
1169 obj = ExprNodes.NameNode(pos=node.pos, name=u"self"),
1170 attribute = EncodedString(u"value"))
1171 var_entries = node.entry.type.scope.var_entries
1173 for entry in var_entries:
1174 attributes.append(ExprNodes.AttributeNode(pos = entry.pos,
1176 attribute = entry.name))
1177 # __init__ assignments
1178 init_assignments = []
1179 for entry, attr in zip(var_entries, attributes):
1180 # TODO: branch on visibility
1181 init_assignments.append(self.init_assignment.substitute({
1182 u"VALUE": ExprNodes.NameNode(entry.pos, name = entry.name),
1184 }, pos = entry.pos))
1187 str_format = u"%s(%s)" % (node.entry.type.name, ("%s, " * len(attributes))[:-2])
1188 wrapper_class = self.struct_or_union_wrapper.substitute({
1189 u"INIT_ASSIGNMENTS": Nodes.StatListNode(node.pos, stats = init_assignments),
1190 u"IS_UNION": ExprNodes.BoolNode(node.pos, value = not node.entry.type.is_struct),
1191 u"MEMBER_TUPLE": ExprNodes.TupleNode(node.pos, args=attributes),
1192 u"STR_FORMAT": ExprNodes.StringNode(node.pos, value = EncodedString(str_format)),
1193 u"REPR_FORMAT": ExprNodes.StringNode(node.pos, value = EncodedString(str_format.replace("%s", "%r"))),
1194 }, pos = node.pos).stats[0]
1195 wrapper_class.class_name = node.name
1196 wrapper_class.shadow = True
1197 class_body = wrapper_class.body.stats
1200 assert isinstance(class_body[0].base_type, Nodes.CSimpleBaseTypeNode)
1201 class_body[0].base_type.name = node.name
1203 # fix __init__ arguments
1204 init_method = class_body[1]
1205 assert isinstance(init_method, Nodes.DefNode) and init_method.name == '__init__'
1206 arg_template = init_method.args[1]
1207 if not node.entry.type.is_struct:
1208 arg_template.kw_only = True
1209 del init_method.args[1]
1210 for entry, attr in zip(var_entries, attributes):
1211 arg = copy.deepcopy(arg_template)
1212 arg.declarator.name = entry.name
1213 init_method.args.append(arg)
1216 for entry, attr in zip(var_entries, attributes):
1217 # TODO: branch on visibility
1218 if entry.type.is_pyobject:
1219 template = self.basic_pyobject_property
1221 template = self.basic_property
1222 property = template.substitute({
1224 }, pos = entry.pos).stats[0]
1225 property.name = entry.name
1226 wrapper_class.body.stats.append(property)
1228 wrapper_class.analyse_declarations(self.env_stack[-1])
1229 return self.visit_CClassDefNode(wrapper_class)
1231 # Some nodes are no longer needed after declaration
1232 # analysis and can be dropped. The analysis was performed
1233 # on these nodes in a seperate recursive process from the
1234 # enclosing function or module, so we can simply drop them.
1235 def visit_CDeclaratorNode(self, node):
1236 # necessary to ensure that all CNameDeclaratorNodes are visited.
1237 self.visitchildren(node)
1240 def visit_CTypeDefNode(self, node):
1243 def visit_CBaseTypeNode(self, node):
1246 def visit_CEnumDefNode(self, node):
1247 if node.visibility == 'public':
1252 def visit_CNameDeclaratorNode(self, node):
1253 if node.name in self.seen_vars_stack[-1]:
1254 entry = self.env_stack[-1].lookup(node.name)
1255 if (entry is None or entry.visibility != 'extern'
1256 and not entry.scope.is_c_class_scope):
1257 warning(node.pos, "cdef variable '%s' declared after it is used" % node.name, 2)
1258 self.visitchildren(node)
1261 def visit_CVarDefNode(self, node):
1262 # to ensure all CNameDeclaratorNodes are visited.
1263 self.visitchildren(node)
1266 def create_Property(self, entry):
1267 if entry.visibility == 'public':
1268 if entry.type.is_pyobject:
1269 template = self.basic_pyobject_property
1271 template = self.basic_property
1272 elif entry.visibility == 'readonly':
1273 template = self.basic_property_ro
1274 property = template.substitute({
1275 u"ATTR": ExprNodes.AttributeNode(pos=entry.pos,
1276 obj=ExprNodes.NameNode(pos=entry.pos, name="self"),
1277 attribute=entry.name),
1278 }, pos=entry.pos).stats[0]
1279 property.name = entry.name
1280 # ---------------------------------------
1281 # XXX This should go to AutoDocTransforms
1282 # ---------------------------------------
1283 if (Options.docstrings and
1284 self.current_directives['embedsignature']):
1285 attr_name = entry.name
1286 type_name = entry.type.declaration_code("", for_display=1)
1288 if not entry.type.is_pyobject:
1289 type_name = "'%s'" % type_name
1290 elif entry.type.is_extension_type:
1291 type_name = entry.type.module_name + '.' + type_name
1292 if entry.init is not None:
1293 default_value = ' = ' + entry.init
1294 elif entry.init_to_none:
1295 default_value = ' = ' + repr(None)
1296 docstring = attr_name + ': ' + type_name + default_value
1297 property.doc = EncodedString(docstring)
1298 # ---------------------------------------
1301 class AnalyseExpressionsTransform(CythonTransform):
1303 def visit_ModuleNode(self, node):
1304 node.scope.infer_types()
1305 node.body.analyse_expressions(node.scope)
1306 self.visitchildren(node)
1309 def visit_FuncDefNode(self, node):
1310 node.local_scope.infer_types()
1311 node.body.analyse_expressions(node.local_scope)
1312 self.visitchildren(node)
1315 def visit_ScopedExprNode(self, node):
1316 if node.has_local_scope:
1317 node.expr_scope.infer_types()
1318 node.analyse_scoped_expressions(node.expr_scope)
1319 self.visitchildren(node)
1322 class ExpandInplaceOperators(EnvTransform):
1324 def visit_InPlaceAssignmentNode(self, node):
1327 if lhs.type.is_cpp_class:
1328 # No getting around this exact operator here.
1330 if isinstance(lhs, ExprNodes.IndexNode) and lhs.is_buffer_access:
1331 # There is code to handle this case.
1334 env = self.current_env()
1335 def side_effect_free_reference(node, setting=False):
1336 if isinstance(node, ExprNodes.NameNode):
1338 elif node.type.is_pyobject and not setting:
1339 node = LetRefNode(node)
1341 elif isinstance(node, ExprNodes.IndexNode):
1342 if node.is_buffer_access:
1343 raise ValueError, "Buffer access"
1344 base, temps = side_effect_free_reference(node.base)
1345 index = LetRefNode(node.index)
1346 return ExprNodes.IndexNode(node.pos, base=base, index=index), temps + [index]
1347 elif isinstance(node, ExprNodes.AttributeNode):
1348 obj, temps = side_effect_free_reference(node.obj)
1349 return ExprNodes.AttributeNode(node.pos, obj=obj, attribute=node.attribute), temps
1351 node = LetRefNode(node)
1354 lhs, let_ref_nodes = side_effect_free_reference(lhs, setting=True)
1357 dup = lhs.__class__(**lhs.__dict__)
1358 binop = ExprNodes.binop_node(node.pos,
1359 operator = node.operator,
1363 # Manually analyse types for new node.
1364 lhs.analyse_target_types(env)
1365 dup.analyse_types(env)
1366 binop.analyse_operation(env)
1367 node = Nodes.SingleAssignmentNode(
1370 rhs=binop.coerce_to(lhs.type, env))
1371 # Use LetRefNode to avoid side effects.
1372 let_ref_nodes.reverse()
1373 for t in let_ref_nodes:
1374 node = LetNode(t, node)
1377 def visit_ExprNode(self, node):
1378 # In-place assignments can't happen within an expression.
1382 class AlignFunctionDefinitions(CythonTransform):
1384 This class takes the signatures from a .pxd file and applies them to
1385 the def methods in a .py file.
1388 def visit_ModuleNode(self, node):
1389 self.scope = node.scope
1390 self.directives = node.directives
1391 self.visitchildren(node)
1394 def visit_PyClassDefNode(self, node):
1395 pxd_def = self.scope.lookup(node.name)
1397 if pxd_def.is_cclass:
1398 return self.visit_CClassDefNode(node.as_cclass(), pxd_def)
1400 error(node.pos, "'%s' redeclared" % node.name)
1402 error(pxd_def.pos, "previous declaration here")
1407 def visit_CClassDefNode(self, node, pxd_def=None):
1409 pxd_def = self.scope.lookup(node.class_name)
1411 outer_scope = self.scope
1412 self.scope = pxd_def.type.scope
1413 self.visitchildren(node)
1415 self.scope = outer_scope
1418 def visit_DefNode(self, node):
1419 pxd_def = self.scope.lookup(node.name)
1420 if pxd_def and (not pxd_def.scope or not pxd_def.scope.is_builtin_scope):
1421 if not pxd_def.is_cfunction:
1422 error(node.pos, "'%s' redeclared" % node.name)
1424 error(pxd_def.pos, "previous declaration here")
1426 node = node.as_cfunction(pxd_def)
1427 elif (self.scope.is_module_scope and self.directives['auto_cpdef']
1428 and node.is_cdef_func_compatible()):
1429 node = node.as_cfunction(scope=self.scope)
1430 # Enable this when nested cdef functions are allowed.
1431 # self.visitchildren(node)
1435 class YieldNodeCollector(TreeVisitor):
1438 super(YieldNodeCollector, self).__init__()
1441 self.has_return_value = False
1443 def visit_Node(self, node):
1444 return self.visitchildren(node)
1446 def visit_YieldExprNode(self, node):
1447 if self.has_return_value:
1448 error(node.pos, "'yield' outside function")
1449 self.yields.append(node)
1450 self.visitchildren(node)
1452 def visit_ReturnStatNode(self, node):
1454 self.has_return_value = True
1456 error(node.pos, "'return' with argument inside generator")
1457 self.returns.append(node)
1459 def visit_ClassDefNode(self, node):
1462 def visit_FuncDefNode(self, node):
1465 def visit_LambdaNode(self, node):
1468 def visit_GeneratorExpressionNode(self, node):
1471 class MarkClosureVisitor(CythonTransform):
1473 def visit_ModuleNode(self, node):
1474 self.needs_closure = False
1475 self.visitchildren(node)
1478 def visit_FuncDefNode(self, node):
1479 self.needs_closure = False
1480 self.visitchildren(node)
1481 node.needs_closure = self.needs_closure
1482 self.needs_closure = True
1484 collector = YieldNodeCollector()
1485 collector.visitchildren(node)
1487 if collector.yields:
1488 for i, yield_expr in enumerate(collector.yields):
1489 yield_expr.label_num = i + 1
1491 gbody = Nodes.GeneratorBodyDefNode(pos=node.pos,
1494 generator = Nodes.GeneratorDefNode(pos=node.pos,
1497 star_arg=node.star_arg,
1498 starstar_arg=node.starstar_arg,
1500 decorators=node.decorators,
1502 lambda_name=node.lambda_name)
1506 def visit_CFuncDefNode(self, node):
1507 self.visit_FuncDefNode(node)
1508 if node.needs_closure:
1509 error(node.pos, "closures inside cdef functions not yet supported")
1512 def visit_LambdaNode(self, node):
1513 self.needs_closure = False
1514 self.visitchildren(node)
1515 node.needs_closure = self.needs_closure
1516 self.needs_closure = True
1519 def visit_ClassDefNode(self, node):
1520 self.visitchildren(node)
1521 self.needs_closure = True
1524 class CreateClosureClasses(CythonTransform):
1525 # Output closure classes in module scope for all functions
1526 # that really need it.
1528 def __init__(self, context):
1529 super(CreateClosureClasses, self).__init__(context)
1531 self.in_lambda = False
1532 self.generator_class = None
1534 def visit_ModuleNode(self, node):
1535 self.module_scope = node.scope
1536 self.visitchildren(node)
1539 def create_generator_class(self, target_module_scope, pos):
1540 if self.generator_class:
1541 return self.generator_class
1542 # XXX: make generator class creation cleaner
1543 entry = target_module_scope.declare_c_class(name='__pyx_Generator',
1544 objstruct_cname='__pyx_Generator_object',
1545 typeobj_cname='__pyx_Generator_type',
1546 pos=pos, defining=True, implementing=True)
1547 klass = entry.type.scope
1548 klass.is_internal = True
1549 klass.directives = {'final': True}
1551 body_type = PyrexTypes.create_typedef_type('generator_body',
1552 PyrexTypes.c_void_ptr_type,
1553 '__pyx_generator_body_t')
1554 klass.declare_var(pos=pos, name='body', cname='body',
1555 type=body_type, is_cdef=True)
1556 klass.declare_var(pos=pos, name='is_running', cname='is_running', type=PyrexTypes.c_int_type,
1558 klass.declare_var(pos=pos, name='resume_label', cname='resume_label', type=PyrexTypes.c_int_type,
1560 klass.declare_var(pos=pos, name='exc_type', cname='exc_type',
1561 type=PyrexTypes.py_object_type, is_cdef=True)
1562 klass.declare_var(pos=pos, name='exc_value', cname='exc_value',
1563 type=PyrexTypes.py_object_type, is_cdef=True)
1564 klass.declare_var(pos=pos, name='exc_traceback', cname='exc_traceback',
1565 type=PyrexTypes.py_object_type, is_cdef=True)
1568 e = klass.declare_pyfunction('send', pos)
1569 e.func_cname = '__Pyx_Generator_Send'
1570 e.signature = TypeSlots.binaryfunc
1572 e = klass.declare_pyfunction('close', pos)
1573 e.func_cname = '__Pyx_Generator_Close'
1574 e.signature = TypeSlots.unaryfunc
1576 e = klass.declare_pyfunction('throw', pos)
1577 e.func_cname = '__Pyx_Generator_Throw'
1578 e.signature = TypeSlots.pyfunction_signature
1580 e = klass.declare_var('__iter__', PyrexTypes.py_object_type, pos, visibility='public')
1581 e.func_cname = 'PyObject_SelfIter'
1583 e = klass.declare_var('__next__', PyrexTypes.py_object_type, pos, visibility='public')
1584 e.func_cname = '__Pyx_Generator_Next'
1586 self.generator_class = entry.type
1587 return self.generator_class
1589 def find_entries_used_in_closures(self, node):
1592 for name, entry in node.local_scope.entries.items():
1593 if entry.from_closure:
1594 from_closure.append((name, entry))
1595 elif entry.in_closure:
1596 in_closure.append((name, entry))
1597 return from_closure, in_closure
1599 def create_class_from_scope(self, node, target_module_scope, inner_node=None):
1600 # skip generator body
1601 if node.is_generator_body:
1603 # move local variables into closure
1604 if node.is_generator:
1605 for entry in node.local_scope.entries.values():
1606 if not entry.from_closure:
1607 entry.in_closure = True
1609 from_closure, in_closure = self.find_entries_used_in_closures(node)
1612 # Now from the begining
1613 node.needs_closure = False
1614 node.needs_outer_scope = False
1616 func_scope = node.local_scope
1617 cscope = node.entry.scope
1618 while cscope.is_py_class_scope or cscope.is_c_class_scope:
1619 cscope = cscope.outer_scope
1621 if not from_closure and (self.path or inner_node):
1624 raise InternalError, "DefNode does not have assignment node"
1625 inner_node = node.assmt.rhs
1626 inner_node.needs_self_code = False
1627 node.needs_outer_scope = False
1630 if node.is_generator:
1631 base_type = self.create_generator_class(target_module_scope, node.pos)
1632 elif not in_closure and not from_closure:
1634 elif not in_closure:
1635 func_scope.is_passthrough = True
1636 func_scope.scope_class = cscope.scope_class
1637 node.needs_outer_scope = True
1640 as_name = '%s_%s' % (target_module_scope.next_id(Naming.closure_class_prefix), node.entry.cname)
1642 entry = target_module_scope.declare_c_class(
1643 name=as_name, pos=node.pos, defining=True,
1644 implementing=True, base_type=base_type)
1646 func_scope.scope_class = entry
1647 class_scope = entry.type.scope
1648 class_scope.is_internal = True
1649 class_scope.directives = {'final': True}
1652 assert cscope.is_closure_scope
1653 class_scope.declare_var(pos=node.pos,
1654 name=Naming.outer_scope_cname,
1655 cname=Naming.outer_scope_cname,
1656 type=cscope.scope_class.type,
1658 node.needs_outer_scope = True
1659 for name, entry in in_closure:
1660 closure_entry = class_scope.declare_var(pos=entry.pos,
1665 if entry.is_declared_generic:
1666 closure_entry.is_declared_generic = 1
1667 node.needs_closure = True
1668 # Do it here because other classes are already checked
1669 target_module_scope.check_c_class(func_scope.scope_class)
1671 def visit_LambdaNode(self, node):
1672 was_in_lambda = self.in_lambda
1673 self.in_lambda = True
1674 self.create_class_from_scope(node.def_node, self.module_scope, node)
1675 self.visitchildren(node)
1676 self.in_lambda = was_in_lambda
1679 def visit_FuncDefNode(self, node):
1681 self.visitchildren(node)
1683 if node.needs_closure or self.path:
1684 self.create_class_from_scope(node, self.module_scope)
1685 self.path.append(node)
1686 self.visitchildren(node)
1691 class GilCheck(VisitorTransform):
1693 Call `node.gil_check(env)` on each node to make sure we hold the
1694 GIL when we need it. Raise an error when on Python operations
1695 inside a `nogil` environment.
1697 def __call__(self, root):
1698 self.env_stack = [root.scope]
1700 return super(GilCheck, self).__call__(root)
1702 def visit_FuncDefNode(self, node):
1703 self.env_stack.append(node.local_scope)
1704 was_nogil = self.nogil
1705 self.nogil = node.local_scope.nogil
1706 if self.nogil and node.nogil_check:
1707 node.nogil_check(node.local_scope)
1708 self.visitchildren(node)
1709 self.env_stack.pop()
1710 self.nogil = was_nogil
1713 def visit_GILStatNode(self, node):
1714 env = self.env_stack[-1]
1715 if self.nogil and node.nogil_check: node.nogil_check()
1716 was_nogil = self.nogil
1717 self.nogil = (node.state == 'nogil')
1718 self.visitchildren(node)
1719 self.nogil = was_nogil
1722 def visit_Node(self, node):
1723 if self.env_stack and self.nogil and node.nogil_check:
1724 node.nogil_check(self.env_stack[-1])
1725 self.visitchildren(node)
1729 class TransformBuiltinMethods(EnvTransform):
1731 def visit_SingleAssignmentNode(self, node):
1732 if node.declaration_only:
1735 self.visitchildren(node)
1738 def visit_AttributeNode(self, node):
1739 self.visitchildren(node)
1740 return self.visit_cython_attribute(node)
1742 def visit_NameNode(self, node):
1743 return self.visit_cython_attribute(node)
1745 def visit_cython_attribute(self, node):
1746 attribute = node.as_cython_attribute()
1748 if attribute == u'compiled':
1749 node = ExprNodes.BoolNode(node.pos, value=True)
1750 elif attribute == u'NULL':
1751 node = ExprNodes.NullNode(node.pos)
1752 elif attribute in (u'set', u'frozenset'):
1753 node = ExprNodes.NameNode(node.pos, name=EncodedString(attribute),
1754 entry=self.current_env().builtin_scope().lookup_here(attribute))
1755 elif not PyrexTypes.parse_basic_type(attribute):
1756 error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute)
1759 def _inject_locals(self, node, func_name):
1760 # locals()/dir() builtins
1761 lenv = self.current_env()
1762 entry = lenv.lookup_here(func_name)
1767 if func_name == 'locals':
1768 if len(node.args) > 0:
1769 error(self.pos, "Builtin 'locals()' called with wrong number of args, expected 0, got %d"
1772 items = [ ExprNodes.DictItemNode(pos,
1773 key=ExprNodes.StringNode(pos, value=var),
1774 value=ExprNodes.NameNode(pos, name=var))
1775 for var in lenv.entries ]
1776 return ExprNodes.DictNode(pos, key_value_pairs=items)
1778 if len(node.args) > 1:
1779 error(self.pos, "Builtin 'dir()' called with wrong number of args, expected 0-1, got %d"
1782 elif len(node.args) == 1:
1783 # optimised in Builtin.py
1785 items = [ ExprNodes.StringNode(pos, value=var) for var in lenv.entries ]
1786 return ExprNodes.ListNode(pos, args=items)
1788 def visit_SimpleCallNode(self, node):
1789 if isinstance(node.function, ExprNodes.NameNode):
1790 func_name = node.function.name
1791 if func_name in ('dir', 'locals'):
1792 return self._inject_locals(node, func_name)
1795 function = node.function.as_cython_attribute()
1797 if function in InterpretCompilerDirectives.unop_method_nodes:
1798 if len(node.args) != 1:
1799 error(node.function.pos, u"%s() takes exactly one argument" % function)
1801 node = InterpretCompilerDirectives.unop_method_nodes[function](node.function.pos, operand=node.args[0])
1802 elif function in InterpretCompilerDirectives.binop_method_nodes:
1803 if len(node.args) != 2:
1804 error(node.function.pos, u"%s() takes exactly two arguments" % function)
1806 node = InterpretCompilerDirectives.binop_method_nodes[function](node.function.pos, operand1=node.args[0], operand2=node.args[1])
1807 elif function == u'cast':
1808 if len(node.args) != 2:
1809 error(node.function.pos, u"cast() takes exactly two arguments")
1811 type = node.args[0].analyse_as_type(self.current_env())
1813 node = ExprNodes.TypecastNode(node.function.pos, type=type, operand=node.args[1])
1815 error(node.args[0].pos, "Not a type")
1816 elif function == u'sizeof':
1817 if len(node.args) != 1:
1818 error(node.function.pos, u"sizeof() takes exactly one argument")
1820 type = node.args[0].analyse_as_type(self.current_env())
1822 node = ExprNodes.SizeofTypeNode(node.function.pos, arg_type=type)
1824 node = ExprNodes.SizeofVarNode(node.function.pos, operand=node.args[0])
1825 elif function == 'cmod':
1826 if len(node.args) != 2:
1827 error(node.function.pos, u"cmod() takes exactly two arguments")
1829 node = ExprNodes.binop_node(node.function.pos, '%', node.args[0], node.args[1])
1830 node.cdivision = True
1831 elif function == 'cdiv':
1832 if len(node.args) != 2:
1833 error(node.function.pos, u"cdiv() takes exactly two arguments")
1835 node = ExprNodes.binop_node(node.function.pos, '/', node.args[0], node.args[1])
1836 node.cdivision = True
1837 elif function == u'set':
1838 node.function = ExprNodes.NameNode(node.pos, name=EncodedString('set'))
1840 error(node.function.pos, u"'%s' not a valid cython language construct" % function)
1842 self.visitchildren(node)
1846 class DebugTransform(CythonTransform):
1848 Create debug information and all functions' visibility to extern in order
1849 to enable debugging.
1852 def __init__(self, context, options, result):
1853 super(DebugTransform, self).__init__(context)
1854 self.visited = cython.set()
1855 # our treebuilder and debug output writer
1856 # (see Cython.Debugger.debug_output.CythonDebugWriter)
1857 self.tb = self.context.gdb_debug_outputwriter
1858 #self.c_output_file = options.output_file
1859 self.c_output_file = result.c_file
1861 # Closure support, basically treat nested functions as if the AST were
1863 self.nested_funcdefs = []
1865 # tells visit_NameNode whether it should register step-into functions
1866 self.register_stepinto = False
1868 def visit_ModuleNode(self, node):
1869 self.tb.module_name = node.full_module_name
1871 module_name=node.full_module_name,
1872 filename=node.pos[0].filename,
1873 c_filename=self.c_output_file)
1875 self.tb.start('Module', attrs)
1877 # serialize functions
1878 self.tb.start('Functions')
1879 # First, serialize functions normally...
1880 self.visitchildren(node)
1882 # ... then, serialize nested functions
1883 for nested_funcdef in self.nested_funcdefs:
1884 self.visit_FuncDefNode(nested_funcdef)
1886 self.register_stepinto = True
1887 self.serialize_modulenode_as_function(node)
1888 self.register_stepinto = False
1889 self.tb.end('Functions')
1891 # 2.3 compatibility. Serialize global variables
1892 self.tb.start('Globals')
1895 for k, v in node.scope.entries.iteritems():
1896 if (v.qualified_name not in self.visited and not
1897 v.name.startswith('__pyx_') and not
1898 v.type.is_cfunction and not
1899 v.type.is_extension_type):
1902 self.serialize_local_variables(entries)
1903 self.tb.end('Globals')
1904 # self.tb.end('Module') # end Module after the line number mapping in
1905 # Cython.Compiler.ModuleNode.ModuleNode._serialize_lineno_map
1908 def visit_FuncDefNode(self, node):
1909 self.visited.add(node.local_scope.qualified_name)
1911 if getattr(node, 'is_wrapper', False):
1914 if self.register_stepinto:
1915 self.nested_funcdefs.append(node)
1918 # node.entry.visibility = 'extern'
1919 if node.py_func is None:
1922 pf_cname = node.py_func.entry.func_cname
1925 name=node.entry.name,
1926 cname=node.entry.func_cname,
1928 qualified_name=node.local_scope.qualified_name,
1929 lineno=str(node.pos[1]))
1931 self.tb.start('Function', attrs=attrs)
1933 self.tb.start('Locals')
1934 self.serialize_local_variables(node.local_scope.entries)
1935 self.tb.end('Locals')
1937 self.tb.start('Arguments')
1938 for arg in node.local_scope.arg_entries:
1939 self.tb.start(arg.name)
1940 self.tb.end(arg.name)
1941 self.tb.end('Arguments')
1943 self.tb.start('StepIntoFunctions')
1944 self.register_stepinto = True
1945 self.visitchildren(node)
1946 self.register_stepinto = False
1947 self.tb.end('StepIntoFunctions')
1948 self.tb.end('Function')
1952 def visit_NameNode(self, node):
1953 if (self.register_stepinto and
1954 node.type.is_cfunction and
1955 getattr(node, 'is_called', False) and
1956 node.entry.func_cname is not None):
1957 # don't check node.entry.in_cinclude, as 'cdef extern: ...'
1958 # declared functions are not 'in_cinclude'.
1959 # This means we will list called 'cdef' functions as
1960 # "step into functions", but this is not an issue as they will be
1961 # recognized as Cython functions anyway.
1962 attrs = dict(name=node.entry.func_cname)
1963 self.tb.start('StepIntoFunction', attrs=attrs)
1964 self.tb.end('StepIntoFunction')
1966 self.visitchildren(node)
1969 def serialize_modulenode_as_function(self, node):
1971 Serialize the module-level code as a function so the debugger will know
1972 it's a "relevant frame" and it will know where to set the breakpoint
1973 for 'break modulename'.
1975 name = node.full_module_name.rpartition('.')[-1]
1977 cname_py2 = 'init' + name
1978 cname_py3 = 'PyInit_' + name
1984 # Ignore the qualified_name, breakpoints should be set using
1985 # `cy break modulename:lineno` for module-level breakpoints.
1988 is_initmodule_function="True",
1991 py3_attrs = dict(py2_attrs, cname=cname_py3)
1993 self._serialize_modulenode_as_function(node, py2_attrs)
1994 self._serialize_modulenode_as_function(node, py3_attrs)
1996 def _serialize_modulenode_as_function(self, node, attrs):
1997 self.tb.start('Function', attrs=attrs)
1999 self.tb.start('Locals')
2000 self.serialize_local_variables(node.scope.entries)
2001 self.tb.end('Locals')
2003 self.tb.start('Arguments')
2004 self.tb.end('Arguments')
2006 self.tb.start('StepIntoFunctions')
2007 self.register_stepinto = True
2008 self.visitchildren(node)
2009 self.register_stepinto = False
2010 self.tb.end('StepIntoFunctions')
2012 self.tb.end('Function')
2014 def serialize_local_variables(self, entries):
2015 for entry in entries.values():
2016 if entry.type.is_pyobject:
2017 vartype = 'PythonObject'
2021 if entry.from_closure:
2022 # We're dealing with a closure where a variable from an outer
2023 # scope is accessed, get it from the scope object.
2024 cname = '%s->%s' % (Naming.cur_scope_cname,
2025 entry.outer_entry.cname)
2027 qname = '%s.%s.%s' % (entry.scope.outer_scope.qualified_name,
2030 elif entry.in_closure:
2031 cname = '%s->%s' % (Naming.cur_scope_cname,
2033 qname = entry.qualified_name
2036 qname = entry.qualified_name
2039 # this happens for variables that are not in the user's code,
2040 # e.g. for the global __builtins__, __doc__, etc. We can just
2041 # set the lineno to 0 for those.
2044 lineno = str(entry.pos[1])
2049 qualified_name=qname,
2053 self.tb.start('LocalVar', attrs)
2054 self.tb.end('LocalVar')