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