Cython has moved to github.
cython-devel
view Cython/Compiler/ParseTreeTransforms.py @ 2563:e3c343d38eb8
Fix #250, Traceback method name is wrong for exceptions caught in methods
| author | Robert Bradshaw <robertwb@math.washington.edu> |
|---|---|
| date | Thu Oct 22 23:21:40 2009 -0700 (2 years ago) |
| parents | 96e07ab19a48 |
| children | 57fa28d3b2e2 9f839d0cbbf9 |
line source
1 from Cython.Compiler.Visitor import VisitorTransform, CythonTransform, TreeVisitor
2 from Cython.Compiler.ModuleNode import ModuleNode
3 from Cython.Compiler.Nodes import *
4 from Cython.Compiler.ExprNodes import *
5 from Cython.Compiler.UtilNodes import *
6 from Cython.Compiler.TreeFragment import TreeFragment, TemplateTransform
7 from Cython.Compiler.StringEncoding import EncodedString
8 from Cython.Compiler.Errors import error, CompileError
9 try:
10 set
11 except NameError:
12 from sets import Set as set
13 import copy
16 class NameNodeCollector(TreeVisitor):
17 """Collect all NameNodes of a (sub-)tree in the ``name_nodes``
18 attribute.
19 """
20 def __init__(self):
21 super(NameNodeCollector, self).__init__()
22 self.name_nodes = []
24 visit_Node = TreeVisitor.visitchildren
26 def visit_NameNode(self, node):
27 self.name_nodes.append(node)
30 class SkipDeclarations(object):
31 """
32 Variable and function declarations can often have a deep tree structure,
33 and yet most transformations don't need to descend to this depth.
35 Declaration nodes are removed after AnalyseDeclarationsTransform, so there
36 is no need to use this for transformations after that point.
37 """
38 def visit_CTypeDefNode(self, node):
39 return node
41 def visit_CVarDefNode(self, node):
42 return node
44 def visit_CDeclaratorNode(self, node):
45 return node
47 def visit_CBaseTypeNode(self, node):
48 return node
50 def visit_CEnumDefNode(self, node):
51 return node
53 def visit_CStructOrUnionDefNode(self, node):
54 return node
57 class NormalizeTree(CythonTransform):
58 """
59 This transform fixes up a few things after parsing
60 in order to make the parse tree more suitable for
61 transforms.
63 a) After parsing, blocks with only one statement will
64 be represented by that statement, not by a StatListNode.
65 When doing transforms this is annoying and inconsistent,
66 as one cannot in general remove a statement in a consistent
67 way and so on. This transform wraps any single statements
68 in a StatListNode containing a single statement.
70 b) The PassStatNode is a noop and serves no purpose beyond
71 plugging such one-statement blocks; i.e., once parsed a
72 ` "pass" can just as well be represented using an empty
73 StatListNode. This means less special cases to worry about
74 in subsequent transforms (one always checks to see if a
75 StatListNode has no children to see if the block is empty).
76 """
78 def __init__(self, context):
79 super(NormalizeTree, self).__init__(context)
80 self.is_in_statlist = False
81 self.is_in_expr = False
83 def visit_ExprNode(self, node):
84 stacktmp = self.is_in_expr
85 self.is_in_expr = True
86 self.visitchildren(node)
87 self.is_in_expr = stacktmp
88 return node
90 def visit_StatNode(self, node, is_listcontainer=False):
91 stacktmp = self.is_in_statlist
92 self.is_in_statlist = is_listcontainer
93 self.visitchildren(node)
94 self.is_in_statlist = stacktmp
95 if not self.is_in_statlist and not self.is_in_expr:
96 return StatListNode(pos=node.pos, stats=[node])
97 else:
98 return node
100 def visit_StatListNode(self, node):
101 self.is_in_statlist = True
102 self.visitchildren(node)
103 self.is_in_statlist = False
104 return node
106 def visit_ParallelAssignmentNode(self, node):
107 return self.visit_StatNode(node, True)
109 def visit_CEnumDefNode(self, node):
110 return self.visit_StatNode(node, True)
112 def visit_CStructOrUnionDefNode(self, node):
113 return self.visit_StatNode(node, True)
115 # Eliminate PassStatNode
116 def visit_PassStatNode(self, node):
117 if not self.is_in_statlist:
118 return StatListNode(pos=node.pos, stats=[])
119 else:
120 return []
122 def visit_CDeclaratorNode(self, node):
123 return node
126 class PostParseError(CompileError): pass
128 # error strings checked by unit tests, so define them
129 ERR_CDEF_INCLASS = 'Cannot assign default value to fields in cdef classes, structs or unions'
130 ERR_BUF_LOCALONLY = 'Buffer types only allowed as function local variables'
131 ERR_BUF_DEFAULTS = 'Invalid buffer defaults specification (see docs)'
132 ERR_INVALID_SPECIALATTR_TYPE = 'Special attributes must not have a type declared'
133 class PostParse(CythonTransform):
134 """
135 Basic interpretation of the parse tree, as well as validity
136 checking that can be done on a very basic level on the parse
137 tree (while still not being a problem with the basic syntax,
138 as such).
140 Specifically:
141 - Default values to cdef assignments are turned into single
142 assignments following the declaration (everywhere but in class
143 bodies, where they raise a compile error)
145 - Interpret some node structures into Python runtime values.
146 Some nodes take compile-time arguments (currently:
147 CBufferAccessTypeNode[args] and __cythonbufferdefaults__ = {args}),
148 which should be interpreted. This happens in a general way
149 and other steps should be taken to ensure validity.
151 Type arguments cannot be interpreted in this way.
153 - For __cythonbufferdefaults__ the arguments are checked for
154 validity.
156 CBufferAccessTypeNode has its directives interpreted:
157 Any first positional argument goes into the "dtype" attribute,
158 any "ndim" keyword argument goes into the "ndim" attribute and
159 so on. Also it is checked that the directive combination is valid.
160 - __cythonbufferdefaults__ attributes are parsed and put into the
161 type information.
163 Note: Currently Parsing.py does a lot of interpretation and
164 reorganization that can be refactored into this transform
165 if a more pure Abstract Syntax Tree is wanted.
166 """
168 # Track our context.
169 scope_type = None # can be either of 'module', 'function', 'class'
171 def __init__(self, context):
172 super(PostParse, self).__init__(context)
173 self.specialattribute_handlers = {
174 '__cythonbufferdefaults__' : self.handle_bufferdefaults
175 }
177 def visit_ModuleNode(self, node):
178 self.scope_type = 'module'
179 self.scope_node = node
180 self.visitchildren(node)
181 return node
183 def visit_scope(self, node, scope_type):
184 prev = self.scope_type, self.scope_node
185 self.scope_type = scope_type
186 self.scope_node = node
187 self.visitchildren(node)
188 self.scope_type, self.scope_node = prev
189 return node
191 def visit_ClassDefNode(self, node):
192 return self.visit_scope(node, 'class')
194 def visit_FuncDefNode(self, node):
195 return self.visit_scope(node, 'function')
197 def visit_CStructOrUnionDefNode(self, node):
198 return self.visit_scope(node, 'struct')
200 # cdef variables
201 def handle_bufferdefaults(self, decl):
202 if not isinstance(decl.default, DictNode):
203 raise PostParseError(decl.pos, ERR_BUF_DEFAULTS)
204 self.scope_node.buffer_defaults_node = decl.default
205 self.scope_node.buffer_defaults_pos = decl.pos
207 def visit_CVarDefNode(self, node):
208 # This assumes only plain names and pointers are assignable on
209 # declaration. Also, it makes use of the fact that a cdef decl
210 # must appear before the first use, so we don't have to deal with
211 # "i = 3; cdef int i = i" and can simply move the nodes around.
212 try:
213 self.visitchildren(node)
214 stats = [node]
215 newdecls = []
216 for decl in node.declarators:
217 declbase = decl
218 while isinstance(declbase, CPtrDeclaratorNode):
219 declbase = declbase.base
220 if isinstance(declbase, CNameDeclaratorNode):
221 if declbase.default is not None:
222 if self.scope_type in ('class', 'struct'):
223 if isinstance(self.scope_node, CClassDefNode):
224 handler = self.specialattribute_handlers.get(decl.name)
225 if handler:
226 if decl is not declbase:
227 raise PostParseError(decl.pos, ERR_INVALID_SPECIALATTR_TYPE)
228 handler(decl)
229 continue # Remove declaration
230 raise PostParseError(decl.pos, ERR_CDEF_INCLASS)
231 first_assignment = self.scope_type != 'module'
232 stats.append(SingleAssignmentNode(node.pos,
233 lhs=NameNode(node.pos, name=declbase.name),
234 rhs=declbase.default, first=first_assignment))
235 declbase.default = None
236 newdecls.append(decl)
237 node.declarators = newdecls
238 return stats
239 except PostParseError, e:
240 # An error in a cdef clause is ok, simply remove the declaration
241 # and try to move on to report more errors
242 self.context.nonfatal_error(e)
243 return None
245 def visit_CBufferAccessTypeNode(self, node):
246 if not self.scope_type == 'function':
247 raise PostParseError(node.pos, ERR_BUF_LOCALONLY)
248 return node
250 class PxdPostParse(CythonTransform, SkipDeclarations):
251 """
252 Basic interpretation/validity checking that should only be
253 done on pxd trees.
255 A lot of this checking currently happens in the parser; but
256 what is listed below happens here.
258 - "def" functions are let through only if they fill the
259 getbuffer/releasebuffer slots
261 - cdef functions are let through only if they are on the
262 top level and are declared "inline"
263 """
264 ERR_INLINE_ONLY = "function definition in pxd file must be declared 'cdef inline'"
265 ERR_NOGO_WITH_INLINE = "inline function definition in pxd file cannot be '%s'"
267 def __call__(self, node):
268 self.scope_type = 'pxd'
269 return super(PxdPostParse, self).__call__(node)
271 def visit_CClassDefNode(self, node):
272 old = self.scope_type
273 self.scope_type = 'cclass'
274 self.visitchildren(node)
275 self.scope_type = old
276 return node
278 def visit_FuncDefNode(self, node):
279 # FuncDefNode always come with an implementation (without
280 # an imp they are CVarDefNodes..)
281 err = self.ERR_INLINE_ONLY
283 if (isinstance(node, DefNode) and self.scope_type == 'cclass'
284 and node.name in ('__getbuffer__', '__releasebuffer__')):
285 err = None # allow these slots
287 if isinstance(node, CFuncDefNode):
288 if u'inline' in node.modifiers and self.scope_type == 'pxd':
289 node.inline_in_pxd = True
290 if node.visibility != 'private':
291 err = self.ERR_NOGO_WITH_INLINE % node.visibility
292 elif node.api:
293 err = self.ERR_NOGO_WITH_INLINE % 'api'
294 else:
295 err = None # allow inline function
296 else:
297 err = self.ERR_INLINE_ONLY
299 if err:
300 self.context.nonfatal_error(PostParseError(node.pos, err))
301 return None
302 else:
303 return node
305 class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
306 """
307 After parsing, directives can be stored in a number of places:
308 - #cython-comments at the top of the file (stored in ModuleNode)
309 - Command-line arguments overriding these
310 - @cython.directivename decorators
311 - with cython.directivename: statements
313 This transform is responsible for interpreting these various sources
314 and store the directive in two ways:
315 - Set the directives attribute of the ModuleNode for global directives.
316 - Use a CompilerDirectivesNode to override directives for a subtree.
318 (The first one is primarily to not have to modify with the tree
319 structure, so that ModuleNode stay on top.)
321 The directives are stored in dictionaries from name to value in effect.
322 Each such dictionary is always filled in for all possible directives,
323 using default values where no value is given by the user.
325 The available directives are controlled in Options.py.
327 Note that we have to run this prior to analysis, and so some minor
328 duplication of functionality has to occur: We manually track cimports
329 and which names the "cython" module may have been imported to.
330 """
331 special_methods = set(['declare', 'union', 'struct', 'typedef', 'sizeof', 'typeof', 'cast', 'address', 'pointer', 'compiled', 'NULL'])
333 def __init__(self, context, compilation_directive_defaults):
334 super(InterpretCompilerDirectives, self).__init__(context)
335 self.compilation_directive_defaults = {}
336 for key, value in compilation_directive_defaults.iteritems():
337 self.compilation_directive_defaults[unicode(key)] = value
338 self.cython_module_names = set()
339 self.directive_names = {}
341 def check_directive_scope(self, pos, directive, scope):
342 legal_scopes = Options.directive_scopes.get(directive, None)
343 if legal_scopes and scope not in legal_scopes:
344 self.context.nonfatal_error(PostParseError(pos, 'The %s compiler directive '
345 'is not allowed in %s scope' % (directive, scope)))
346 return False
347 else:
348 return True
350 # Set up processing and handle the cython: comments.
351 def visit_ModuleNode(self, node):
352 for key, value in node.directive_comments.iteritems():
353 if not self.check_directive_scope(node.pos, key, 'module'):
354 self.wrong_scope_error(node.pos, key, 'module')
355 del node.directive_comments[key]
357 directives = copy.copy(Options.directive_defaults)
358 directives.update(self.compilation_directive_defaults)
359 directives.update(node.directive_comments)
360 self.directives = directives
361 node.directives = directives
362 self.visitchildren(node)
363 node.cython_module_names = self.cython_module_names
364 return node
366 # Track cimports of the cython module.
367 def visit_CImportStatNode(self, node):
368 if node.module_name == u"cython":
369 if node.as_name:
370 modname = node.as_name
371 else:
372 modname = u"cython"
373 self.cython_module_names.add(modname)
374 return node
376 def visit_FromCImportStatNode(self, node):
377 if node.module_name == u"cython":
378 newimp = []
379 for pos, name, as_name, kind in node.imported_names:
380 if (name in Options.directive_types or
381 name in self.special_methods or
382 PyrexTypes.parse_basic_type(name)):
383 if as_name is None:
384 as_name = name
385 self.directive_names[as_name] = name
386 if kind is not None:
387 self.context.nonfatal_error(PostParseError(pos,
388 "Compiler directive imports must be plain imports"))
389 else:
390 newimp.append((pos, name, as_name, kind))
391 if not newimp:
392 return None
393 node.imported_names = newimp
394 return node
396 def visit_FromImportStatNode(self, node):
397 if node.module.module_name.value == u"cython":
398 newimp = []
399 for name, name_node in node.items:
400 if (name in Options.directive_types or
401 name in self.special_methods or
402 PyrexTypes.parse_basic_type(name)):
403 self.directive_names[name_node.name] = name
404 else:
405 newimp.append((name, name_node))
406 if not newimp:
407 return None
408 node.items = newimp
409 return node
411 def visit_SingleAssignmentNode(self, node):
412 if (isinstance(node.rhs, ImportNode) and
413 node.rhs.module_name.value == u'cython'):
414 node = CImportStatNode(node.pos,
415 module_name = u'cython',
416 as_name = node.lhs.name)
417 self.visit_CImportStatNode(node)
418 else:
419 self.visitchildren(node)
420 return node
422 def visit_NameNode(self, node):
423 if node.name in self.cython_module_names:
424 node.is_cython_module = True
425 else:
426 node.cython_attribute = self.directive_names.get(node.name)
427 return node
429 def try_to_parse_directive(self, node):
430 # If node is the contents of an directive (in a with statement or
431 # decorator), returns (directivename, value).
432 # Otherwise, returns None
433 optname = None
434 if isinstance(node, CallNode):
435 self.visit(node.function)
436 optname = node.function.as_cython_attribute()
438 if optname:
439 directivetype = Options.directive_types.get(optname)
440 if directivetype:
441 args, kwds = node.explicit_args_kwds()
442 if directivetype is bool:
443 if kwds is not None or len(args) != 1 or not isinstance(args[0], BoolNode):
444 raise PostParseError(node.function.pos,
445 'The %s directive takes one compile-time boolean argument' % optname)
446 return (optname, args[0].value)
447 elif directivetype is str:
448 if kwds is not None or len(args) != 1 or not isinstance(args[0], (StringNode, UnicodeNode)):
449 raise PostParseError(node.function.pos,
450 'The %s directive takes one compile-time string argument' % optname)
451 return (optname, str(args[0].value))
452 elif directivetype is dict:
453 if len(args) != 0:
454 raise PostParseError(node.function.pos,
455 'The %s directive takes no prepositional arguments' % optname)
456 return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs])
457 elif directivetype is list:
458 if kwds and len(kwds) != 0:
459 raise PostParseError(node.function.pos,
460 'The %s directive takes no keyword arguments' % optname)
461 return optname, [ str(arg.value) for arg in args ]
462 else:
463 assert False
465 return None
467 def visit_with_directives(self, body, directives):
468 olddirectives = self.directives
469 newdirectives = copy.copy(olddirectives)
470 newdirectives.update(directives)
471 self.directives = newdirectives
472 assert isinstance(body, StatListNode), body
473 retbody = self.visit_Node(body)
474 directive = CompilerDirectivesNode(pos=retbody.pos, body=retbody,
475 directives=newdirectives)
476 self.directives = olddirectives
477 return directive
479 # Handle decorators
480 def visit_FuncDefNode(self, node):
481 directives = []
482 if node.decorators:
483 # Split the decorators into two lists -- real decorators and directives
484 realdecs = []
485 for dec in node.decorators:
486 directive = self.try_to_parse_directive(dec.decorator)
487 if directive is not None:
488 directives.append(directive)
489 else:
490 realdecs.append(dec)
491 if realdecs and isinstance(node, CFuncDefNode):
492 raise PostParseError(realdecs[0].pos, "Cdef functions cannot take arbitrary decorators.")
493 else:
494 node.decorators = realdecs
496 if directives:
497 optdict = {}
498 directives.reverse() # Decorators coming first take precedence
499 for directive in directives:
500 name, value = directive
501 legal_scopes = Options.directive_scopes.get(name, None)
502 if not self.check_directive_scope(node.pos, name, 'function'):
503 continue
504 if name in optdict:
505 old_value = optdict[name]
506 # keywords and arg lists can be merged, everything
507 # else overrides completely
508 if isinstance(old_value, dict):
509 old_value.update(value)
510 elif isinstance(old_value, list):
511 old_value.extend(value)
512 else:
513 optdict[name] = value
514 else:
515 optdict[name] = value
516 body = StatListNode(node.pos, stats=[node])
517 return self.visit_with_directives(body, optdict)
518 else:
519 return self.visit_Node(node)
521 def visit_CVarDefNode(self, node):
522 if node.decorators:
523 for dec in node.decorators:
524 directive = self.try_to_parse_directive(dec.decorator)
525 if directive is not None and directive[0] == u'locals':
526 node.directive_locals = directive[1]
527 else:
528 self.context.nonfatal_error(PostParseError(dec.pos,
529 "Cdef functions can only take cython.locals() decorator."))
530 continue
531 return node
533 # Handle with statements
534 def visit_WithStatNode(self, node):
535 directive = self.try_to_parse_directive(node.manager)
536 if directive is not None:
537 if node.target is not None:
538 self.context.nonfatal_error(
539 PostParseError(node.pos, "Compiler directive with statements cannot contain 'as'"))
540 else:
541 name, value = directive
542 if self.check_directive_scope(node.pos, name, 'with statement'):
543 return self.visit_with_directives(node.body, {name:value})
544 return self.visit_Node(node)
546 class WithTransform(CythonTransform, SkipDeclarations):
548 # EXCINFO is manually set to a variable that contains
549 # the exc_info() tuple that can be generated by the enclosing except
550 # statement.
551 template_without_target = TreeFragment(u"""
552 MGR = EXPR
553 EXIT = MGR.__exit__
554 MGR.__enter__()
555 EXC = True
556 try:
557 try:
558 EXCINFO = None
559 BODY
560 except:
561 EXC = False
562 if not EXIT(*EXCINFO):
563 raise
564 finally:
565 if EXC:
566 EXIT(None, None, None)
567 """, temps=[u'MGR', u'EXC', u"EXIT"],
568 pipeline=[NormalizeTree(None)])
570 template_with_target = TreeFragment(u"""
571 MGR = EXPR
572 EXIT = MGR.__exit__
573 VALUE = MGR.__enter__()
574 EXC = True
575 try:
576 try:
577 EXCINFO = None
578 TARGET = VALUE
579 BODY
580 except:
581 EXC = False
582 if not EXIT(*EXCINFO):
583 raise
584 finally:
585 if EXC:
586 EXIT(None, None, None)
587 MGR = EXIT = VALUE = EXC = None
589 """, temps=[u'MGR', u'EXC', u"EXIT", u"VALUE"],
590 pipeline=[NormalizeTree(None)])
592 def visit_WithStatNode(self, node):
593 # TODO: Cleanup badly needed
594 TemplateTransform.temp_name_counter += 1
595 handle = "__tmpvar_%d" % TemplateTransform.temp_name_counter
597 self.visitchildren(node, ['body'])
598 excinfo_temp = NameNode(node.pos, name=handle)#TempHandle(Builtin.tuple_type)
599 if node.target is not None:
600 result = self.template_with_target.substitute({
601 u'EXPR' : node.manager,
602 u'BODY' : node.body,
603 u'TARGET' : node.target,
604 u'EXCINFO' : excinfo_temp
605 }, pos=node.pos)
606 else:
607 result = self.template_without_target.substitute({
608 u'EXPR' : node.manager,
609 u'BODY' : node.body,
610 u'EXCINFO' : excinfo_temp
611 }, pos=node.pos)
613 # Set except excinfo target to EXCINFO
614 try_except = result.stats[-1].body.stats[-1]
615 try_except.except_clauses[0].excinfo_target = NameNode(node.pos, name=handle)
616 # excinfo_temp.ref(node.pos))
618 # result.stats[-1].body.stats[-1] = TempsBlockNode(
619 # node.pos, temps=[excinfo_temp], body=try_except)
621 return result
623 def visit_ExprNode(self, node):
624 # With statements are never inside expressions.
625 return node
628 class DecoratorTransform(CythonTransform, SkipDeclarations):
630 def visit_DefNode(self, func_node):
631 self.visitchildren(func_node)
632 if not func_node.decorators:
633 return func_node
634 return self._handle_decorators(
635 func_node, func_node.name)
637 def _visit_CClassDefNode(self, class_node):
638 # This doesn't currently work, so it's disabled (also in the
639 # parser).
640 #
641 # Problem: assignments to cdef class names do not work. They
642 # would require an additional check anyway, as the extension
643 # type must not change its C type, so decorators cannot
644 # replace an extension type, just alter it and return it.
646 self.visitchildren(class_node)
647 if not class_node.decorators:
648 return class_node
649 return self._handle_decorators(
650 class_node, class_node.class_name)
652 def visit_ClassDefNode(self, class_node):
653 self.visitchildren(class_node)
654 if not class_node.decorators:
655 return class_node
656 return self._handle_decorators(
657 class_node, class_node.name)
659 def _handle_decorators(self, node, name):
660 decorator_result = NameNode(node.pos, name = name)
661 for decorator in node.decorators[::-1]:
662 decorator_result = SimpleCallNode(
663 decorator.pos,
664 function = decorator.decorator,
665 args = [decorator_result])
667 name_node = NameNode(node.pos, name = name)
668 reassignment = SingleAssignmentNode(
669 node.pos,
670 lhs = name_node,
671 rhs = decorator_result)
672 return [node, reassignment]
675 class AnalyseDeclarationsTransform(CythonTransform):
677 basic_property = TreeFragment(u"""
678 property NAME:
679 def __get__(self):
680 return ATTR
681 def __set__(self, value):
682 ATTR = value
683 """, level='c_class')
685 def __call__(self, root):
686 self.env_stack = [root.scope]
687 # needed to determine if a cdef var is declared after it's used.
688 self.seen_vars_stack = []
689 return super(AnalyseDeclarationsTransform, self).__call__(root)
691 def visit_NameNode(self, node):
692 self.seen_vars_stack[-1].add(node.name)
693 return node
695 def visit_ModuleNode(self, node):
696 self.seen_vars_stack.append(set())
697 node.analyse_declarations(self.env_stack[-1])
698 self.visitchildren(node)
699 self.seen_vars_stack.pop()
700 return node
702 def visit_ClassDefNode(self, node):
703 self.env_stack.append(node.scope)
704 self.visitchildren(node)
705 self.env_stack.pop()
706 return node
708 def visit_FuncDefNode(self, node):
709 self.seen_vars_stack.append(set())
710 lenv = node.create_local_scope(self.env_stack[-1])
711 node.body.analyse_control_flow(lenv) # this will be totally refactored
712 node.declare_arguments(lenv)
713 for var, type_node in node.directive_locals.items():
714 if not lenv.lookup_here(var): # don't redeclare args
715 type = type_node.analyse_as_type(lenv)
716 if type:
717 lenv.declare_var(var, type, type_node.pos)
718 else:
719 error(type_node.pos, "Not a type")
720 node.body.analyse_declarations(lenv)
721 self.env_stack.append(lenv)
722 self.visitchildren(node)
723 self.env_stack.pop()
724 self.seen_vars_stack.pop()
725 return node
727 # Some nodes are no longer needed after declaration
728 # analysis and can be dropped. The analysis was performed
729 # on these nodes in a seperate recursive process from the
730 # enclosing function or module, so we can simply drop them.
731 def visit_CDeclaratorNode(self, node):
732 # necessary to ensure that all CNameDeclaratorNodes are visited.
733 self.visitchildren(node)
734 return node
736 def visit_CTypeDefNode(self, node):
737 return node
739 def visit_CBaseTypeNode(self, node):
740 return None
742 def visit_CEnumDefNode(self, node):
743 if node.visibility == 'public':
744 return node
745 else:
746 return None
748 def visit_CStructOrUnionDefNode(self, node):
749 return None
751 def visit_CNameDeclaratorNode(self, node):
752 if node.name in self.seen_vars_stack[-1]:
753 entry = self.env_stack[-1].lookup(node.name)
754 if entry is None or entry.visibility != 'extern':
755 warning(node.pos, "cdef variable '%s' declared after it is used" % node.name, 2)
756 self.visitchildren(node)
757 return node
759 def visit_CVarDefNode(self, node):
761 # to ensure all CNameDeclaratorNodes are visited.
762 self.visitchildren(node)
764 if node.need_properties:
765 # cdef public attributes may need type testing on
766 # assignment, so we create a property accesss
767 # mechanism for them.
768 stats = []
769 for entry in node.need_properties:
770 property = self.create_Property(entry)
771 property.analyse_declarations(node.dest_scope)
772 self.visit(property)
773 stats.append(property)
774 return StatListNode(pos=node.pos, stats=stats)
775 else:
776 return None
778 def create_Property(self, entry):
779 template = self.basic_property
780 property = template.substitute({
781 u"ATTR": AttributeNode(pos=entry.pos,
782 obj=NameNode(pos=entry.pos, name="self"),
783 attribute=entry.name),
784 }, pos=entry.pos).stats[0]
785 property.name = entry.name
786 return property
788 class AnalyseExpressionsTransform(CythonTransform):
790 def visit_ModuleNode(self, node):
791 node.scope.infer_types()
792 node.body.analyse_expressions(node.scope)
793 self.visitchildren(node)
794 return node
796 def visit_FuncDefNode(self, node):
797 node.local_scope.infer_types()
798 node.body.analyse_expressions(node.local_scope)
799 self.visitchildren(node)
800 return node
802 class AlignFunctionDefinitions(CythonTransform):
803 """
804 This class takes the signatures from a .pxd file and applies them to
805 the def methods in a .py file.
806 """
808 def visit_ModuleNode(self, node):
809 self.scope = node.scope
810 self.directives = node.directives
811 self.visitchildren(node)
812 return node
814 def visit_PyClassDefNode(self, node):
815 pxd_def = self.scope.lookup(node.name)
816 if pxd_def:
817 if pxd_def.is_cclass:
818 return self.visit_CClassDefNode(node.as_cclass(), pxd_def)
819 else:
820 error(node.pos, "'%s' redeclared" % node.name)
821 error(pxd_def.pos, "previous declaration here")
822 return None
823 else:
824 return node
826 def visit_CClassDefNode(self, node, pxd_def=None):
827 if pxd_def is None:
828 pxd_def = self.scope.lookup(node.class_name)
829 if pxd_def:
830 outer_scope = self.scope
831 self.scope = pxd_def.type.scope
832 self.visitchildren(node)
833 if pxd_def:
834 self.scope = outer_scope
835 return node
837 def visit_DefNode(self, node):
838 pxd_def = self.scope.lookup(node.name)
839 if pxd_def:
840 if self.scope.is_c_class_scope and len(pxd_def.type.args) > 0:
841 # The self parameter type needs adjusting.
842 pxd_def.type.args[0].type = self.scope.parent_type
843 if pxd_def.is_cfunction:
844 node = node.as_cfunction(pxd_def)
845 else:
846 error(node.pos, "'%s' redeclared" % node.name)
847 error(pxd_def.pos, "previous declaration here")
848 return None
849 elif self.scope.is_module_scope and self.directives['auto_cpdef']:
850 node = node.as_cfunction(scope=self.scope)
851 # Enable this when internal def functions are allowed.
852 # self.visitchildren(node)
853 return node
856 class MarkClosureVisitor(CythonTransform):
858 needs_closure = False
860 def visit_FuncDefNode(self, node):
861 self.needs_closure = False
862 self.visitchildren(node)
863 node.needs_closure = self.needs_closure
864 self.needs_closure = True
865 return node
867 def visit_ClassDefNode(self, node):
868 self.visitchildren(node)
869 self.needs_closure = True
870 return node
872 def visit_YieldNode(self, node):
873 self.needs_closure = True
875 class CreateClosureClasses(CythonTransform):
876 # Output closure classes in module scope for all functions
877 # that need it.
879 def visit_ModuleNode(self, node):
880 self.module_scope = node.scope
881 self.visitchildren(node)
882 return node
884 def create_class_from_scope(self, node, target_module_scope):
885 as_name = temp_name_handle("closure")
886 func_scope = node.local_scope
888 entry = target_module_scope.declare_c_class(name = as_name,
889 pos = node.pos, defining = True, implementing = True)
890 class_scope = entry.type.scope
891 for entry in func_scope.entries.values():
892 class_scope.declare_var(pos=node.pos,
893 name=entry.name,
894 cname=entry.cname,
895 type=entry.type,
896 is_cdef=True)
898 def visit_FuncDefNode(self, node):
899 self.create_class_from_scope(node, self.module_scope)
900 return node
903 class GilCheck(VisitorTransform):
904 """
905 Call `node.gil_check(env)` on each node to make sure we hold the
906 GIL when we need it. Raise an error when on Python operations
907 inside a `nogil` environment.
908 """
909 def __call__(self, root):
910 self.env_stack = [root.scope]
911 self.nogil = False
912 return super(GilCheck, self).__call__(root)
914 def visit_FuncDefNode(self, node):
915 self.env_stack.append(node.local_scope)
916 was_nogil = self.nogil
917 self.nogil = node.local_scope.nogil
918 if self.nogil and node.nogil_check:
919 node.nogil_check(node.local_scope)
920 self.visitchildren(node)
921 self.env_stack.pop()
922 self.nogil = was_nogil
923 return node
925 def visit_GILStatNode(self, node):
926 env = self.env_stack[-1]
927 if self.nogil and node.nogil_check: node.nogil_check()
928 was_nogil = self.nogil
929 self.nogil = (node.state == 'nogil')
930 self.visitchildren(node)
931 self.nogil = was_nogil
932 return node
934 def visit_Node(self, node):
935 if self.env_stack and self.nogil and node.nogil_check:
936 node.nogil_check(self.env_stack[-1])
937 self.visitchildren(node)
938 return node
941 class EnvTransform(CythonTransform):
942 """
943 This transformation keeps a stack of the environments.
944 """
945 def __call__(self, root):
946 self.env_stack = [root.scope]
947 return super(EnvTransform, self).__call__(root)
949 def visit_FuncDefNode(self, node):
950 self.env_stack.append(node.local_scope)
951 self.visitchildren(node)
952 self.env_stack.pop()
953 return node
956 class TransformBuiltinMethods(EnvTransform):
958 def visit_SingleAssignmentNode(self, node):
959 if node.declaration_only:
960 return None
961 else:
962 self.visitchildren(node)
963 return node
965 def visit_AttributeNode(self, node):
966 self.visitchildren(node)
967 return self.visit_cython_attribute(node)
969 def visit_NameNode(self, node):
970 return self.visit_cython_attribute(node)
972 def visit_cython_attribute(self, node):
973 attribute = node.as_cython_attribute()
974 if attribute:
975 if attribute == u'compiled':
976 node = BoolNode(node.pos, value=True)
977 elif attribute == u'NULL':
978 node = NullNode(node.pos)
979 elif not PyrexTypes.parse_basic_type(attribute):
980 error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute)
981 return node
983 def visit_SimpleCallNode(self, node):
985 # locals builtin
986 if isinstance(node.function, ExprNodes.NameNode):
987 if node.function.name == 'locals':
988 lenv = self.env_stack[-1]
989 entry = lenv.lookup_here('locals')
990 if entry:
991 # not the builtin 'locals'
992 return node
993 if len(node.args) > 0:
994 error(self.pos, "Builtin 'locals()' called with wrong number of args, expected 0, got %d" % len(node.args))
995 return node
996 pos = node.pos
997 items = [ExprNodes.DictItemNode(pos,
998 key=ExprNodes.StringNode(pos, value=var),
999 value=ExprNodes.NameNode(pos, name=var)) for var in lenv.entries]
1000 return ExprNodes.DictNode(pos, key_value_pairs=items)
1002 # cython.foo
1003 function = node.function.as_cython_attribute()
1004 if function:
1005 if function == u'cast':
1006 if len(node.args) != 2:
1007 error(node.function.pos, u"cast takes exactly two arguments")
1008 else:
1009 type = node.args[0].analyse_as_type(self.env_stack[-1])
1010 if type:
1011 node = TypecastNode(node.function.pos, type=type, operand=node.args[1])
1012 else:
1013 error(node.args[0].pos, "Not a type")
1014 elif function == u'sizeof':
1015 if len(node.args) != 1:
1016 error(node.function.pos, u"sizeof takes exactly one argument" % function)
1017 else:
1018 type = node.args[0].analyse_as_type(self.env_stack[-1])
1019 if type:
1020 node = SizeofTypeNode(node.function.pos, arg_type=type)
1021 else:
1022 node = SizeofVarNode(node.function.pos, operand=node.args[0])
1023 elif function == 'typeof':
1024 if len(node.args) != 1:
1025 error(node.function.pos, u"sizeof takes exactly one argument" % function)
1026 else:
1027 node = TypeofNode(node.function.pos, operand=node.args[0])
1028 elif function == 'address':
1029 if len(node.args) != 1:
1030 error(node.function.pos, u"sizeof takes exactly one argument" % function)
1031 else:
1032 node = AmpersandNode(node.function.pos, operand=node.args[0])
1033 elif function == 'cmod':
1034 if len(node.args) != 2:
1035 error(node.function.pos, u"cmod takes exactly one argument" % function)
1036 else:
1037 node = binop_node(node.function.pos, '%', node.args[0], node.args[1])
1038 node.cdivision = True
1039 elif function == 'cdiv':
1040 if len(node.args) != 2:
1041 error(node.function.pos, u"cmod takes exactly one argument" % function)
1042 else:
1043 node = binop_node(node.function.pos, '/', node.args[0], node.args[1])
1044 node.cdivision = True
1045 else:
1046 error(node.function.pos, u"'%s' not a valid cython language construct" % function)
1048 self.visitchildren(node)
1049 return node
