1# cython: language_level=3str 2 3from __future__ import absolute_import 4 5import cython 6cython.declare(PyrexTypes=object, Naming=object, ExprNodes=object, Nodes=object, 7 Options=object, UtilNodes=object, LetNode=object, 8 LetRefNode=object, TreeFragment=object, EncodedString=object, 9 error=object, warning=object, copy=object, _unicode=object) 10 11import copy 12import hashlib 13 14from . import PyrexTypes 15from . import Naming 16from . import ExprNodes 17from . import Nodes 18from . import Options 19from . import Builtin 20from . import Errors 21 22from .Visitor import VisitorTransform, TreeVisitor 23from .Visitor import CythonTransform, EnvTransform, ScopeTrackingTransform 24from .UtilNodes import LetNode, LetRefNode 25from .TreeFragment import TreeFragment 26from .StringEncoding import EncodedString, _unicode 27from .Errors import error, warning, CompileError, InternalError 28from .Code import UtilityCode 29 30 31class 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. 35 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 41 42 def visit_CVarDefNode(self, node): 43 return node 44 45 def visit_CDeclaratorNode(self, node): 46 return node 47 48 def visit_CBaseTypeNode(self, node): 49 return node 50 51 def visit_CEnumDefNode(self, node): 52 return node 53 54 def visit_CStructOrUnionDefNode(self, node): 55 return node 56 57 def visit_CppClassNode(self, node): 58 if node.visibility != "extern": 59 # Need to traverse methods. 60 self.visitchildren(node) 61 return node 62 63 64class NormalizeTree(CythonTransform): 65 """ 66 This transform fixes up a few things after parsing 67 in order to make the parse tree more suitable for 68 transforms. 69 70 a) After parsing, blocks with only one statement will 71 be represented by that statement, not by a StatListNode. 72 When doing transforms this is annoying and inconsistent, 73 as one cannot in general remove a statement in a consistent 74 way and so on. This transform wraps any single statements 75 in a StatListNode containing a single statement. 76 77 b) The PassStatNode is a noop and serves no purpose beyond 78 plugging such one-statement blocks; i.e., once parsed a 79` "pass" can just as well be represented using an empty 80 StatListNode. This means less special cases to worry about 81 in subsequent transforms (one always checks to see if a 82 StatListNode has no children to see if the block is empty). 83 """ 84 85 def __init__(self, context): 86 super(NormalizeTree, self).__init__(context) 87 self.is_in_statlist = False 88 self.is_in_expr = False 89 90 def visit_ModuleNode(self, node): 91 self.visitchildren(node) 92 if not isinstance(node.body, Nodes.StatListNode): 93 # This can happen when the body only consists of a single (unused) declaration and no statements. 94 node.body = Nodes.StatListNode(pos=node.pos, stats=[node.body]) 95 return node 96 97 def visit_ExprNode(self, node): 98 stacktmp = self.is_in_expr 99 self.is_in_expr = True 100 self.visitchildren(node) 101 self.is_in_expr = stacktmp 102 return node 103 104 def visit_StatNode(self, node, is_listcontainer=False): 105 stacktmp = self.is_in_statlist 106 self.is_in_statlist = is_listcontainer 107 self.visitchildren(node) 108 self.is_in_statlist = stacktmp 109 if not self.is_in_statlist and not self.is_in_expr: 110 return Nodes.StatListNode(pos=node.pos, stats=[node]) 111 else: 112 return node 113 114 def visit_StatListNode(self, node): 115 self.is_in_statlist = True 116 self.visitchildren(node) 117 self.is_in_statlist = False 118 return node 119 120 def visit_ParallelAssignmentNode(self, node): 121 return self.visit_StatNode(node, True) 122 123 def visit_CEnumDefNode(self, node): 124 return self.visit_StatNode(node, True) 125 126 def visit_CStructOrUnionDefNode(self, node): 127 return self.visit_StatNode(node, True) 128 129 def visit_PassStatNode(self, node): 130 """Eliminate PassStatNode""" 131 if not self.is_in_statlist: 132 return Nodes.StatListNode(pos=node.pos, stats=[]) 133 else: 134 return [] 135 136 def visit_ExprStatNode(self, node): 137 """Eliminate useless string literals""" 138 if node.expr.is_string_literal: 139 return self.visit_PassStatNode(node) 140 else: 141 return self.visit_StatNode(node) 142 143 def visit_CDeclaratorNode(self, node): 144 return node 145 146 147class PostParseError(CompileError): pass 148 149# error strings checked by unit tests, so define them 150ERR_CDEF_INCLASS = 'Cannot assign default value to fields in cdef classes, structs or unions' 151ERR_BUF_DEFAULTS = 'Invalid buffer defaults specification (see docs)' 152ERR_INVALID_SPECIALATTR_TYPE = 'Special attributes must not have a type declared' 153class PostParse(ScopeTrackingTransform): 154 """ 155 Basic interpretation of the parse tree, as well as validity 156 checking that can be done on a very basic level on the parse 157 tree (while still not being a problem with the basic syntax, 158 as such). 159 160 Specifically: 161 - Default values to cdef assignments are turned into single 162 assignments following the declaration (everywhere but in class 163 bodies, where they raise a compile error) 164 165 - Interpret some node structures into Python runtime values. 166 Some nodes take compile-time arguments (currently: 167 TemplatedTypeNode[args] and __cythonbufferdefaults__ = {args}), 168 which should be interpreted. This happens in a general way 169 and other steps should be taken to ensure validity. 170 171 Type arguments cannot be interpreted in this way. 172 173 - For __cythonbufferdefaults__ the arguments are checked for 174 validity. 175 176 TemplatedTypeNode has its directives interpreted: 177 Any first positional argument goes into the "dtype" attribute, 178 any "ndim" keyword argument goes into the "ndim" attribute and 179 so on. Also it is checked that the directive combination is valid. 180 - __cythonbufferdefaults__ attributes are parsed and put into the 181 type information. 182 183 Note: Currently Parsing.py does a lot of interpretation and 184 reorganization that can be refactored into this transform 185 if a more pure Abstract Syntax Tree is wanted. 186 """ 187 def __init__(self, context): 188 super(PostParse, self).__init__(context) 189 self.specialattribute_handlers = { 190 '__cythonbufferdefaults__' : self.handle_bufferdefaults 191 } 192 193 def visit_LambdaNode(self, node): 194 # unpack a lambda expression into the corresponding DefNode 195 collector = YieldNodeCollector() 196 collector.visitchildren(node.result_expr) 197 if collector.has_yield or collector.has_await or isinstance(node.result_expr, ExprNodes.YieldExprNode): 198 body = Nodes.ExprStatNode( 199 node.result_expr.pos, expr=node.result_expr) 200 else: 201 body = Nodes.ReturnStatNode( 202 node.result_expr.pos, value=node.result_expr) 203 node.def_node = Nodes.DefNode( 204 node.pos, name=node.name, 205 args=node.args, star_arg=node.star_arg, 206 starstar_arg=node.starstar_arg, 207 body=body, doc=None) 208 self.visitchildren(node) 209 return node 210 211 def visit_GeneratorExpressionNode(self, node): 212 # unpack a generator expression into the corresponding DefNode 213 collector = YieldNodeCollector() 214 collector.visitchildren(node.loop) 215 node.def_node = Nodes.DefNode( 216 node.pos, name=node.name, doc=None, 217 args=[], star_arg=None, starstar_arg=None, 218 body=node.loop, is_async_def=collector.has_await) 219 self.visitchildren(node) 220 return node 221 222 def visit_ComprehensionNode(self, node): 223 # enforce local scope also in Py2 for async generators (seriously, that's a Py3.6 feature...) 224 if not node.has_local_scope: 225 collector = YieldNodeCollector() 226 collector.visitchildren(node.loop) 227 if collector.has_await: 228 node.has_local_scope = True 229 self.visitchildren(node) 230 return node 231 232 # cdef variables 233 def handle_bufferdefaults(self, decl): 234 if not isinstance(decl.default, ExprNodes.DictNode): 235 raise PostParseError(decl.pos, ERR_BUF_DEFAULTS) 236 self.scope_node.buffer_defaults_node = decl.default 237 self.scope_node.buffer_defaults_pos = decl.pos 238 239 def visit_CVarDefNode(self, node): 240 # This assumes only plain names and pointers are assignable on 241 # declaration. Also, it makes use of the fact that a cdef decl 242 # must appear before the first use, so we don't have to deal with 243 # "i = 3; cdef int i = i" and can simply move the nodes around. 244 try: 245 self.visitchildren(node) 246 stats = [node] 247 newdecls = [] 248 for decl in node.declarators: 249 declbase = decl 250 while isinstance(declbase, Nodes.CPtrDeclaratorNode): 251 declbase = declbase.base 252 if isinstance(declbase, Nodes.CNameDeclaratorNode): 253 if declbase.default is not None: 254 if self.scope_type in ('cclass', 'pyclass', 'struct'): 255 if isinstance(self.scope_node, Nodes.CClassDefNode): 256 handler = self.specialattribute_handlers.get(decl.name) 257 if handler: 258 if decl is not declbase: 259 raise PostParseError(decl.pos, ERR_INVALID_SPECIALATTR_TYPE) 260 handler(decl) 261 continue # Remove declaration 262 raise PostParseError(decl.pos, ERR_CDEF_INCLASS) 263 first_assignment = self.scope_type != 'module' 264 stats.append(Nodes.SingleAssignmentNode(node.pos, 265 lhs=ExprNodes.NameNode(node.pos, name=declbase.name), 266 rhs=declbase.default, first=first_assignment)) 267 declbase.default = None 268 newdecls.append(decl) 269 node.declarators = newdecls 270 return stats 271 except PostParseError as e: 272 # An error in a cdef clause is ok, simply remove the declaration 273 # and try to move on to report more errors 274 self.context.nonfatal_error(e) 275 return None 276 277 # Split parallel assignments (a,b = b,a) into separate partial 278 # assignments that are executed rhs-first using temps. This 279 # restructuring must be applied before type analysis so that known 280 # types on rhs and lhs can be matched directly. It is required in 281 # the case that the types cannot be coerced to a Python type in 282 # order to assign from a tuple. 283 284 def visit_SingleAssignmentNode(self, node): 285 self.visitchildren(node) 286 return self._visit_assignment_node(node, [node.lhs, node.rhs]) 287 288 def visit_CascadedAssignmentNode(self, node): 289 self.visitchildren(node) 290 return self._visit_assignment_node(node, node.lhs_list + [node.rhs]) 291 292 def _visit_assignment_node(self, node, expr_list): 293 """Flatten parallel assignments into separate single 294 assignments or cascaded assignments. 295 """ 296 if sum([ 1 for expr in expr_list 297 if expr.is_sequence_constructor or expr.is_string_literal ]) < 2: 298 # no parallel assignments => nothing to do 299 return node 300 301 expr_list_list = [] 302 flatten_parallel_assignments(expr_list, expr_list_list) 303 temp_refs = [] 304 eliminate_rhs_duplicates(expr_list_list, temp_refs) 305 306 nodes = [] 307 for expr_list in expr_list_list: 308 lhs_list = expr_list[:-1] 309 rhs = expr_list[-1] 310 if len(lhs_list) == 1: 311 node = Nodes.SingleAssignmentNode(rhs.pos, 312 lhs = lhs_list[0], rhs = rhs) 313 else: 314 node = Nodes.CascadedAssignmentNode(rhs.pos, 315 lhs_list = lhs_list, rhs = rhs) 316 nodes.append(node) 317 318 if len(nodes) == 1: 319 assign_node = nodes[0] 320 else: 321 assign_node = Nodes.ParallelAssignmentNode(nodes[0].pos, stats = nodes) 322 323 if temp_refs: 324 duplicates_and_temps = [ (temp.expression, temp) 325 for temp in temp_refs ] 326 sort_common_subsequences(duplicates_and_temps) 327 for _, temp_ref in duplicates_and_temps[::-1]: 328 assign_node = LetNode(temp_ref, assign_node) 329 330 return assign_node 331 332 def _flatten_sequence(self, seq, result): 333 for arg in seq.args: 334 if arg.is_sequence_constructor: 335 self._flatten_sequence(arg, result) 336 else: 337 result.append(arg) 338 return result 339 340 def visit_DelStatNode(self, node): 341 self.visitchildren(node) 342 node.args = self._flatten_sequence(node, []) 343 return node 344 345 def visit_ExceptClauseNode(self, node): 346 if node.is_except_as: 347 # except-as must delete NameNode target at the end 348 del_target = Nodes.DelStatNode( 349 node.pos, 350 args=[ExprNodes.NameNode( 351 node.target.pos, name=node.target.name)], 352 ignore_nonexisting=True) 353 node.body = Nodes.StatListNode( 354 node.pos, 355 stats=[Nodes.TryFinallyStatNode( 356 node.pos, 357 body=node.body, 358 finally_clause=Nodes.StatListNode( 359 node.pos, 360 stats=[del_target]))]) 361 self.visitchildren(node) 362 return node 363 364 def visit_AssertStatNode(self, node): 365 """Extract the exception raising into a RaiseStatNode to simplify GIL handling. 366 """ 367 if node.exception is None: 368 node.exception = Nodes.RaiseStatNode( 369 node.pos, 370 exc_type=ExprNodes.NameNode(node.pos, name=EncodedString("AssertionError")), 371 exc_value=node.value, 372 exc_tb=None, 373 cause=None, 374 builtin_exc_name="AssertionError", 375 wrap_tuple_value=True, 376 ) 377 node.value = None 378 self.visitchildren(node) 379 return node 380 381 382def eliminate_rhs_duplicates(expr_list_list, ref_node_sequence): 383 """Replace rhs items by LetRefNodes if they appear more than once. 384 Creates a sequence of LetRefNodes that set up the required temps 385 and appends them to ref_node_sequence. The input list is modified 386 in-place. 387 """ 388 seen_nodes = set() 389 ref_nodes = {} 390 def find_duplicates(node): 391 if node.is_literal or node.is_name: 392 # no need to replace those; can't include attributes here 393 # as their access is not necessarily side-effect free 394 return 395 if node in seen_nodes: 396 if node not in ref_nodes: 397 ref_node = LetRefNode(node) 398 ref_nodes[node] = ref_node 399 ref_node_sequence.append(ref_node) 400 else: 401 seen_nodes.add(node) 402 if node.is_sequence_constructor: 403 for item in node.args: 404 find_duplicates(item) 405 406 for expr_list in expr_list_list: 407 rhs = expr_list[-1] 408 find_duplicates(rhs) 409 if not ref_nodes: 410 return 411 412 def substitute_nodes(node): 413 if node in ref_nodes: 414 return ref_nodes[node] 415 elif node.is_sequence_constructor: 416 node.args = list(map(substitute_nodes, node.args)) 417 return node 418 419 # replace nodes inside of the common subexpressions 420 for node in ref_nodes: 421 if node.is_sequence_constructor: 422 node.args = list(map(substitute_nodes, node.args)) 423 424 # replace common subexpressions on all rhs items 425 for expr_list in expr_list_list: 426 expr_list[-1] = substitute_nodes(expr_list[-1]) 427 428def sort_common_subsequences(items): 429 """Sort items/subsequences so that all items and subsequences that 430 an item contains appear before the item itself. This is needed 431 because each rhs item must only be evaluated once, so its value 432 must be evaluated first and then reused when packing sequences 433 that contain it. 434 435 This implies a partial order, and the sort must be stable to 436 preserve the original order as much as possible, so we use a 437 simple insertion sort (which is very fast for short sequences, the 438 normal case in practice). 439 """ 440 def contains(seq, x): 441 for item in seq: 442 if item is x: 443 return True 444 elif item.is_sequence_constructor and contains(item.args, x): 445 return True 446 return False 447 def lower_than(a,b): 448 return b.is_sequence_constructor and contains(b.args, a) 449 450 for pos, item in enumerate(items): 451 key = item[1] # the ResultRefNode which has already been injected into the sequences 452 new_pos = pos 453 for i in range(pos-1, -1, -1): 454 if lower_than(key, items[i][0]): 455 new_pos = i 456 if new_pos != pos: 457 for i in range(pos, new_pos, -1): 458 items[i] = items[i-1] 459 items[new_pos] = item 460 461def unpack_string_to_character_literals(literal): 462 chars = [] 463 pos = literal.pos 464 stype = literal.__class__ 465 sval = literal.value 466 sval_type = sval.__class__ 467 for char in sval: 468 cval = sval_type(char) 469 chars.append(stype(pos, value=cval, constant_result=cval)) 470 return chars 471 472def flatten_parallel_assignments(input, output): 473 # The input is a list of expression nodes, representing the LHSs 474 # and RHS of one (possibly cascaded) assignment statement. For 475 # sequence constructors, rearranges the matching parts of both 476 # sides into a list of equivalent assignments between the 477 # individual elements. This transformation is applied 478 # recursively, so that nested structures get matched as well. 479 rhs = input[-1] 480 if (not (rhs.is_sequence_constructor or isinstance(rhs, ExprNodes.UnicodeNode)) 481 or not sum([lhs.is_sequence_constructor for lhs in input[:-1]])): 482 output.append(input) 483 return 484 485 complete_assignments = [] 486 487 if rhs.is_sequence_constructor: 488 rhs_args = rhs.args 489 elif rhs.is_string_literal: 490 rhs_args = unpack_string_to_character_literals(rhs) 491 492 rhs_size = len(rhs_args) 493 lhs_targets = [[] for _ in range(rhs_size)] 494 starred_assignments = [] 495 for lhs in input[:-1]: 496 if not lhs.is_sequence_constructor: 497 if lhs.is_starred: 498 error(lhs.pos, "starred assignment target must be in a list or tuple") 499 complete_assignments.append(lhs) 500 continue 501 lhs_size = len(lhs.args) 502 starred_targets = sum([1 for expr in lhs.args if expr.is_starred]) 503 if starred_targets > 1: 504 error(lhs.pos, "more than 1 starred expression in assignment") 505 output.append([lhs,rhs]) 506 continue 507 elif lhs_size - starred_targets > rhs_size: 508 error(lhs.pos, "need more than %d value%s to unpack" 509 % (rhs_size, (rhs_size != 1) and 's' or '')) 510 output.append([lhs,rhs]) 511 continue 512 elif starred_targets: 513 map_starred_assignment(lhs_targets, starred_assignments, 514 lhs.args, rhs_args) 515 elif lhs_size < rhs_size: 516 error(lhs.pos, "too many values to unpack (expected %d, got %d)" 517 % (lhs_size, rhs_size)) 518 output.append([lhs,rhs]) 519 continue 520 else: 521 for targets, expr in zip(lhs_targets, lhs.args): 522 targets.append(expr) 523 524 if complete_assignments: 525 complete_assignments.append(rhs) 526 output.append(complete_assignments) 527 528 # recursively flatten partial assignments 529 for cascade, rhs in zip(lhs_targets, rhs_args): 530 if cascade: 531 cascade.append(rhs) 532 flatten_parallel_assignments(cascade, output) 533 534 # recursively flatten starred assignments 535 for cascade in starred_assignments: 536 if cascade[0].is_sequence_constructor: 537 flatten_parallel_assignments(cascade, output) 538 else: 539 output.append(cascade) 540 541def map_starred_assignment(lhs_targets, starred_assignments, lhs_args, rhs_args): 542 # Appends the fixed-position LHS targets to the target list that 543 # appear left and right of the starred argument. 544 # 545 # The starred_assignments list receives a new tuple 546 # (lhs_target, rhs_values_list) that maps the remaining arguments 547 # (those that match the starred target) to a list. 548 549 # left side of the starred target 550 for i, (targets, expr) in enumerate(zip(lhs_targets, lhs_args)): 551 if expr.is_starred: 552 starred = i 553 lhs_remaining = len(lhs_args) - i - 1 554 break 555 targets.append(expr) 556 else: 557 raise InternalError("no starred arg found when splitting starred assignment") 558 559 # right side of the starred target 560 for i, (targets, expr) in enumerate(zip(lhs_targets[-lhs_remaining:], 561 lhs_args[starred + 1:])): 562 targets.append(expr) 563 564 # the starred target itself, must be assigned a (potentially empty) list 565 target = lhs_args[starred].target # unpack starred node 566 starred_rhs = rhs_args[starred:] 567 if lhs_remaining: 568 starred_rhs = starred_rhs[:-lhs_remaining] 569 if starred_rhs: 570 pos = starred_rhs[0].pos 571 else: 572 pos = target.pos 573 starred_assignments.append([ 574 target, ExprNodes.ListNode(pos=pos, args=starred_rhs)]) 575 576 577class PxdPostParse(CythonTransform, SkipDeclarations): 578 """ 579 Basic interpretation/validity checking that should only be 580 done on pxd trees. 581 582 A lot of this checking currently happens in the parser; but 583 what is listed below happens here. 584 585 - "def" functions are let through only if they fill the 586 getbuffer/releasebuffer slots 587 588 - cdef functions are let through only if they are on the 589 top level and are declared "inline" 590 """ 591 ERR_INLINE_ONLY = "function definition in pxd file must be declared 'cdef inline'" 592 ERR_NOGO_WITH_INLINE = "inline function definition in pxd file cannot be '%s'" 593 594 def __call__(self, node): 595 self.scope_type = 'pxd' 596 return super(PxdPostParse, self).__call__(node) 597 598 def visit_CClassDefNode(self, node): 599 old = self.scope_type 600 self.scope_type = 'cclass' 601 self.visitchildren(node) 602 self.scope_type = old 603 return node 604 605 def visit_FuncDefNode(self, node): 606 # FuncDefNode always come with an implementation (without 607 # an imp they are CVarDefNodes..) 608 err = self.ERR_INLINE_ONLY 609 610 if (isinstance(node, Nodes.DefNode) and self.scope_type == 'cclass' 611 and node.name in ('__getbuffer__', '__releasebuffer__')): 612 err = None # allow these slots 613 614 if isinstance(node, Nodes.CFuncDefNode): 615 if (u'inline' in node.modifiers and 616 self.scope_type in ('pxd', 'cclass')): 617 node.inline_in_pxd = True 618 if node.visibility != 'private': 619 err = self.ERR_NOGO_WITH_INLINE % node.visibility 620 elif node.api: 621 err = self.ERR_NOGO_WITH_INLINE % 'api' 622 else: 623 err = None # allow inline function 624 else: 625 err = self.ERR_INLINE_ONLY 626 627 if err: 628 self.context.nonfatal_error(PostParseError(node.pos, err)) 629 return None 630 else: 631 return node 632 633 634class TrackNumpyAttributes(VisitorTransform, SkipDeclarations): 635 # TODO: Make name handling as good as in InterpretCompilerDirectives() below - probably best to merge the two. 636 def __init__(self): 637 super(TrackNumpyAttributes, self).__init__() 638 self.numpy_module_names = set() 639 640 def visit_CImportStatNode(self, node): 641 if node.module_name == u"numpy": 642 self.numpy_module_names.add(node.as_name or u"numpy") 643 return node 644 645 def visit_AttributeNode(self, node): 646 self.visitchildren(node) 647 obj = node.obj 648 if (obj.is_name and obj.name in self.numpy_module_names) or obj.is_numpy_attribute: 649 node.is_numpy_attribute = True 650 return node 651 652 visit_Node = VisitorTransform.recurse_to_children 653 654 655class InterpretCompilerDirectives(CythonTransform): 656 """ 657 After parsing, directives can be stored in a number of places: 658 - #cython-comments at the top of the file (stored in ModuleNode) 659 - Command-line arguments overriding these 660 - @cython.directivename decorators 661 - with cython.directivename: statements 662 - replaces "cython.compiled" with BoolNode(value=True) 663 allowing unreachable blocks to be removed at a fairly early stage 664 before cython typing rules are forced on applied 665 666 This transform is responsible for interpreting these various sources 667 and store the directive in two ways: 668 - Set the directives attribute of the ModuleNode for global directives. 669 - Use a CompilerDirectivesNode to override directives for a subtree. 670 671 (The first one is primarily to not have to modify with the tree 672 structure, so that ModuleNode stay on top.) 673 674 The directives are stored in dictionaries from name to value in effect. 675 Each such dictionary is always filled in for all possible directives, 676 using default values where no value is given by the user. 677 678 The available directives are controlled in Options.py. 679 680 Note that we have to run this prior to analysis, and so some minor 681 duplication of functionality has to occur: We manually track cimports 682 and which names the "cython" module may have been imported to. 683 """ 684 unop_method_nodes = { 685 'typeof': ExprNodes.TypeofNode, 686 687 'operator.address': ExprNodes.AmpersandNode, 688 'operator.dereference': ExprNodes.DereferenceNode, 689 'operator.preincrement' : ExprNodes.inc_dec_constructor(True, '++'), 690 'operator.predecrement' : ExprNodes.inc_dec_constructor(True, '--'), 691 'operator.postincrement': ExprNodes.inc_dec_constructor(False, '++'), 692 'operator.postdecrement': ExprNodes.inc_dec_constructor(False, '--'), 693 'operator.typeid' : ExprNodes.TypeidNode, 694 695 # For backwards compatibility. 696 'address': ExprNodes.AmpersandNode, 697 } 698 699 binop_method_nodes = { 700 'operator.comma' : ExprNodes.c_binop_constructor(','), 701 } 702 703 special_methods = { 704 'declare', 'union', 'struct', 'typedef', 705 'sizeof', 'cast', 'pointer', 'compiled', 706 'NULL', 'fused_type', 'parallel', 707 } 708 special_methods.update(unop_method_nodes) 709 710 valid_parallel_directives = { 711 "parallel", 712 "prange", 713 "threadid", 714 #"threadsavailable", 715 } 716 717 def __init__(self, context, compilation_directive_defaults): 718 super(InterpretCompilerDirectives, self).__init__(context) 719 self.cython_module_names = set() 720 self.directive_names = {'staticmethod': 'staticmethod'} 721 self.parallel_directives = {} 722 directives = copy.deepcopy(Options.get_directive_defaults()) 723 for key, value in compilation_directive_defaults.items(): 724 directives[_unicode(key)] = copy.deepcopy(value) 725 self.directives = directives 726 727 def check_directive_scope(self, pos, directive, scope): 728 legal_scopes = Options.directive_scopes.get(directive, None) 729 if legal_scopes and scope not in legal_scopes: 730 self.context.nonfatal_error(PostParseError(pos, 'The %s compiler directive ' 731 'is not allowed in %s scope' % (directive, scope))) 732 return False 733 else: 734 if directive not in Options.directive_types: 735 error(pos, "Invalid directive: '%s'." % (directive,)) 736 return True 737 738 # Set up processing and handle the cython: comments. 739 def visit_ModuleNode(self, node): 740 for key in sorted(node.directive_comments): 741 if not self.check_directive_scope(node.pos, key, 'module'): 742 self.wrong_scope_error(node.pos, key, 'module') 743 del node.directive_comments[key] 744 745 self.module_scope = node.scope 746 747 self.directives.update(node.directive_comments) 748 node.directives = self.directives 749 node.parallel_directives = self.parallel_directives 750 self.visitchildren(node) 751 node.cython_module_names = self.cython_module_names 752 return node 753 754 # The following four functions track imports and cimports that 755 # begin with "cython" 756 def is_cython_directive(self, name): 757 return (name in Options.directive_types or 758 name in self.special_methods or 759 PyrexTypes.parse_basic_type(name)) 760 761 def is_parallel_directive(self, full_name, pos): 762 """ 763 Checks to see if fullname (e.g. cython.parallel.prange) is a valid 764 parallel directive. If it is a star import it also updates the 765 parallel_directives. 766 """ 767 result = (full_name + ".").startswith("cython.parallel.") 768 769 if result: 770 directive = full_name.split('.') 771 if full_name == u"cython.parallel": 772 self.parallel_directives[u"parallel"] = u"cython.parallel" 773 elif full_name == u"cython.parallel.*": 774 for name in self.valid_parallel_directives: 775 self.parallel_directives[name] = u"cython.parallel.%s" % name 776 elif (len(directive) != 3 or 777 directive[-1] not in self.valid_parallel_directives): 778 error(pos, "No such directive: %s" % full_name) 779 780 self.module_scope.use_utility_code( 781 UtilityCode.load_cached("InitThreads", "ModuleSetupCode.c")) 782 783 return result 784 785 def visit_CImportStatNode(self, node): 786 module_name = node.module_name 787 if module_name == u"cython.cimports": 788 error(node.pos, "Cannot cimport the 'cython.cimports' package directly, only submodules.") 789 if module_name.startswith(u"cython.cimports."): 790 if node.as_name and node.as_name != u'cython': 791 node.module_name = module_name[len(u"cython.cimports."):] 792 return node 793 error(node.pos, 794 "Python cimports must use 'from cython.cimports... import ...'" 795 " or 'import ... as ...', not just 'import ...'") 796 797 if module_name == u"cython": 798 self.cython_module_names.add(node.as_name or u"cython") 799 elif module_name.startswith(u"cython."): 800 if module_name.startswith(u"cython.parallel."): 801 error(node.pos, node.module_name + " is not a module") 802 if module_name == u"cython.parallel": 803 if node.as_name and node.as_name != u"cython": 804 self.parallel_directives[node.as_name] = module_name 805 else: 806 self.cython_module_names.add(u"cython") 807 self.parallel_directives[ 808 u"cython.parallel"] = module_name 809 self.module_scope.use_utility_code( 810 UtilityCode.load_cached("InitThreads", "ModuleSetupCode.c")) 811 elif node.as_name: 812 self.directive_names[node.as_name] = module_name[7:] 813 else: 814 self.cython_module_names.add(u"cython") 815 # if this cimport was a compiler directive, we don't 816 # want to leave the cimport node sitting in the tree 817 return None 818 return node 819 820 def visit_FromCImportStatNode(self, node): 821 module_name = node.module_name 822 if module_name == u"cython.cimports" or module_name.startswith(u"cython.cimports."): 823 # only supported for convenience 824 return self._create_cimport_from_import( 825 node.pos, module_name, node.relative_level, node.imported_names) 826 elif not node.relative_level and ( 827 module_name == u"cython" or module_name.startswith(u"cython.")): 828 submodule = (module_name + u".")[7:] 829 newimp = [] 830 831 for pos, name, as_name, kind in node.imported_names: 832 full_name = submodule + name 833 qualified_name = u"cython." + full_name 834 835 if self.is_parallel_directive(qualified_name, node.pos): 836 # from cython cimport parallel, or 837 # from cython.parallel cimport parallel, prange, ... 838 self.parallel_directives[as_name or name] = qualified_name 839 elif self.is_cython_directive(full_name): 840 self.directive_names[as_name or name] = full_name 841 if kind is not None: 842 self.context.nonfatal_error(PostParseError(pos, 843 "Compiler directive imports must be plain imports")) 844 else: 845 newimp.append((pos, name, as_name, kind)) 846 847 if not newimp: 848 return None 849 850 node.imported_names = newimp 851 return node 852 853 def visit_FromImportStatNode(self, node): 854 import_node = node.module 855 module_name = import_node.module_name.value 856 if module_name == u"cython.cimports" or module_name.startswith(u"cython.cimports."): 857 imported_names = [] 858 for name, name_node in node.items: 859 imported_names.append( 860 (name_node.pos, name, None if name == name_node.name else name_node.name, None)) 861 return self._create_cimport_from_import( 862 node.pos, module_name, import_node.level, imported_names) 863 elif module_name == u"cython" or module_name.startswith(u"cython."): 864 submodule = (module_name + u".")[7:] 865 newimp = [] 866 for name, name_node in node.items: 867 full_name = submodule + name 868 qualified_name = u"cython." + full_name 869 if self.is_parallel_directive(qualified_name, node.pos): 870 self.parallel_directives[name_node.name] = qualified_name 871 elif self.is_cython_directive(full_name): 872 self.directive_names[name_node.name] = full_name 873 else: 874 newimp.append((name, name_node)) 875 if not newimp: 876 return None 877 node.items = newimp 878 return node 879 880 def _create_cimport_from_import(self, node_pos, module_name, level, imported_names): 881 if module_name == u"cython.cimports" or module_name.startswith(u"cython.cimports."): 882 module_name = EncodedString(module_name[len(u"cython.cimports."):]) # may be empty 883 884 if module_name: 885 # from cython.cimports.a.b import x, y, z => from a.b cimport x, y, z 886 return Nodes.FromCImportStatNode( 887 node_pos, module_name=module_name, 888 relative_level=level, 889 imported_names=imported_names) 890 else: 891 # from cython.cimports import x, y, z => cimport x; cimport y; cimport z 892 return [ 893 Nodes.CImportStatNode( 894 pos, 895 module_name=dotted_name, 896 as_name=as_name, 897 is_absolute=level == 0) 898 for pos, dotted_name, as_name, _ in imported_names 899 ] 900 901 def visit_SingleAssignmentNode(self, node): 902 if isinstance(node.rhs, ExprNodes.ImportNode): 903 module_name = node.rhs.module_name.value 904 is_special_module = (module_name + u".").startswith((u"cython.parallel.", u"cython.cimports.")) 905 if module_name != u"cython" and not is_special_module: 906 return node 907 908 node = Nodes.CImportStatNode(node.pos, module_name=module_name, as_name=node.lhs.name) 909 node = self.visit_CImportStatNode(node) 910 else: 911 self.visitchildren(node) 912 913 return node 914 915 def visit_NameNode(self, node): 916 if node.name in self.cython_module_names: 917 node.is_cython_module = True 918 else: 919 directive = self.directive_names.get(node.name) 920 if directive is not None: 921 node.cython_attribute = directive 922 if node.as_cython_attribute() == "compiled": 923 return ExprNodes.BoolNode(node.pos, value=True) # replace early so unused branches can be dropped 924 # before they have a chance to cause compile-errors 925 return node 926 927 def visit_AttributeNode(self, node): 928 self.visitchildren(node) 929 if node.as_cython_attribute() == "compiled": 930 return ExprNodes.BoolNode(node.pos, value=True) # replace early so unused branches can be dropped 931 # before they have a chance to cause compile-errors 932 return node 933 934 def visit_AnnotationNode(self, node): 935 # for most transforms annotations are left unvisited (because they're unevaluated) 936 # however, it is important to pick up compiler directives from them 937 if node.expr: 938 self.visitchildren(node.expr) 939 return node 940 941 def visit_NewExprNode(self, node): 942 self.visit(node.cppclass) 943 self.visitchildren(node) 944 return node 945 946 def try_to_parse_directives(self, node): 947 # If node is the contents of an directive (in a with statement or 948 # decorator), returns a list of (directivename, value) pairs. 949 # Otherwise, returns None 950 if isinstance(node, ExprNodes.CallNode): 951 self.visit(node.function) 952 optname = node.function.as_cython_attribute() 953 if optname: 954 directivetype = Options.directive_types.get(optname) 955 if directivetype: 956 args, kwds = node.explicit_args_kwds() 957 directives = [] 958 key_value_pairs = [] 959 if kwds is not None and directivetype is not dict: 960 for keyvalue in kwds.key_value_pairs: 961 key, value = keyvalue 962 sub_optname = "%s.%s" % (optname, key.value) 963 if Options.directive_types.get(sub_optname): 964 directives.append(self.try_to_parse_directive(sub_optname, [value], None, keyvalue.pos)) 965 else: 966 key_value_pairs.append(keyvalue) 967 if not key_value_pairs: 968 kwds = None 969 else: 970 kwds.key_value_pairs = key_value_pairs 971 if directives and not kwds and not args: 972 return directives 973 directives.append(self.try_to_parse_directive(optname, args, kwds, node.function.pos)) 974 return directives 975 elif isinstance(node, (ExprNodes.AttributeNode, ExprNodes.NameNode)): 976 self.visit(node) 977 optname = node.as_cython_attribute() 978 if optname: 979 directivetype = Options.directive_types.get(optname) 980 if directivetype is bool: 981 arg = ExprNodes.BoolNode(node.pos, value=True) 982 return [self.try_to_parse_directive(optname, [arg], None, node.pos)] 983 elif directivetype is None: 984 return [(optname, None)] 985 else: 986 raise PostParseError( 987 node.pos, "The '%s' directive should be used as a function call." % optname) 988 return None 989 990 def try_to_parse_directive(self, optname, args, kwds, pos): 991 if optname == 'np_pythran' and not self.context.cpp: 992 raise PostParseError(pos, 'The %s directive can only be used in C++ mode.' % optname) 993 elif optname == 'exceptval': 994 # default: exceptval(None, check=True) 995 arg_error = len(args) > 1 996 check = True 997 if kwds and kwds.key_value_pairs: 998 kw = kwds.key_value_pairs[0] 999 if (len(kwds.key_value_pairs) == 1 and 1000 kw.key.is_string_literal and kw.key.value == 'check' and 1001 isinstance(kw.value, ExprNodes.BoolNode)): 1002 check = kw.value.value 1003 else: 1004 arg_error = True 1005 if arg_error: 1006 raise PostParseError( 1007 pos, 'The exceptval directive takes 0 or 1 positional arguments and the boolean keyword "check"') 1008 return ('exceptval', (args[0] if args else None, check)) 1009 1010 directivetype = Options.directive_types.get(optname) 1011 if len(args) == 1 and isinstance(args[0], ExprNodes.NoneNode): 1012 return optname, Options.get_directive_defaults()[optname] 1013 elif directivetype is bool: 1014 if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.BoolNode): 1015 raise PostParseError(pos, 1016 'The %s directive takes one compile-time boolean argument' % optname) 1017 return (optname, args[0].value) 1018 elif directivetype is int: 1019 if kwds is not None or len(args) != 1 or not isinstance(args[0], ExprNodes.IntNode): 1020 raise PostParseError(pos, 1021 'The %s directive takes one compile-time integer argument' % optname) 1022 return (optname, int(args[0].value)) 1023 elif directivetype is str: 1024 if kwds is not None or len(args) != 1 or not isinstance( 1025 args[0], (ExprNodes.StringNode, ExprNodes.UnicodeNode)): 1026 raise PostParseError(pos, 1027 'The %s directive takes one compile-time string argument' % optname) 1028 return (optname, str(args[0].value)) 1029 elif directivetype is type: 1030 if kwds is not None or len(args) != 1: 1031 raise PostParseError(pos, 1032 'The %s directive takes one type argument' % optname) 1033 return (optname, args[0]) 1034 elif directivetype is dict: 1035 if len(args) != 0: 1036 raise PostParseError(pos, 1037 'The %s directive takes no prepositional arguments' % optname) 1038 return optname, dict([(key.value, value) for key, value in kwds.key_value_pairs]) 1039 elif directivetype is list: 1040 if kwds and len(kwds.key_value_pairs) != 0: 1041 raise PostParseError(pos, 1042 'The %s directive takes no keyword arguments' % optname) 1043 return optname, [ str(arg.value) for arg in args ] 1044 elif callable(directivetype): 1045 if kwds is not None or len(args) != 1 or not isinstance( 1046 args[0], (ExprNodes.StringNode, ExprNodes.UnicodeNode)): 1047 raise PostParseError(pos, 1048 'The %s directive takes one compile-time string argument' % optname) 1049 return (optname, directivetype(optname, str(args[0].value))) 1050 else: 1051 assert False 1052 1053 def visit_with_directives(self, node, directives): 1054 if not directives: 1055 return self.visit_Node(node) 1056 1057 old_directives = self.directives 1058 new_directives = Options.copy_inherited_directives(old_directives, **directives) 1059 1060 if new_directives == old_directives: 1061 return self.visit_Node(node) 1062 1063 self.directives = new_directives 1064 retbody = self.visit_Node(node) 1065 self.directives = old_directives 1066 1067 if not isinstance(retbody, Nodes.StatListNode): 1068 retbody = Nodes.StatListNode(node.pos, stats=[retbody]) 1069 return Nodes.CompilerDirectivesNode( 1070 pos=retbody.pos, body=retbody, directives=new_directives) 1071 1072 # Handle decorators 1073 def visit_FuncDefNode(self, node): 1074 directives = self._extract_directives(node, 'function') 1075 return self.visit_with_directives(node, directives) 1076 1077 def visit_CVarDefNode(self, node): 1078 directives = self._extract_directives(node, 'function') 1079 for name, value in directives.items(): 1080 if name == 'locals': 1081 node.directive_locals = value 1082 elif name not in ('final', 'staticmethod'): 1083 self.context.nonfatal_error(PostParseError( 1084 node.pos, 1085 "Cdef functions can only take cython.locals(), " 1086 "staticmethod, or final decorators, got %s." % name)) 1087 return self.visit_with_directives(node, directives) 1088 1089 def visit_CClassDefNode(self, node): 1090 directives = self._extract_directives(node, 'cclass') 1091 return self.visit_with_directives(node, directives) 1092 1093 def visit_CppClassNode(self, node): 1094 directives = self._extract_directives(node, 'cppclass') 1095 return self.visit_with_directives(node, directives) 1096 1097 def visit_PyClassDefNode(self, node): 1098 directives = self._extract_directives(node, 'class') 1099 return self.visit_with_directives(node, directives) 1100 1101 def _extract_directives(self, node, scope_name): 1102 if not node.decorators: 1103 return {} 1104 # Split the decorators into two lists -- real decorators and directives 1105 directives = [] 1106 realdecs = [] 1107 both = [] 1108 # Decorators coming first take precedence. 1109 for dec in node.decorators[::-1]: 1110 new_directives = self.try_to_parse_directives(dec.decorator) 1111 if new_directives is not None: 1112 for directive in new_directives: 1113 if self.check_directive_scope(node.pos, directive[0], scope_name): 1114 name, value = directive 1115 if self.directives.get(name, object()) != value: 1116 directives.append(directive) 1117 if directive[0] == 'staticmethod': 1118 both.append(dec) 1119 # Adapt scope type based on decorators that change it. 1120 if directive[0] == 'cclass' and scope_name == 'class': 1121 scope_name = 'cclass' 1122 else: 1123 realdecs.append(dec) 1124 if realdecs and (scope_name == 'cclass' or 1125 isinstance(node, (Nodes.CClassDefNode, Nodes.CVarDefNode))): 1126 # Note - arbitrary C function decorators are caught later in DecoratorTransform 1127 raise PostParseError(realdecs[0].pos, "Cdef functions/classes cannot take arbitrary decorators.") 1128 node.decorators = realdecs[::-1] + both[::-1] 1129 # merge or override repeated directives 1130 optdict = {} 1131 for directive in directives: 1132 name, value = directive 1133 if name in optdict: 1134 old_value = optdict[name] 1135 # keywords and arg lists can be merged, everything 1136 # else overrides completely 1137 if isinstance(old_value, dict): 1138 old_value.update(value) 1139 elif isinstance(old_value, list): 1140 old_value.extend(value) 1141 else: 1142 optdict[name] = value 1143 else: 1144 optdict[name] = value 1145 return optdict 1146 1147 # Handle with-statements 1148 def visit_WithStatNode(self, node): 1149 directive_dict = {} 1150 for directive in self.try_to_parse_directives(node.manager) or []: 1151 if directive is not None: 1152 if node.target is not None: 1153 self.context.nonfatal_error( 1154 PostParseError(node.pos, "Compiler directive with statements cannot contain 'as'")) 1155 else: 1156 name, value = directive 1157 if name in ('nogil', 'gil'): 1158 # special case: in pure mode, "with nogil" spells "with cython.nogil" 1159 node = Nodes.GILStatNode(node.pos, state = name, body = node.body) 1160 return self.visit_Node(node) 1161 if self.check_directive_scope(node.pos, name, 'with statement'): 1162 directive_dict[name] = value 1163 if directive_dict: 1164 return self.visit_with_directives(node.body, directive_dict) 1165 return self.visit_Node(node) 1166 1167 1168class ParallelRangeTransform(CythonTransform, SkipDeclarations): 1169 """ 1170 Transform cython.parallel stuff. The parallel_directives come from the 1171 module node, set there by InterpretCompilerDirectives. 1172 1173 x = cython.parallel.threadavailable() -> ParallelThreadAvailableNode 1174 with nogil, cython.parallel.parallel(): -> ParallelWithBlockNode 1175 print cython.parallel.threadid() -> ParallelThreadIdNode 1176 for i in cython.parallel.prange(...): -> ParallelRangeNode 1177 ... 1178 """ 1179 1180 # a list of names, maps 'cython.parallel.prange' in the code to 1181 # ['cython', 'parallel', 'prange'] 1182 parallel_directive = None 1183 1184 # Indicates whether a namenode in an expression is the cython module 1185 namenode_is_cython_module = False 1186 1187 # Keep track of whether we are the context manager of a 'with' statement 1188 in_context_manager_section = False 1189 1190 # One of 'prange' or 'with parallel'. This is used to disallow closely 1191 # nested 'with parallel:' blocks 1192 state = None 1193 1194 directive_to_node = { 1195 u"cython.parallel.parallel": Nodes.ParallelWithBlockNode, 1196 # u"cython.parallel.threadsavailable": ExprNodes.ParallelThreadsAvailableNode, 1197 u"cython.parallel.threadid": ExprNodes.ParallelThreadIdNode, 1198 u"cython.parallel.prange": Nodes.ParallelRangeNode, 1199 } 1200 1201 def node_is_parallel_directive(self, node): 1202 return node.name in self.parallel_directives or node.is_cython_module 1203 1204 def get_directive_class_node(self, node): 1205 """ 1206 Figure out which parallel directive was used and return the associated 1207 Node class. 1208 1209 E.g. for a cython.parallel.prange() call we return ParallelRangeNode 1210 """ 1211 if self.namenode_is_cython_module: 1212 directive = '.'.join(self.parallel_directive) 1213 else: 1214 directive = self.parallel_directives[self.parallel_directive[0]] 1215 directive = '%s.%s' % (directive, 1216 '.'.join(self.parallel_directive[1:])) 1217 directive = directive.rstrip('.') 1218 1219 cls = self.directive_to_node.get(directive) 1220 if cls is None and not (self.namenode_is_cython_module and 1221 self.parallel_directive[0] != 'parallel'): 1222 error(node.pos, "Invalid directive: %s" % directive) 1223 1224 self.namenode_is_cython_module = False 1225 self.parallel_directive = None 1226 1227 return cls 1228 1229 def visit_ModuleNode(self, node): 1230 """ 1231 If any parallel directives were imported, copy them over and visit 1232 the AST 1233 """ 1234 if node.parallel_directives: 1235 self.parallel_directives = node.parallel_directives 1236 return self.visit_Node(node) 1237 1238 # No parallel directives were imported, so they can't be used :) 1239 return node 1240 1241 def visit_NameNode(self, node): 1242 if self.node_is_parallel_directive(node): 1243 self.parallel_directive = [node.name] 1244 self.namenode_is_cython_module = node.is_cython_module 1245 return node 1246 1247 def visit_AttributeNode(self, node): 1248 self.visitchildren(node) 1249 if self.parallel_directive: 1250 self.parallel_directive.append(node.attribute) 1251 return node 1252 1253 def visit_CallNode(self, node): 1254 self.visit(node.function) 1255 if not self.parallel_directive: 1256 self.visitchildren(node, exclude=('function',)) 1257 return node 1258 1259 # We are a parallel directive, replace this node with the 1260 # corresponding ParallelSomethingSomething node 1261 1262 if isinstance(node, ExprNodes.GeneralCallNode): 1263 args = node.positional_args.args 1264 kwargs = node.keyword_args 1265 else: 1266 args = node.args 1267 kwargs = {} 1268 1269 parallel_directive_class = self.get_directive_class_node(node) 1270 if parallel_directive_class: 1271 # Note: in case of a parallel() the body is set by 1272 # visit_WithStatNode 1273 node = parallel_directive_class(node.pos, args=args, kwargs=kwargs) 1274 1275 return node 1276 1277 def visit_WithStatNode(self, node): 1278 "Rewrite with cython.parallel.parallel() blocks" 1279 newnode = self.visit(node.manager) 1280 1281 if isinstance(newnode, Nodes.ParallelWithBlockNode): 1282 if self.state == 'parallel with': 1283 error(node.manager.pos, 1284 "Nested parallel with blocks are disallowed") 1285 1286 self.state = 'parallel with' 1287 body = self.visit(node.body) 1288 self.state = None 1289 1290 newnode.body = body 1291 return newnode 1292 elif self.parallel_directive: 1293 parallel_directive_class = self.get_directive_class_node(node) 1294 1295 if not parallel_directive_class: 1296 # There was an error, stop here and now 1297 return None 1298 1299 if parallel_directive_class is Nodes.ParallelWithBlockNode: 1300 error(node.pos, "The parallel directive must be called") 1301 return None 1302 1303 node.body = self.visit(node.body) 1304 return node 1305 1306 def visit_ForInStatNode(self, node): 1307 "Rewrite 'for i in cython.parallel.prange(...):'" 1308 self.visit(node.iterator) 1309 self.visit(node.target) 1310 1311 in_prange = isinstance(node.iterator.sequence, 1312 Nodes.ParallelRangeNode) 1313 previous_state = self.state 1314 1315 if in_prange: 1316 # This will replace the entire ForInStatNode, so copy the 1317 # attributes 1318 parallel_range_node = node.iterator.sequence 1319 1320 parallel_range_node.target = node.target 1321 parallel_range_node.body = node.body 1322 parallel_range_node.else_clause = node.else_clause 1323 1324 node = parallel_range_node 1325 1326 if not isinstance(node.target, ExprNodes.NameNode): 1327 error(node.target.pos, 1328 "Can only iterate over an iteration variable") 1329 1330 self.state = 'prange' 1331 1332 self.visit(node.body) 1333 self.state = previous_state 1334 self.visit(node.else_clause) 1335 return node 1336 1337 def visit(self, node): 1338 "Visit a node that may be None" 1339 if node is not None: 1340 return super(ParallelRangeTransform, self).visit(node) 1341 1342 1343class WithTransform(CythonTransform, SkipDeclarations): 1344 def visit_WithStatNode(self, node): 1345 self.visitchildren(node, 'body') 1346 pos = node.pos 1347 is_async = node.is_async 1348 body, target, manager = node.body, node.target, node.manager 1349 node.enter_call = ExprNodes.SimpleCallNode( 1350 pos, function=ExprNodes.AttributeNode( 1351 pos, obj=ExprNodes.CloneNode(manager), 1352 attribute=EncodedString('__aenter__' if is_async else '__enter__'), 1353 is_special_lookup=True), 1354 args=[], 1355 is_temp=True) 1356 1357 if is_async: 1358 node.enter_call = ExprNodes.AwaitExprNode(pos, arg=node.enter_call) 1359 1360 if target is not None: 1361 body = Nodes.StatListNode( 1362 pos, stats=[ 1363 Nodes.WithTargetAssignmentStatNode( 1364 pos, lhs=target, with_node=node), 1365 body]) 1366 1367 excinfo_target = ExprNodes.TupleNode(pos, slow=True, args=[ 1368 ExprNodes.ExcValueNode(pos) for _ in range(3)]) 1369 except_clause = Nodes.ExceptClauseNode( 1370 pos, body=Nodes.IfStatNode( 1371 pos, if_clauses=[ 1372 Nodes.IfClauseNode( 1373 pos, condition=ExprNodes.NotNode( 1374 pos, operand=ExprNodes.WithExitCallNode( 1375 pos, with_stat=node, 1376 test_if_run=False, 1377 args=excinfo_target, 1378 await_expr=ExprNodes.AwaitExprNode(pos, arg=None) if is_async else None)), 1379 body=Nodes.ReraiseStatNode(pos), 1380 ), 1381 ], 1382 else_clause=None), 1383 pattern=None, 1384 target=None, 1385 excinfo_target=excinfo_target, 1386 ) 1387 1388 node.body = Nodes.TryFinallyStatNode( 1389 pos, body=Nodes.TryExceptStatNode( 1390 pos, body=body, 1391 except_clauses=[except_clause], 1392 else_clause=None, 1393 ), 1394 finally_clause=Nodes.ExprStatNode( 1395 pos, expr=ExprNodes.WithExitCallNode( 1396 pos, with_stat=node, 1397 test_if_run=True, 1398 args=ExprNodes.TupleNode( 1399 pos, args=[ExprNodes.NoneNode(pos) for _ in range(3)]), 1400 await_expr=ExprNodes.AwaitExprNode(pos, arg=None) if is_async else None)), 1401 handle_error_case=False, 1402 ) 1403 return node 1404 1405 def visit_ExprNode(self, node): 1406 # With statements are never inside expressions. 1407 return node 1408 1409 1410class DecoratorTransform(ScopeTrackingTransform, SkipDeclarations): 1411 """ 1412 Transforms method decorators in cdef classes into nested calls or properties. 1413 1414 Python-style decorator properties are transformed into a PropertyNode 1415 with up to the three getter, setter and deleter DefNodes. 1416 The functional style isn't supported yet. 1417 """ 1418 _properties = None 1419 1420 _map_property_attribute = { 1421 'getter': EncodedString('__get__'), 1422 'setter': EncodedString('__set__'), 1423 'deleter': EncodedString('__del__'), 1424 }.get 1425 1426 def visit_CClassDefNode(self, node): 1427 if self._properties is None: 1428 self._properties = [] 1429 self._properties.append({}) 1430 node = super(DecoratorTransform, self).visit_CClassDefNode(node) 1431 self._properties.pop() 1432 return node 1433 1434 def visit_PropertyNode(self, node): 1435 # Low-level warning for other code until we can convert all our uses over. 1436 level = 2 if isinstance(node.pos[0], str) else 0 1437 warning(node.pos, "'property %s:' syntax is deprecated, use '@property'" % node.name, level) 1438 return node 1439 1440 def visit_CFuncDefNode(self, node): 1441 node = self.visit_FuncDefNode(node) 1442 if not node.decorators: 1443 return node 1444 elif self.scope_type != 'cclass' or self.scope_node.visibility != "extern": 1445 # at the moment cdef functions are very restricted in what decorators they can take 1446 # so it's simple to test for the small number of allowed decorators.... 1447 if not (len(node.decorators) == 1 and node.decorators[0].decorator.is_name and 1448 node.decorators[0].decorator.name == "staticmethod"): 1449 error(node.decorators[0].pos, "Cdef functions cannot take arbitrary decorators.") 1450 return node 1451 1452 ret_node = node 1453 decorator_node = self._find_property_decorator(node) 1454 if decorator_node: 1455 if decorator_node.decorator.is_name: 1456 name = node.declared_name() 1457 if name: 1458 ret_node = self._add_property(node, name, decorator_node) 1459 else: 1460 error(decorator_node.pos, "C property decorator can only be @property") 1461 1462 if node.decorators: 1463 return self._reject_decorated_property(node, node.decorators[0]) 1464 return ret_node 1465 1466 def visit_DefNode(self, node): 1467 scope_type = self.scope_type 1468 node = self.visit_FuncDefNode(node) 1469 if scope_type != 'cclass' or not node.decorators: 1470 return node 1471 1472 # transform @property decorators 1473 decorator_node = self._find_property_decorator(node) 1474 if decorator_node is not None: 1475 decorator = decorator_node.decorator 1476 if decorator.is_name: 1477 return self._add_property(node, node.name, decorator_node) 1478 else: 1479 handler_name = self._map_property_attribute(decorator.attribute) 1480 if handler_name: 1481 if decorator.obj.name != node.name: 1482 # CPython does not generate an error or warning, but not something useful either. 1483 error(decorator_node.pos, 1484 "Mismatching property names, expected '%s', got '%s'" % ( 1485 decorator.obj.name, node.name)) 1486 elif len(node.decorators) > 1: 1487 return self._reject_decorated_property(node, decorator_node) 1488 else: 1489 return self._add_to_property(node, handler_name, decorator_node) 1490 1491 # we clear node.decorators, so we need to set the 1492 # is_staticmethod/is_classmethod attributes now 1493 for decorator in node.decorators: 1494 func = decorator.decorator 1495 if func.is_name: 1496 node.is_classmethod |= func.name == 'classmethod' 1497 node.is_staticmethod |= func.name == 'staticmethod' 1498 1499 # transform normal decorators 1500 decs = node.decorators 1501 node.decorators = None 1502 return self.chain_decorators(node, decs, node.name) 1503 1504 def _find_property_decorator(self, node): 1505 properties = self._properties[-1] 1506 for decorator_node in node.decorators[::-1]: 1507 decorator = decorator_node.decorator 1508 if decorator.is_name and decorator.name == 'property': 1509 # @property 1510 return decorator_node 1511 elif decorator.is_attribute and decorator.obj.name in properties: 1512 # @prop.setter etc. 1513 return decorator_node 1514 return None 1515 1516 @staticmethod 1517 def _reject_decorated_property(node, decorator_node): 1518 # restrict transformation to outermost decorator as wrapped properties will probably not work 1519 for deco in node.decorators: 1520 if deco != decorator_node: 1521 error(deco.pos, "Property methods with additional decorators are not supported") 1522 return node 1523 1524 def _add_property(self, node, name, decorator_node): 1525 if len(node.decorators) > 1: 1526 return self._reject_decorated_property(node, decorator_node) 1527 node.decorators.remove(decorator_node) 1528 properties = self._properties[-1] 1529 is_cproperty = isinstance(node, Nodes.CFuncDefNode) 1530 body = Nodes.StatListNode(node.pos, stats=[node]) 1531 if is_cproperty: 1532 if name in properties: 1533 error(node.pos, "C property redeclared") 1534 if 'inline' not in node.modifiers: 1535 error(node.pos, "C property method must be declared 'inline'") 1536 prop = Nodes.CPropertyNode(node.pos, doc=node.doc, name=name, body=body) 1537 elif name in properties: 1538 prop = properties[name] 1539 if prop.is_cproperty: 1540 error(node.pos, "C property redeclared") 1541 else: 1542 node.name = EncodedString("__get__") 1543 prop.pos = node.pos 1544 prop.doc = node.doc 1545 prop.body.stats = [node] 1546 return None 1547 else: 1548 node.name = EncodedString("__get__") 1549 prop = Nodes.PropertyNode( 1550 node.pos, name=name, doc=node.doc, body=body) 1551 properties[name] = prop 1552 return prop 1553 1554 def _add_to_property(self, node, name, decorator): 1555 properties = self._properties[-1] 1556 prop = properties[node.name] 1557 if prop.is_cproperty: 1558 error(node.pos, "C property redeclared") 1559 return None 1560 node.name = name 1561 node.decorators.remove(decorator) 1562 stats = prop.body.stats 1563 for i, stat in enumerate(stats): 1564 if stat.name == name: 1565 stats[i] = node 1566 break 1567 else: 1568 stats.append(node) 1569 return None 1570 1571 @staticmethod 1572 def chain_decorators(node, decorators, name): 1573 """ 1574 Decorators are applied directly in DefNode and PyClassDefNode to avoid 1575 reassignments to the function/class name - except for cdef class methods. 1576 For those, the reassignment is required as methods are originally 1577 defined in the PyMethodDef struct. 1578 1579 The IndirectionNode allows DefNode to override the decorator. 1580 """ 1581 decorator_result = ExprNodes.NameNode(node.pos, name=name) 1582 for decorator in decorators[::-1]: 1583 decorator_result = ExprNodes.SimpleCallNode( 1584 decorator.pos, 1585 function=decorator.decorator, 1586 args=[decorator_result]) 1587 1588 name_node = ExprNodes.NameNode(node.pos, name=name) 1589 reassignment = Nodes.SingleAssignmentNode( 1590 node.pos, 1591 lhs=name_node, 1592 rhs=decorator_result) 1593 1594 reassignment = Nodes.IndirectionNode([reassignment]) 1595 node.decorator_indirection = reassignment 1596 return [node, reassignment] 1597 1598 1599class CnameDirectivesTransform(CythonTransform, SkipDeclarations): 1600 """ 1601 Only part of the CythonUtilityCode pipeline. Must be run before 1602 DecoratorTransform in case this is a decorator for a cdef class. 1603 It filters out @cname('my_cname') decorators and rewrites them to 1604 CnameDecoratorNodes. 1605 """ 1606 1607 def handle_function(self, node): 1608 if not getattr(node, 'decorators', None): 1609 return self.visit_Node(node) 1610 1611 for i, decorator in enumerate(node.decorators): 1612 decorator = decorator.decorator 1613 1614 if (isinstance(decorator, ExprNodes.CallNode) and 1615 decorator.function.is_name and 1616 decorator.function.name == 'cname'): 1617 args, kwargs = decorator.explicit_args_kwds() 1618 1619 if kwargs: 1620 raise AssertionError( 1621 "cname decorator does not take keyword arguments") 1622 1623 if len(args) != 1: 1624 raise AssertionError( 1625 "cname decorator takes exactly one argument") 1626 1627 if not (args[0].is_literal and 1628 args[0].type == Builtin.str_type): 1629 raise AssertionError( 1630 "argument to cname decorator must be a string literal") 1631 1632 cname = args[0].compile_time_value(None) 1633 del node.decorators[i] 1634 node = Nodes.CnameDecoratorNode(pos=node.pos, node=node, 1635 cname=cname) 1636 break 1637 1638 return self.visit_Node(node) 1639 1640 visit_FuncDefNode = handle_function 1641 visit_CClassDefNode = handle_function 1642 visit_CEnumDefNode = handle_function 1643 visit_CStructOrUnionDefNode = handle_function 1644 1645 1646class ForwardDeclareTypes(CythonTransform): 1647 """ 1648 Declare all global cdef names that we allow referencing in other places, 1649 before declaring everything (else) in source code order. 1650 """ 1651 1652 def visit_CompilerDirectivesNode(self, node): 1653 env = self.module_scope 1654 old = env.directives 1655 env.directives = node.directives 1656 self.visitchildren(node) 1657 env.directives = old 1658 return node 1659 1660 def visit_ModuleNode(self, node): 1661 self.module_scope = node.scope 1662 self.module_scope.directives = node.directives 1663 self.visitchildren(node) 1664 return node 1665 1666 def visit_CDefExternNode(self, node): 1667 old_cinclude_flag = self.module_scope.in_cinclude 1668 self.module_scope.in_cinclude = 1 1669 self.visitchildren(node) 1670 self.module_scope.in_cinclude = old_cinclude_flag 1671 return node 1672 1673 def visit_CEnumDefNode(self, node): 1674 node.declare(self.module_scope) 1675 return node 1676 1677 def visit_CStructOrUnionDefNode(self, node): 1678 if node.name not in self.module_scope.entries: 1679 node.declare(self.module_scope) 1680 return node 1681 1682 def visit_CClassDefNode(self, node): 1683 if node.class_name not in self.module_scope.entries: 1684 node.declare(self.module_scope) 1685 # Expand fused methods of .pxd declared types to construct the final vtable order. 1686 type = self.module_scope.entries[node.class_name].type 1687 if type is not None and type.is_extension_type and not type.is_builtin_type and type.scope: 1688 scope = type.scope 1689 for entry in scope.cfunc_entries: 1690 if entry.type and entry.type.is_fused: 1691 entry.type.get_all_specialized_function_types() 1692 return node 1693 1694 def visit_FuncDefNode(self, node): 1695 # no traversal needed 1696 return node 1697 1698 def visit_PyClassDefNode(self, node): 1699 # no traversal needed 1700 return node 1701 1702 1703class AnalyseDeclarationsTransform(EnvTransform): 1704 1705 basic_property = TreeFragment(u""" 1706property NAME: 1707 def __get__(self): 1708 return ATTR 1709 def __set__(self, value): 1710 ATTR = value 1711 """, level='c_class', pipeline=[NormalizeTree(None)]) 1712 basic_pyobject_property = TreeFragment(u""" 1713property NAME: 1714 def __get__(self): 1715 return ATTR 1716 def __set__(self, value): 1717 ATTR = value 1718 def __del__(self): 1719 ATTR = None 1720 """, level='c_class', pipeline=[NormalizeTree(None)]) 1721 basic_property_ro = TreeFragment(u""" 1722property NAME: 1723 def __get__(self): 1724 return ATTR 1725 """, level='c_class', pipeline=[NormalizeTree(None)]) 1726 1727 struct_or_union_wrapper = TreeFragment(u""" 1728cdef class NAME: 1729 cdef TYPE value 1730 def __init__(self, MEMBER=None): 1731 cdef int count 1732 count = 0 1733 INIT_ASSIGNMENTS 1734 if IS_UNION and count > 1: 1735 raise ValueError, "At most one union member should be specified." 1736 def __str__(self): 1737 return STR_FORMAT % MEMBER_TUPLE 1738 def __repr__(self): 1739 return REPR_FORMAT % MEMBER_TUPLE 1740 """, pipeline=[NormalizeTree(None)]) 1741 1742 init_assignment = TreeFragment(u""" 1743if VALUE is not None: 1744 ATTR = VALUE 1745 count += 1 1746 """, pipeline=[NormalizeTree(None)]) 1747 1748 fused_function = None 1749 in_lambda = 0 1750 1751 def __call__(self, root): 1752 # needed to determine if a cdef var is declared after it's used. 1753 self.seen_vars_stack = [] 1754 self.fused_error_funcs = set() 1755 super_class = super(AnalyseDeclarationsTransform, self) 1756 self._super_visit_FuncDefNode = super_class.visit_FuncDefNode 1757 return super_class.__call__(root) 1758 1759 def visit_NameNode(self, node): 1760 self.seen_vars_stack[-1].add(node.name) 1761 return node 1762 1763 def visit_ModuleNode(self, node): 1764 # Pickling support requires injecting module-level nodes. 1765 self.extra_module_declarations = [] 1766 self.seen_vars_stack.append(set()) 1767 node.analyse_declarations(self.current_env()) 1768 self.visitchildren(node) 1769 self.seen_vars_stack.pop() 1770 node.body.stats.extend(self.extra_module_declarations) 1771 return node 1772 1773 def visit_LambdaNode(self, node): 1774 self.in_lambda += 1 1775 node.analyse_declarations(self.current_env()) 1776 self.visitchildren(node) 1777 self.in_lambda -= 1 1778 return node 1779 1780 def visit_CClassDefNode(self, node): 1781 node = self.visit_ClassDefNode(node) 1782 if node.scope and node.scope.implemented and node.body: 1783 stats = [] 1784 for entry in node.scope.var_entries: 1785 if entry.needs_property: 1786 property = self.create_Property(entry) 1787 property.analyse_declarations(node.scope) 1788 self.visit(property) 1789 stats.append(property) 1790 if stats: 1791 node.body.stats += stats 1792 if (node.visibility != 'extern' 1793 and not node.scope.lookup('__reduce__') 1794 and not node.scope.lookup('__reduce_ex__')): 1795 self._inject_pickle_methods(node) 1796 return node 1797 1798 def _inject_pickle_methods(self, node): 1799 env = self.current_env() 1800 if node.scope.directives['auto_pickle'] is False: # None means attempt it. 1801 # Old behavior of not doing anything. 1802 return 1803 auto_pickle_forced = node.scope.directives['auto_pickle'] is True 1804 1805 all_members = [] 1806 cls = node.entry.type 1807 cinit = None 1808 inherited_reduce = None 1809 while cls is not None: 1810 all_members.extend(e for e in cls.scope.var_entries if e.name not in ('__weakref__', '__dict__')) 1811 cinit = cinit or cls.scope.lookup('__cinit__') 1812 inherited_reduce = inherited_reduce or cls.scope.lookup('__reduce__') or cls.scope.lookup('__reduce_ex__') 1813 cls = cls.base_type 1814 all_members.sort(key=lambda e: e.name) 1815 1816 if inherited_reduce: 1817 # This is not failsafe, as we may not know whether a cimported class defines a __reduce__. 1818 # This is why we define __reduce_cython__ and only replace __reduce__ 1819 # (via ExtensionTypes.SetupReduce utility code) at runtime on class creation. 1820 return 1821 1822 non_py = [ 1823 e for e in all_members 1824 if not e.type.is_pyobject and (not e.type.can_coerce_to_pyobject(env) 1825 or not e.type.can_coerce_from_pyobject(env)) 1826 ] 1827 1828 structs = [e for e in all_members if e.type.is_struct_or_union] 1829 1830 if cinit or non_py or (structs and not auto_pickle_forced): 1831 if cinit: 1832 # TODO(robertwb): We could allow this if __cinit__ has no require arguments. 1833 msg = 'no default __reduce__ due to non-trivial __cinit__' 1834 elif non_py: 1835 msg = "%s cannot be converted to a Python object for pickling" % ','.join("self.%s" % e.name for e in non_py) 1836 else: 1837 # Extern structs may be only partially defined. 1838 # TODO(robertwb): Limit the restriction to extern 1839 # (and recursively extern-containing) structs. 1840 msg = ("Pickling of struct members such as %s must be explicitly requested " 1841 "with @auto_pickle(True)" % ','.join("self.%s" % e.name for e in structs)) 1842 1843 if auto_pickle_forced: 1844 error(node.pos, msg) 1845 1846 pickle_func = TreeFragment(u""" 1847 def __reduce_cython__(self): 1848 raise TypeError, "%(msg)s" 1849 def __setstate_cython__(self, __pyx_state): 1850 raise TypeError, "%(msg)s" 1851 """ % {'msg': msg}, 1852 level='c_class', pipeline=[NormalizeTree(None)]).substitute({}) 1853 pickle_func.analyse_declarations(node.scope) 1854 self.visit(pickle_func) 1855 node.body.stats.append(pickle_func) 1856 1857 else: 1858 for e in all_members: 1859 if not e.type.is_pyobject: 1860 e.type.create_to_py_utility_code(env) 1861 e.type.create_from_py_utility_code(env) 1862 all_members_names = sorted([e.name for e in all_members]) 1863 checksum = '0x%s' % hashlib.sha1(' '.join(all_members_names).encode('utf-8')).hexdigest()[:7] 1864 unpickle_func_name = '__pyx_unpickle_%s' % node.punycode_class_name 1865 1866 # TODO(robertwb): Move the state into the third argument 1867 # so it can be pickled *after* self is memoized. 1868 unpickle_func = TreeFragment(u""" 1869 def %(unpickle_func_name)s(__pyx_type, long __pyx_checksum, __pyx_state): 1870 cdef object __pyx_PickleError 1871 cdef object __pyx_result 1872 if __pyx_checksum != %(checksum)s: 1873 from pickle import PickleError as __pyx_PickleError 1874 raise __pyx_PickleError, "Incompatible checksums (%%s vs %(checksum)s = (%(members)s))" %% __pyx_checksum 1875 __pyx_result = %(class_name)s.__new__(__pyx_type) 1876 if __pyx_state is not None: 1877 %(unpickle_func_name)s__set_state(<%(class_name)s> __pyx_result, __pyx_state) 1878 return __pyx_result 1879 1880 cdef %(unpickle_func_name)s__set_state(%(class_name)s __pyx_result, tuple __pyx_state): 1881 %(assignments)s 1882 if len(__pyx_state) > %(num_members)d and hasattr(__pyx_result, '__dict__'): 1883 __pyx_result.__dict__.update(__pyx_state[%(num_members)d]) 1884 """ % { 1885 'unpickle_func_name': unpickle_func_name, 1886 'checksum': checksum, 1887 'members': ', '.join(all_members_names), 1888 'class_name': node.class_name, 1889 'assignments': '; '.join( 1890 '__pyx_result.%s = __pyx_state[%s]' % (v, ix) 1891 for ix, v in enumerate(all_members_names)), 1892 'num_members': len(all_members_names), 1893 }, level='module', pipeline=[NormalizeTree(None)]).substitute({}) 1894 unpickle_func.analyse_declarations(node.entry.scope) 1895 self.visit(unpickle_func) 1896 self.extra_module_declarations.append(unpickle_func) 1897 1898 pickle_func = TreeFragment(u""" 1899 def __reduce_cython__(self): 1900 cdef tuple state 1901 cdef object _dict 1902 cdef bint use_setstate 1903 state = (%(members)s) 1904 _dict = getattr(self, '__dict__', None) 1905 if _dict is not None: 1906 state += (_dict,) 1907 use_setstate = True 1908 else: 1909 use_setstate = %(any_notnone_members)s 1910 if use_setstate: 1911 return %(unpickle_func_name)s, (type(self), %(checksum)s, None), state 1912 else: 1913 return %(unpickle_func_name)s, (type(self), %(checksum)s, state) 1914 1915 def __setstate_cython__(self, __pyx_state): 1916 %(unpickle_func_name)s__set_state(self, __pyx_state) 1917 """ % { 1918 'unpickle_func_name': unpickle_func_name, 1919 'checksum': checksum, 1920 'members': ', '.join('self.%s' % v for v in all_members_names) + (',' if len(all_members_names) == 1 else ''), 1921 # Even better, we could check PyType_IS_GC. 1922 'any_notnone_members' : ' or '.join(['self.%s is not None' % e.name for e in all_members if e.type.is_pyobject] or ['False']), 1923 }, 1924 level='c_class', pipeline=[NormalizeTree(None)]).substitute({}) 1925 pickle_func.analyse_declarations(node.scope) 1926 self.enter_scope(node, node.scope) # functions should be visited in the class scope 1927 self.visit(pickle_func) 1928 self.exit_scope() 1929 node.body.stats.append(pickle_func) 1930 1931 def _handle_fused_def_decorators(self, old_decorators, env, node): 1932 """ 1933 Create function calls to the decorators and reassignments to 1934 the function. 1935 """ 1936 # Delete staticmethod and classmethod decorators, this is 1937 # handled directly by the fused function object. 1938 decorators = [] 1939 for decorator in old_decorators: 1940 func = decorator.decorator 1941 if (not func.is_name or 1942 func.name not in ('staticmethod', 'classmethod') or 1943 env.lookup_here(func.name)): 1944 # not a static or classmethod 1945 decorators.append(decorator) 1946 1947 if decorators: 1948 transform = DecoratorTransform(self.context) 1949 def_node = node.node 1950 _, reassignments = transform.chain_decorators( 1951 def_node, decorators, def_node.name) 1952 reassignments.analyse_declarations(env) 1953 node = [node, reassignments] 1954 1955 return node 1956 1957 def _handle_def(self, decorators, env, node): 1958 "Handle def or cpdef fused functions" 1959 # Create PyCFunction nodes for each specialization 1960 node.stats.insert(0, node.py_func) 1961 node.py_func = self.visit(node.py_func) 1962 node.update_fused_defnode_entry(env) 1963 # For the moment, fused functions do not support METH_FASTCALL 1964 node.py_func.entry.signature.use_fastcall = False 1965 pycfunc = ExprNodes.PyCFunctionNode.from_defnode(node.py_func, binding=True) 1966 pycfunc = ExprNodes.ProxyNode(pycfunc.coerce_to_temp(env)) 1967 node.resulting_fused_function = pycfunc 1968 # Create assignment node for our def function 1969 node.fused_func_assignment = self._create_assignment( 1970 node.py_func, ExprNodes.CloneNode(pycfunc), env) 1971 1972 if decorators: 1973 node = self._handle_fused_def_decorators(decorators, env, node) 1974 1975 return node 1976 1977 def _create_fused_function(self, env, node): 1978 "Create a fused function for a DefNode with fused arguments" 1979 from . import FusedNode 1980 1981 if self.fused_function or self.in_lambda: 1982 if self.fused_function not in self.fused_error_funcs: 1983 if self.in_lambda: 1984 error(node.pos, "Fused lambdas not allowed") 1985 else: 1986 error(node.pos, "Cannot nest fused functions") 1987 1988 self.fused_error_funcs.add(self.fused_function) 1989 1990 node.body = Nodes.PassStatNode(node.pos) 1991 for arg in node.args: 1992 if arg.type.is_fused: 1993 arg.type = arg.type.get_fused_types()[0] 1994 1995 return node 1996 1997 decorators = getattr(node, 'decorators', None) 1998 node = FusedNode.FusedCFuncDefNode(node, env) 1999 self.fused_function = node 2000 self.visitchildren(node) 2001 self.fused_function = None 2002 if node.py_func: 2003 node = self._handle_def(decorators, env, node) 2004 2005 return node 2006 2007 def _handle_fused(self, node): 2008 if node.is_generator and node.has_fused_arguments: 2009 node.has_fused_arguments = False 2010 error(node.pos, "Fused generators not supported") 2011 node.gbody = Nodes.StatListNode(node.pos, 2012 stats=[], 2013 body=Nodes.PassStatNode(node.pos)) 2014 2015 return node.has_fused_arguments 2016 2017 def visit_FuncDefNode(self, node): 2018 """ 2019 Analyse a function and its body, as that hasn't happened yet. Also 2020 analyse the directive_locals set by @cython.locals(). 2021 2022 Then, if we are a function with fused arguments, replace the function 2023 (after it has declared itself in the symbol table!) with a 2024 FusedCFuncDefNode, and analyse its children (which are in turn normal 2025 functions). If we're a normal function, just analyse the body of the 2026 function. 2027 """ 2028 env = self.current_env() 2029 2030 self.seen_vars_stack.append(set()) 2031 lenv = node.local_scope 2032 node.declare_arguments(lenv) 2033 2034 # @cython.locals(...) 2035 for var, type_node in node.directive_locals.items(): 2036 if not lenv.lookup_here(var): # don't redeclare args 2037 type = type_node.analyse_as_type(lenv) 2038 if type and type.is_fused and lenv.fused_to_specific: 2039 type = type.specialize(lenv.fused_to_specific) 2040 if type: 2041 lenv.declare_var(var, type, type_node.pos) 2042 else: 2043 error(type_node.pos, "Not a type") 2044 2045 if self._handle_fused(node): 2046 node = self._create_fused_function(env, node) 2047 else: 2048 node.body.analyse_declarations(lenv) 2049 self._super_visit_FuncDefNode(node) 2050 2051 self.seen_vars_stack.pop() 2052 return node 2053 2054 def visit_DefNode(self, node): 2055 node = self.visit_FuncDefNode(node) 2056 env = self.current_env() 2057 if (not isinstance(node, Nodes.DefNode) or 2058 node.fused_py_func or node.is_generator_body or 2059 not node.needs_assignment_synthesis(env)): 2060 return node 2061 return [node, self._synthesize_assignment(node, env)] 2062 2063 def visit_GeneratorBodyDefNode(self, node): 2064 return self.visit_FuncDefNode(node) 2065 2066 def _synthesize_assignment(self, node, env): 2067 # Synthesize assignment node and put it right after defnode 2068 genv = env 2069 while genv.is_py_class_scope or genv.is_c_class_scope: 2070 genv = genv.outer_scope 2071 2072 if genv.is_closure_scope: 2073 rhs = node.py_cfunc_node = ExprNodes.InnerFunctionNode( 2074 node.pos, def_node=node, 2075 pymethdef_cname=node.entry.pymethdef_cname, 2076 code_object=ExprNodes.CodeObjectNode(node)) 2077 else: 2078 binding = self.current_directives.get('binding') 2079 rhs = ExprNodes.PyCFunctionNode.from_defnode(node, binding) 2080 node.code_object = rhs.code_object 2081 if node.is_generator: 2082 node.gbody.code_object = node.code_object 2083 2084 if env.is_py_class_scope: 2085 rhs.binding = True 2086 2087 node.is_cyfunction = rhs.binding 2088 return self._create_assignment(node, rhs, env) 2089 2090 def _create_assignment(self, def_node, rhs, env): 2091 if def_node.decorators: 2092 for decorator in def_node.decorators[::-1]: 2093 rhs = ExprNodes.SimpleCallNode( 2094 decorator.pos, 2095 function = decorator.decorator, 2096 args = [rhs]) 2097 def_node.decorators = None 2098 2099 assmt = Nodes.SingleAssignmentNode( 2100 def_node.pos, 2101 lhs=ExprNodes.NameNode(def_node.pos, name=def_node.name), 2102 rhs=rhs) 2103 assmt.analyse_declarations(env) 2104 return assmt 2105 2106 def visit_ScopedExprNode(self, node): 2107 env = self.current_env() 2108 node.analyse_declarations(env) 2109 # the node may or may not have a local scope 2110 if node.has_local_scope: 2111 self.seen_vars_stack.append(set(self.seen_vars_stack[-1])) 2112 self.enter_scope(node, node.expr_scope) 2113 node.analyse_scoped_declarations(node.expr_scope) 2114 self.visitchildren(node) 2115 self.exit_scope() 2116 self.seen_vars_stack.pop() 2117 else: 2118 node.analyse_scoped_declarations(env) 2119 self.visitchildren(node) 2120 return node 2121 2122 def visit_TempResultFromStatNode(self, node): 2123 self.visitchildren(node) 2124 node.analyse_declarations(self.current_env()) 2125 return node 2126 2127 def visit_CppClassNode(self, node): 2128 if node.visibility == 'extern': 2129 return None 2130 else: 2131 return self.visit_ClassDefNode(node) 2132 2133 def visit_CStructOrUnionDefNode(self, node): 2134 # Create a wrapper node if needed. 2135 # We want to use the struct type information (so it can't happen 2136 # before this phase) but also create new objects to be declared 2137 # (so it can't happen later). 2138 # Note that we don't return the original node, as it is 2139 # never used after this phase. 2140 if True: # private (default) 2141 return None 2142 2143 self_value = ExprNodes.AttributeNode( 2144 pos = node.pos, 2145 obj = ExprNodes.NameNode(pos=node.pos, name=u"self"), 2146 attribute = EncodedString(u"value")) 2147 var_entries = node.entry.type.scope.var_entries 2148 attributes = [] 2149 for entry in var_entries: 2150 attributes.append(ExprNodes.AttributeNode(pos = entry.pos, 2151 obj = self_value, 2152 attribute = entry.name)) 2153 # __init__ assignments 2154 init_assignments = [] 2155 for entry, attr in zip(var_entries, attributes): 2156 # TODO: branch on visibility 2157 init_assignments.append(self.init_assignment.substitute({ 2158 u"VALUE": ExprNodes.NameNode(entry.pos, name = entry.name), 2159 u"ATTR": attr, 2160 }, pos = entry.pos)) 2161 2162 # create the class 2163 str_format = u"%s(%s)" % (node.entry.type.name, ("%s, " * len(attributes))[:-2]) 2164 wrapper_class = self.struct_or_union_wrapper.substitute({ 2165 u"INIT_ASSIGNMENTS": Nodes.StatListNode(node.pos, stats = init_assignments), 2166 u"IS_UNION": ExprNodes.BoolNode(node.pos, value = not node.entry.type.is_struct), 2167 u"MEMBER_TUPLE": ExprNodes.TupleNode(node.pos, args=attributes), 2168 u"STR_FORMAT": ExprNodes.StringNode(node.pos, value = EncodedString(str_format)), 2169 u"REPR_FORMAT": ExprNodes.StringNode(node.pos, value = EncodedString(str_format.replace("%s", "%r"))), 2170 }, pos = node.pos).stats[0] 2171 wrapper_class.class_name = node.name 2172 wrapper_class.shadow = True 2173 class_body = wrapper_class.body.stats 2174 2175 # fix value type 2176 assert isinstance(class_body[0].base_type, Nodes.CSimpleBaseTypeNode) 2177 class_body[0].base_type.name = node.name 2178 2179 # fix __init__ arguments 2180 init_method = class_body[1] 2181 assert isinstance(init_method, Nodes.DefNode) and init_method.name == '__init__' 2182 arg_template = init_method.args[1] 2183 if not node.entry.type.is_struct: 2184 arg_template.kw_only = True 2185 del init_method.args[1] 2186 for entry, attr in zip(var_entries, attributes): 2187 arg = copy.deepcopy(arg_template) 2188 arg.declarator.name = entry.name 2189 init_method.args.append(arg) 2190 2191 # setters/getters 2192 for entry, attr in zip(var_entries, attributes): 2193 # TODO: branch on visibility 2194 if entry.type.is_pyobject: 2195 template = self.basic_pyobject_property 2196 else: 2197 template = self.basic_property 2198 property = template.substitute({ 2199 u"ATTR": attr, 2200 }, pos = entry.pos).stats[0] 2201 property.name = entry.name 2202 wrapper_class.body.stats.append(property) 2203 2204 wrapper_class.analyse_declarations(self.current_env()) 2205 return self.visit_CClassDefNode(wrapper_class) 2206 2207 # Some nodes are no longer needed after declaration 2208 # analysis and can be dropped. The analysis was performed 2209 # on these nodes in a separate recursive process from the 2210 # enclosing function or module, so we can simply drop them. 2211 def visit_CDeclaratorNode(self, node): 2212 # necessary to ensure that all CNameDeclaratorNodes are visited. 2213 self.visitchildren(node) 2214 return node 2215 2216 def visit_CTypeDefNode(self, node): 2217 return node 2218 2219 def visit_CBaseTypeNode(self, node): 2220 return None 2221 2222 def visit_CEnumDefNode(self, node): 2223 if node.visibility == 'public': 2224 return node 2225 else: 2226 return None 2227 2228 def visit_CNameDeclaratorNode(self, node): 2229 if node.name in self.seen_vars_stack[-1]: 2230 entry = self.current_env().lookup(node.name) 2231 if (entry is None or entry.visibility != 'extern' 2232 and not entry.scope.is_c_class_scope): 2233 error(node.pos, "cdef variable '%s' declared after it is used" % node.name) 2234 self.visitchildren(node) 2235 return node 2236 2237 def visit_CVarDefNode(self, node): 2238 # to ensure all CNameDeclaratorNodes are visited. 2239 self.visitchildren(node) 2240 return None 2241 2242 def visit_CnameDecoratorNode(self, node): 2243 child_node = self.visit(node.node) 2244 if not child_node: 2245 return None 2246 if type(child_node) is list: # Assignment synthesized 2247 node.child_node = child_node[0] 2248 return [node] + child_node[1:] 2249 node.node = child_node 2250 return node 2251 2252 def create_Property(self, entry): 2253 if entry.visibility == 'public': 2254 if entry.type.is_pyobject: 2255 template = self.basic_pyobject_property 2256 else: 2257 template = self.basic_property 2258 elif entry.visibility == 'readonly': 2259 template = self.basic_property_ro 2260 property = template.substitute({ 2261 u"ATTR": ExprNodes.AttributeNode(pos=entry.pos, 2262 obj=ExprNodes.NameNode(pos=entry.pos, name="self"), 2263 attribute=entry.name), 2264 }, pos=entry.pos).stats[0] 2265 property.name = entry.name 2266 property.doc = entry.doc 2267 return property 2268 2269 2270class CalculateQualifiedNamesTransform(EnvTransform): 2271 """ 2272 Calculate and store the '__qualname__' and the global 2273 module name on some nodes. 2274 """ 2275 def visit_ModuleNode(self, node): 2276 self.module_name = self.global_scope().qualified_name 2277 self.qualified_name = [] 2278 _super = super(CalculateQualifiedNamesTransform, self) 2279 self._super_visit_FuncDefNode = _super.visit_FuncDefNode 2280 self._super_visit_ClassDefNode = _super.visit_ClassDefNode 2281 self.visitchildren(node) 2282 return node 2283 2284 def _set_qualname(self, node, name=None): 2285 if name: 2286 qualname = self.qualified_name[:] 2287 qualname.append(name) 2288 else: 2289 qualname = self.qualified_name 2290 node.qualname = EncodedString('.'.join(qualname)) 2291 node.module_name = self.module_name 2292 2293 def _append_entry(self, entry): 2294 if entry.is_pyglobal and not entry.is_pyclass_attr: 2295 self.qualified_name = [entry.name] 2296 else: 2297 self.qualified_name.append(entry.name) 2298 2299 def visit_ClassNode(self, node): 2300 self._set_qualname(node, node.name) 2301 self.visitchildren(node) 2302 return node 2303 2304 def visit_PyClassNamespaceNode(self, node): 2305 # class name was already added by parent node 2306 self._set_qualname(node) 2307 self.visitchildren(node) 2308 return node 2309 2310 def visit_PyCFunctionNode(self, node): 2311 orig_qualified_name = self.qualified_name[:] 2312 if node.def_node.is_wrapper and self.qualified_name and self.qualified_name[-1] == '<locals>': 2313 self.qualified_name.pop() 2314 self._set_qualname(node) 2315 else: 2316 self._set_qualname(node, node.def_node.name) 2317 self.visitchildren(node) 2318 self.qualified_name = orig_qualified_name 2319 return node 2320 2321 def visit_DefNode(self, node): 2322 if node.is_wrapper and self.qualified_name: 2323 assert self.qualified_name[-1] == '<locals>', self.qualified_name 2324 orig_qualified_name = self.qualified_name[:] 2325 self.qualified_name.pop() 2326 self._set_qualname(node) 2327 self._super_visit_FuncDefNode(node) 2328 self.qualified_name = orig_qualified_name 2329 else: 2330 self._set_qualname(node, node.name) 2331 self.visit_FuncDefNode(node) 2332 return node 2333 2334 def visit_FuncDefNode(self, node): 2335 orig_qualified_name = self.qualified_name[:] 2336 if getattr(node, 'name', None) == '<lambda>': 2337 self.qualified_name.append('<lambda>') 2338 else: 2339 self._append_entry(node.entry) 2340 self.qualified_name.append('<locals>') 2341 self._super_visit_FuncDefNode(node) 2342 self.qualified_name = orig_qualified_name 2343 return node 2344 2345 def visit_ClassDefNode(self, node): 2346 orig_qualified_name = self.qualified_name[:] 2347 entry = (getattr(node, 'entry', None) or # PyClass 2348 self.current_env().lookup_here(node.target.name)) # CClass 2349 self._append_entry(entry) 2350 self._super_visit_ClassDefNode(node) 2351 self.qualified_name = orig_qualified_name 2352 return node 2353 2354 2355class AnalyseExpressionsTransform(CythonTransform): 2356 2357 def visit_ModuleNode(self, node): 2358 node.scope.infer_types() 2359 node.body = node.body.analyse_expressions(node.scope) 2360 self.visitchildren(node) 2361 return node 2362 2363 def visit_FuncDefNode(self, node): 2364 node.local_scope.infer_types() 2365 node.body = node.body.analyse_expressions(node.local_scope) 2366 self.visitchildren(node) 2367 return node 2368 2369 def visit_ScopedExprNode(self, node): 2370 if node.has_local_scope: 2371 node.expr_scope.infer_types() 2372 node = node.analyse_scoped_expressions(node.expr_scope) 2373 self.visitchildren(node) 2374 return node 2375 2376 def visit_IndexNode(self, node): 2377 """ 2378 Replace index nodes used to specialize cdef functions with fused 2379 argument types with the Attribute- or NameNode referring to the 2380 function. We then need to copy over the specialization properties to 2381 the attribute or name node. 2382 2383 Because the indexing might be a Python indexing operation on a fused 2384 function, or (usually) a Cython indexing operation, we need to 2385 re-analyse the types. 2386 """ 2387 self.visit_Node(node) 2388 if node.is_fused_index and not node.type.is_error: 2389 node = node.base 2390 return node 2391 2392 2393class FindInvalidUseOfFusedTypes(CythonTransform): 2394 2395 def visit_FuncDefNode(self, node): 2396 # Errors related to use in functions with fused args will already 2397 # have been detected 2398 if not node.has_fused_arguments: 2399 if not node.is_generator_body and node.return_type.is_fused: 2400 error(node.pos, "Return type is not specified as argument type") 2401 else: 2402 self.visitchildren(node) 2403 2404 return node 2405 2406 def visit_ExprNode(self, node): 2407 if node.type and node.type.is_fused: 2408 error(node.pos, "Invalid use of fused types, type cannot be specialized") 2409 else: 2410 self.visitchildren(node) 2411 2412 return node 2413 2414 2415class ExpandInplaceOperators(EnvTransform): 2416 2417 def visit_InPlaceAssignmentNode(self, node): 2418 lhs = node.lhs 2419 rhs = node.rhs 2420 if lhs.type.is_cpp_class: 2421 # No getting around this exact operator here. 2422 return node 2423 if isinstance(lhs, ExprNodes.BufferIndexNode): 2424 # There is code to handle this case in InPlaceAssignmentNode 2425 return node 2426 2427 env = self.current_env() 2428 def side_effect_free_reference(node, setting=False): 2429 if node.is_name: 2430 return node, [] 2431 elif node.type.is_pyobject and not setting: 2432 node = LetRefNode(node) 2433 return node, [node] 2434 elif node.is_subscript: 2435 base, temps = side_effect_free_reference(node.base) 2436 index = LetRefNode(node.index) 2437 return ExprNodes.IndexNode(node.pos, base=base, index=index), temps + [index] 2438 elif node.is_attribute: 2439 obj, temps = side_effect_free_reference(node.obj) 2440 return ExprNodes.AttributeNode(node.pos, obj=obj, attribute=node.attribute), temps 2441 elif isinstance(node, ExprNodes.BufferIndexNode): 2442 raise ValueError("Don't allow things like attributes of buffer indexing operations") 2443 else: 2444 node = LetRefNode(node) 2445 return node, [node] 2446 try: 2447 lhs, let_ref_nodes = side_effect_free_reference(lhs, setting=True) 2448 except ValueError: 2449 return node 2450 dup = lhs.__class__(**lhs.__dict__) 2451 binop = ExprNodes.binop_node(node.pos, 2452 operator = node.operator, 2453 operand1 = dup, 2454 operand2 = rhs, 2455 inplace=True) 2456 # Manually analyse types for new node. 2457 lhs = lhs.analyse_target_types(env) 2458 dup.analyse_types(env) # FIXME: no need to reanalyse the copy, right? 2459 binop.analyse_operation(env) 2460 node = Nodes.SingleAssignmentNode( 2461 node.pos, 2462 lhs = lhs, 2463 rhs=binop.coerce_to(lhs.type, env)) 2464 # Use LetRefNode to avoid side effects. 2465 let_ref_nodes.reverse() 2466 for t in let_ref_nodes: 2467 node = LetNode(t, node) 2468 return node 2469 2470 def visit_ExprNode(self, node): 2471 # In-place assignments can't happen within an expression. 2472 return node 2473 2474 2475class AdjustDefByDirectives(CythonTransform, SkipDeclarations): 2476 """ 2477 Adjust function and class definitions by the decorator directives: 2478 2479 @cython.cfunc 2480 @cython.cclass 2481 @cython.ccall 2482 @cython.inline 2483 @cython.nogil 2484 """ 2485 2486 def visit_ModuleNode(self, node): 2487 self.directives = node.directives 2488 self.in_py_class = False 2489 self.visitchildren(node) 2490 return node 2491 2492 def visit_CompilerDirectivesNode(self, node): 2493 old_directives = self.directives 2494 self.directives = node.directives 2495 self.visitchildren(node) 2496 self.directives = old_directives 2497 return node 2498 2499 def visit_DefNode(self, node): 2500 modifiers = [] 2501 if 'inline' in self.directives: 2502 modifiers.append('inline') 2503 nogil = self.directives.get('nogil') 2504 except_val = self.directives.get('exceptval') 2505 return_type_node = self.directives.get('returns') 2506 if return_type_node is None and self.directives['annotation_typing']: 2507 return_type_node = node.return_type_annotation 2508 # for Python annotations, prefer safe exception handling by default 2509 if return_type_node is not None and except_val is None: 2510 except_val = (None, True) # except * 2511 elif except_val is None: 2512 # backward compatible default: no exception check, unless there's also a "@returns" declaration 2513 except_val = (None, True if return_type_node else False) 2514 if 'ccall' in self.directives: 2515 node = node.as_cfunction( 2516 overridable=True, modifiers=modifiers, nogil=nogil, 2517 returns=return_type_node, except_val=except_val) 2518 return self.visit(node) 2519 if 'cfunc' in self.directives: 2520 if self.in_py_class: 2521 error(node.pos, "cfunc directive is not allowed here") 2522 else: 2523 node = node.as_cfunction( 2524 overridable=False, modifiers=modifiers, nogil=nogil, 2525 returns=return_type_node, except_val=except_val) 2526 return self.visit(node) 2527 if 'inline' in modifiers: 2528 error(node.pos, "Python functions cannot be declared 'inline'") 2529 if nogil: 2530 # TODO: turn this into a "with gil" declaration. 2531 error(node.pos, "Python functions cannot be declared 'nogil'") 2532 self.visitchildren(node) 2533 return node 2534 2535 def visit_LambdaNode(self, node): 2536 # No directives should modify lambdas or generator expressions (and also nothing in them). 2537 return node 2538 2539 def visit_PyClassDefNode(self, node): 2540 if 'cclass' in self.directives: 2541 node = node.as_cclass() 2542 return self.visit(node) 2543 else: 2544 old_in_pyclass = self.in_py_class 2545 self.in_py_class = True 2546 self.visitchildren(node) 2547 self.in_py_class = old_in_pyclass 2548 return node 2549 2550 def visit_CClassDefNode(self, node): 2551 old_in_pyclass = self.in_py_class 2552 self.in_py_class = False 2553 self.visitchildren(node) 2554 self.in_py_class = old_in_pyclass 2555 return node 2556 2557 2558class AlignFunctionDefinitions(CythonTransform): 2559 """ 2560 This class takes the signatures from a .pxd file and applies them to 2561 the def methods in a .py file. 2562 """ 2563 2564 def visit_ModuleNode(self, node): 2565 self.scope = node.scope 2566 self.visitchildren(node) 2567 return node 2568 2569 def visit_PyClassDefNode(self, node): 2570 pxd_def = self.scope.lookup(node.name) 2571 if pxd_def: 2572 if pxd_def.is_cclass: 2573 return self.visit_CClassDefNode(node.as_cclass(), pxd_def) 2574 elif not pxd_def.scope or not pxd_def.scope.is_builtin_scope: 2575 error(node.pos, "'%s' redeclared" % node.name) 2576 if pxd_def.pos: 2577 error(pxd_def.pos, "previous declaration here") 2578 return None 2579 return node 2580 2581 def visit_CClassDefNode(self, node, pxd_def=None): 2582 if pxd_def is None: 2583 pxd_def = self.scope.lookup(node.class_name) 2584 if pxd_def: 2585 if not pxd_def.defined_in_pxd: 2586 return node 2587 outer_scope = self.scope 2588 self.scope = pxd_def.type.scope 2589 self.visitchildren(node) 2590 if pxd_def: 2591 self.scope = outer_scope 2592 return node 2593 2594 def visit_DefNode(self, node): 2595 pxd_def = self.scope.lookup(node.name) 2596 if pxd_def and (not pxd_def.scope or not pxd_def.scope.is_builtin_scope): 2597 if not pxd_def.is_cfunction: 2598 error(node.pos, "'%s' redeclared" % node.name) 2599 if pxd_def.pos: 2600 error(pxd_def.pos, "previous declaration here") 2601 return None 2602 node = node.as_cfunction(pxd_def) 2603 # Enable this when nested cdef functions are allowed. 2604 # self.visitchildren(node) 2605 return node 2606 2607 def visit_ExprNode(self, node): 2608 # ignore lambdas and everything else that appears in expressions 2609 return node 2610 2611 2612class AutoCpdefFunctionDefinitions(CythonTransform): 2613 2614 def visit_ModuleNode(self, node): 2615 self.directives = node.directives 2616 self.imported_names = set() # hack, see visit_FromImportStatNode() 2617 self.scope = node.scope 2618 self.visitchildren(node) 2619 return node 2620 2621 def visit_DefNode(self, node): 2622 if (self.scope.is_module_scope and self.directives['auto_cpdef'] 2623 and node.name not in self.imported_names 2624 and node.is_cdef_func_compatible()): 2625 # FIXME: cpdef-ing should be done in analyse_declarations() 2626 node = node.as_cfunction(scope=self.scope) 2627 return node 2628 2629 def visit_CClassDefNode(self, node, pxd_def=None): 2630 if pxd_def is None: 2631 pxd_def = self.scope.lookup(node.class_name) 2632 if pxd_def: 2633 if not pxd_def.defined_in_pxd: 2634 return node 2635 outer_scope = self.scope 2636 self.scope = pxd_def.type.scope 2637 self.visitchildren(node) 2638 if pxd_def: 2639 self.scope = outer_scope 2640 return node 2641 2642 def visit_FromImportStatNode(self, node): 2643 # hack to prevent conditional import fallback functions from 2644 # being cdpef-ed (global Python variables currently conflict 2645 # with imports) 2646 if self.scope.is_module_scope: 2647 for name, _ in node.items: 2648 self.imported_names.add(name) 2649 return node 2650 2651 def visit_ExprNode(self, node): 2652 # ignore lambdas and everything else that appears in expressions 2653 return node 2654 2655 2656class RemoveUnreachableCode(CythonTransform): 2657 def visit_StatListNode(self, node): 2658 if not self.current_directives['remove_unreachable']: 2659 return node 2660 self.visitchildren(node) 2661 for idx, stat in enumerate(node.stats): 2662 idx += 1 2663 if stat.is_terminator: 2664 if idx < len(node.stats): 2665 if self.current_directives['warn.unreachable']: 2666 warning(node.stats[idx].pos, "Unreachable code", 2) 2667 node.stats = node.stats[:idx] 2668 node.is_terminator = True 2669 break 2670 return node 2671 2672 def visit_IfClauseNode(self, node): 2673 self.visitchildren(node) 2674 if node.body.is_terminator: 2675 node.is_terminator = True 2676 return node 2677 2678 def visit_IfStatNode(self, node): 2679 self.visitchildren(node) 2680 if node.else_clause and node.else_clause.is_terminator: 2681 for clause in node.if_clauses: 2682 if not clause.is_terminator: 2683 break 2684 else: 2685 node.is_terminator = True 2686 return node 2687 2688 def visit_TryExceptStatNode(self, node): 2689 self.visitchildren(node) 2690 if node.body.is_terminator and node.else_clause: 2691 if self.current_directives['warn.unreachable']: 2692 warning(node.else_clause.pos, "Unreachable code", 2) 2693 node.else_clause = None 2694 return node 2695 2696 def visit_TryFinallyStatNode(self, node): 2697 self.visitchildren(node) 2698 if node.finally_clause.is_terminator: 2699 node.is_terminator = True 2700 return node 2701 2702 2703class YieldNodeCollector(TreeVisitor): 2704 2705 def __init__(self): 2706 super(YieldNodeCollector, self).__init__() 2707 self.yields = [] 2708 self.returns = [] 2709 self.finallys = [] 2710 self.excepts = [] 2711 self.has_return_value = False 2712 self.has_yield = False 2713 self.has_await = False 2714 2715 def visit_Node(self, node): 2716 self.visitchildren(node) 2717 2718 def visit_YieldExprNode(self, node): 2719 self.yields.append(node) 2720 self.has_yield = True 2721 self.visitchildren(node) 2722 2723 def visit_AwaitExprNode(self, node): 2724 self.yields.append(node) 2725 self.has_await = True 2726 self.visitchildren(node) 2727 2728 def visit_ReturnStatNode(self, node): 2729 self.visitchildren(node) 2730 if node.value: 2731 self.has_return_value = True 2732 self.returns.append(node) 2733 2734 def visit_TryFinallyStatNode(self, node): 2735 self.visitchildren(node) 2736 self.finallys.append(node) 2737 2738 def visit_TryExceptStatNode(self, node): 2739 self.visitchildren(node) 2740 self.excepts.append(node) 2741 2742 def visit_ClassDefNode(self, node): 2743 pass 2744 2745 def visit_FuncDefNode(self, node): 2746 pass 2747 2748 def visit_LambdaNode(self, node): 2749 pass 2750 2751 def visit_GeneratorExpressionNode(self, node): 2752 pass 2753 2754 def visit_CArgDeclNode(self, node): 2755 # do not look into annotations 2756 # FIXME: support (yield) in default arguments (currently crashes) 2757 pass 2758 2759 2760class MarkClosureVisitor(CythonTransform): 2761 2762 def visit_ModuleNode(self, node): 2763 self.needs_closure = False 2764 self.visitchildren(node) 2765 return node 2766 2767 def visit_FuncDefNode(self, node): 2768 self.needs_closure = False 2769 self.visitchildren(node) 2770 node.needs_closure = self.needs_closure 2771 self.needs_closure = True 2772 2773 collector = YieldNodeCollector() 2774 collector.visitchildren(node) 2775 2776 if node.is_async_def: 2777 coroutine_type = Nodes.AsyncDefNode 2778 if collector.has_yield: 2779 coroutine_type = Nodes.AsyncGenNode 2780 for yield_expr in collector.yields + collector.returns: 2781 yield_expr.in_async_gen = True 2782 elif self.current_directives['iterable_coroutine']: 2783 coroutine_type = Nodes.IterableAsyncDefNode 2784 elif collector.has_await: 2785 found = next(y for y in collector.yields if y.is_await) 2786 error(found.pos, "'await' not allowed in generators (use 'yield')") 2787 return node 2788 elif collector.has_yield: 2789 coroutine_type = Nodes.GeneratorDefNode 2790 else: 2791 return node 2792 2793 for i, yield_expr in enumerate(collector.yields, 1): 2794 yield_expr.label_num = i 2795 for retnode in collector.returns + collector.finallys + collector.excepts: 2796 retnode.in_generator = True 2797 2798 gbody = Nodes.GeneratorBodyDefNode( 2799 pos=node.pos, name=node.name, body=node.body, 2800 is_async_gen_body=node.is_async_def and collector.has_yield) 2801 coroutine = coroutine_type( 2802 pos=node.pos, name=node.name, args=node.args, 2803 star_arg=node.star_arg, starstar_arg=node.starstar_arg, 2804 doc=node.doc, decorators=node.decorators, 2805 gbody=gbody, lambda_name=node.lambda_name, 2806 return_type_annotation=node.return_type_annotation) 2807 return coroutine 2808 2809 def visit_CFuncDefNode(self, node): 2810 self.needs_closure = False 2811 self.visitchildren(node) 2812 node.needs_closure = self.needs_closure 2813 self.needs_closure = True 2814 if node.needs_closure and node.overridable: 2815 error(node.pos, "closures inside cpdef functions not yet supported") 2816 return node 2817 2818 def visit_LambdaNode(self, node): 2819 self.needs_closure = False 2820 self.visitchildren(node) 2821 node.needs_closure = self.needs_closure 2822 self.needs_closure = True 2823 return node 2824 2825 def visit_ClassDefNode(self, node): 2826 self.visitchildren(node) 2827 self.needs_closure = True 2828 return node 2829 2830 2831class CreateClosureClasses(CythonTransform): 2832 # Output closure classes in module scope for all functions 2833 # that really need it. 2834 2835 def __init__(self, context): 2836 super(CreateClosureClasses, self).__init__(context) 2837 self.path = [] 2838 self.in_lambda = False 2839 2840 def visit_ModuleNode(self, node): 2841 self.module_scope = node.scope 2842 self.visitchildren(node) 2843 return node 2844 2845 def find_entries_used_in_closures(self, node): 2846 from_closure = [] 2847 in_closure = [] 2848 for scope in node.local_scope.iter_local_scopes(): 2849 for name, entry in scope.entries.items(): 2850 if not name: 2851 continue 2852 if entry.from_closure: 2853 from_closure.append((name, entry)) 2854 elif entry.in_closure: 2855 in_closure.append((name, entry)) 2856 return from_closure, in_closure 2857 2858 def create_class_from_scope(self, node, target_module_scope, inner_node=None): 2859 # move local variables into closure 2860 if node.is_generator: 2861 for scope in node.local_scope.iter_local_scopes(): 2862 for entry in scope.entries.values(): 2863 if not (entry.from_closure or entry.is_pyglobal or entry.is_cglobal): 2864 entry.in_closure = True 2865 2866 from_closure, in_closure = self.find_entries_used_in_closures(node) 2867 in_closure.sort() 2868 2869 # Now from the beginning 2870 node.needs_closure = False 2871 node.needs_outer_scope = False 2872 2873 func_scope = node.local_scope 2874 cscope = node.entry.scope 2875 while cscope.is_py_class_scope or cscope.is_c_class_scope: 2876 cscope = cscope.outer_scope 2877 2878 if not from_closure and (self.path or inner_node): 2879 if not inner_node: 2880 if not node.py_cfunc_node: 2881 raise InternalError("DefNode does not have assignment node") 2882 inner_node = node.py_cfunc_node 2883 inner_node.needs_closure_code = False 2884 node.needs_outer_scope = False 2885 2886 if node.is_generator: 2887 pass 2888 elif not in_closure and not from_closure: 2889 return 2890 elif not in_closure: 2891 func_scope.is_passthrough = True 2892 func_scope.scope_class = cscope.scope_class 2893 node.needs_outer_scope = True 2894 return 2895 2896 # entry.cname can contain periods (eg. a derived C method of a class). 2897 # We want to use the cname as part of a C struct name, so we replace 2898 # periods with double underscores. 2899 as_name = '%s_%s' % ( 2900 target_module_scope.next_id(Naming.closure_class_prefix), 2901 node.entry.cname.replace('.','__')) 2902 as_name = EncodedString(as_name) 2903 2904 entry = target_module_scope.declare_c_class( 2905 name=as_name, pos=node.pos, defining=True, 2906 implementing=True) 2907 entry.type.is_final_type = True 2908 2909 func_scope.scope_class = entry 2910 class_scope = entry.type.scope 2911 class_scope.is_internal = True 2912 class_scope.is_closure_class_scope = True 2913 if node.is_async_def or node.is_generator: 2914 # Generators need their closure intact during cleanup as they resume to handle GeneratorExit 2915 class_scope.directives['no_gc_clear'] = True 2916 if Options.closure_freelist_size: 2917 class_scope.directives['freelist'] = Options.closure_freelist_size 2918 2919 if from_closure: 2920 assert cscope.is_closure_scope 2921 class_scope.declare_var(pos=node.pos, 2922 name=Naming.outer_scope_cname, 2923 cname=Naming.outer_scope_cname, 2924 type=cscope.scope_class.type, 2925 is_cdef=True) 2926 node.needs_outer_scope = True 2927 for name, entry in in_closure: 2928 closure_entry = class_scope.declare_var( 2929 pos=entry.pos, 2930 name=entry.name if not entry.in_subscope else None, 2931 cname=entry.cname, 2932 type=entry.type, 2933 is_cdef=True) 2934 if entry.is_declared_generic: 2935 closure_entry.is_declared_generic = 1 2936 node.needs_closure = True 2937 # Do it here because other classes are already checked 2938 target_module_scope.check_c_class(func_scope.scope_class) 2939 2940 def visit_LambdaNode(self, node): 2941 if not isinstance(node.def_node, Nodes.DefNode): 2942 # fused function, an error has been previously issued 2943 return node 2944 2945 was_in_lambda = self.in_lambda 2946 self.in_lambda = True 2947 self.create_class_from_scope(node.def_node, self.module_scope, node) 2948 self.visitchildren(node) 2949 self.in_lambda = was_in_lambda 2950 return node 2951 2952 def visit_FuncDefNode(self, node): 2953 if self.in_lambda: 2954 self.visitchildren(node) 2955 return node 2956 if node.needs_closure or self.path: 2957 self.create_class_from_scope(node, self.module_scope) 2958 self.path.append(node) 2959 self.visitchildren(node) 2960 self.path.pop() 2961 return node 2962 2963 def visit_GeneratorBodyDefNode(self, node): 2964 self.visitchildren(node) 2965 return node 2966 2967 def visit_CFuncDefNode(self, node): 2968 if not node.overridable: 2969 return self.visit_FuncDefNode(node) 2970 else: 2971 self.visitchildren(node) 2972 return node 2973 2974 2975class InjectGilHandling(VisitorTransform, SkipDeclarations): 2976 """ 2977 Allow certain Python operations inside of nogil blocks by implicitly acquiring the GIL. 2978 2979 Must run before the AnalyseDeclarationsTransform to make sure the GILStatNodes get 2980 set up, parallel sections know that the GIL is acquired inside of them, etc. 2981 """ 2982 nogil = False 2983 2984 # special node handling 2985 2986 def _inject_gil_in_nogil(self, node): 2987 """Allow the (Python statement) node in nogil sections by wrapping it in a 'with gil' block.""" 2988 if self.nogil: 2989 node = Nodes.GILStatNode(node.pos, state='gil', body=node) 2990 return node 2991 2992 visit_RaiseStatNode = _inject_gil_in_nogil 2993 visit_PrintStatNode = _inject_gil_in_nogil # sadly, not the function 2994 2995 # further candidates: 2996 # def visit_ReraiseStatNode(self, node): 2997 2998 # nogil tracking 2999 3000 def visit_GILStatNode(self, node): 3001 was_nogil = self.nogil 3002 self.nogil = (node.state == 'nogil') 3003 self.visitchildren(node) 3004 self.nogil = was_nogil 3005 return node 3006 3007 def visit_CFuncDefNode(self, node): 3008 was_nogil = self.nogil 3009 if isinstance(node.declarator, Nodes.CFuncDeclaratorNode): 3010 self.nogil = node.declarator.nogil and not node.declarator.with_gil 3011 self.visitchildren(node) 3012 self.nogil = was_nogil 3013 return node 3014 3015 def visit_ParallelRangeNode(self, node): 3016 was_nogil = self.nogil 3017 self.nogil = node.nogil 3018 self.visitchildren(node) 3019 self.nogil = was_nogil 3020 return node 3021 3022 def visit_ExprNode(self, node): 3023 # No special GIL handling inside of expressions for now. 3024 return node 3025 3026 visit_Node = VisitorTransform.recurse_to_children 3027 3028 3029class GilCheck(VisitorTransform): 3030 """ 3031 Call `node.gil_check(env)` on each node to make sure we hold the 3032 GIL when we need it. Raise an error when on Python operations 3033 inside a `nogil` environment. 3034 3035 Additionally, raise exceptions for closely nested with gil or with nogil 3036 statements. The latter would abort Python. 3037 """ 3038 3039 def __call__(self, root): 3040 self.env_stack = [root.scope] 3041 self.nogil = False 3042 3043 # True for 'cdef func() nogil:' functions, as the GIL may be held while 3044 # calling this function (thus contained 'nogil' blocks may be valid). 3045 self.nogil_declarator_only = False 3046 return super(GilCheck, self).__call__(root) 3047 3048 def _visit_scoped_children(self, node, gil_state): 3049 was_nogil = self.nogil 3050 outer_attrs = node.outer_attrs 3051 if outer_attrs and len(self.env_stack) > 1: 3052 self.nogil = self.env_stack[-2].nogil 3053 self.visitchildren(node, outer_attrs) 3054 3055 self.nogil = gil_state 3056 self.visitchildren(node, attrs=None, exclude=outer_attrs) 3057 self.nogil = was_nogil 3058 3059 def visit_FuncDefNode(self, node): 3060 self.env_stack.append(node.local_scope) 3061 inner_nogil = node.local_scope.nogil 3062 3063 if inner_nogil: 3064 self.nogil_declarator_only = True 3065 3066 if inner_nogil and node.nogil_check: 3067 node.nogil_check(node.local_scope) 3068 3069 self._visit_scoped_children(node, inner_nogil) 3070 3071 # This cannot be nested, so it doesn't need backup/restore 3072 self.nogil_declarator_only = False 3073 3074 self.env_stack.pop() 3075 return node 3076 3077 def visit_GILStatNode(self, node): 3078 if node.condition is not None: 3079 error(node.condition.pos, 3080 "Non-constant condition in a " 3081 "`with %s(<condition>)` statement" % node.state) 3082 return node 3083 3084 if self.nogil and node.nogil_check: 3085 node.nogil_check() 3086 3087 was_nogil = self.nogil 3088 is_nogil = (node.state == 'nogil') 3089 3090 if was_nogil == is_nogil and not self.nogil_declarator_only: 3091 if not was_nogil: 3092 error(node.pos, "Trying to acquire the GIL while it is " 3093 "already held.") 3094 else: 3095 error(node.pos, "Trying to release the GIL while it was " 3096 "previously released.") 3097 3098 if isinstance(node.finally_clause, Nodes.StatListNode): 3099 # The finally clause of the GILStatNode is a GILExitNode, 3100 # which is wrapped in a StatListNode. Just unpack that. 3101 node.finally_clause, = node.finally_clause.stats 3102 3103 self._visit_scoped_children(node, is_nogil) 3104 return node 3105 3106 def visit_ParallelRangeNode(self, node): 3107 if node.nogil: 3108 node.nogil = False 3109 node = Nodes.GILStatNode(node.pos, state='nogil', body=node) 3110 return self.visit_GILStatNode(node) 3111 3112 if not self.nogil: 3113 error(node.pos, "prange() can only be used without the GIL") 3114 # Forget about any GIL-related errors that may occur in the body 3115 return None 3116 3117 node.nogil_check(self.env_stack[-1]) 3118 self.visitchildren(node) 3119 return node 3120 3121 def visit_ParallelWithBlockNode(self, node): 3122 if not self.nogil: 3123 error(node.pos, "The parallel section may only be used without " 3124 "the GIL") 3125 return None 3126 3127 if node.nogil_check: 3128 # It does not currently implement this, but test for it anyway to 3129 # avoid potential future surprises 3130 node.nogil_check(self.env_stack[-1]) 3131 3132 self.visitchildren(node) 3133 return node 3134 3135 def visit_TryFinallyStatNode(self, node): 3136 """ 3137 Take care of try/finally statements in nogil code sections. 3138 """ 3139 if not self.nogil or isinstance(node, Nodes.GILStatNode): 3140 return self.visit_Node(node) 3141 3142 node.nogil_check = None 3143 node.is_try_finally_in_nogil = True 3144 self.visitchildren(node) 3145 return node 3146 3147 def visit_Node(self, node): 3148 if self.env_stack and self.nogil and node.nogil_check: 3149 node.nogil_check(self.env_stack[-1]) 3150 if node.outer_attrs: 3151 self._visit_scoped_children(node, self.nogil) 3152 else: 3153 self.visitchildren(node) 3154 if self.nogil: 3155 node.in_nogil_context = True 3156 return node 3157 3158 3159class CoerceCppTemps(EnvTransform, SkipDeclarations): 3160 """ 3161 For temporary expression that are implemented using std::optional it's necessary the temps are 3162 assigned using `__pyx_t_x = value;` but accessed using `something = (*__pyx_t_x)`. This transform 3163 inserts a coercion node to take care of this, and runs absolutely last (once nothing else can be 3164 inserted into the tree) 3165 3166 TODO: a possible alternative would be to split ExprNode.result() into ExprNode.rhs_rhs() and ExprNode.lhs_rhs()??? 3167 """ 3168 def visit_ModuleNode(self, node): 3169 if self.current_env().cpp: 3170 # skipping this makes it essentially free for C files 3171 self.visitchildren(node) 3172 return node 3173 3174 def visit_ExprNode(self, node): 3175 self.visitchildren(node) 3176 if (self.current_env().directives['cpp_locals'] and 3177 node.is_temp and node.type.is_cpp_class and 3178 # Fake references are not replaced with "std::optional()". 3179 not node.type.is_fake_reference): 3180 node = ExprNodes.CppOptionalTempCoercion(node) 3181 3182 return node 3183 3184 3185class TransformBuiltinMethods(EnvTransform): 3186 """ 3187 Replace Cython's own cython.* builtins by the corresponding tree nodes. 3188 """ 3189 3190 def visit_SingleAssignmentNode(self, node): 3191 if node.declaration_only: 3192 return None 3193 else: 3194 self.visitchildren(node) 3195 return node 3196 3197 def visit_AttributeNode(self, node): 3198 self.visitchildren(node) 3199 return self.visit_cython_attribute(node) 3200 3201 def visit_NameNode(self, node): 3202 return self.visit_cython_attribute(node) 3203 3204 def visit_cython_attribute(self, node): 3205 attribute = node.as_cython_attribute() 3206 if attribute: 3207 if attribute == u'__version__': 3208 from .. import __version__ as version 3209 node = ExprNodes.StringNode(node.pos, value=EncodedString(version)) 3210 elif attribute == u'NULL': 3211 node = ExprNodes.NullNode(node.pos) 3212 elif attribute in (u'set', u'frozenset', u'staticmethod'): 3213 node = ExprNodes.NameNode(node.pos, name=EncodedString(attribute), 3214 entry=self.current_env().builtin_scope().lookup_here(attribute)) 3215 elif PyrexTypes.parse_basic_type(attribute): 3216 pass 3217 elif self.context.cython_scope.lookup_qualified_name(attribute): 3218 pass 3219 else: 3220 error(node.pos, u"'%s' not a valid cython attribute or is being used incorrectly" % attribute) 3221 return node 3222 3223 def visit_ExecStatNode(self, node): 3224 lenv = self.current_env() 3225 self.visitchildren(node) 3226 if len(node.args) == 1: 3227 node.args.append(ExprNodes.GlobalsExprNode(node.pos)) 3228 if not lenv.is_module_scope: 3229 node.args.append( 3230 ExprNodes.LocalsExprNode( 3231 node.pos, self.current_scope_node(), lenv)) 3232 return node 3233 3234 def _inject_locals(self, node, func_name): 3235 # locals()/dir()/vars() builtins 3236 lenv = self.current_env() 3237 entry = lenv.lookup_here(func_name) 3238 if entry: 3239 # not the builtin 3240 return node 3241 pos = node.pos 3242 if func_name in ('locals', 'vars'): 3243 if func_name == 'locals' and len(node.args) > 0: 3244 error(self.pos, "Builtin 'locals()' called with wrong number of args, expected 0, got %d" 3245 % len(node.args)) 3246 return node 3247 elif func_name == 'vars': 3248 if len(node.args) > 1: 3249 error(self.pos, "Builtin 'vars()' called with wrong number of args, expected 0-1, got %d" 3250 % len(node.args)) 3251 if len(node.args) > 0: 3252 return node # nothing to do 3253 return ExprNodes.LocalsExprNode(pos, self.current_scope_node(), lenv) 3254 else: # dir() 3255 if len(node.args) > 1: 3256 error(self.pos, "Builtin 'dir()' called with wrong number of args, expected 0-1, got %d" 3257 % len(node.args)) 3258 if len(node.args) > 0: 3259 # optimised in Builtin.py 3260 return node 3261 if lenv.is_py_class_scope or lenv.is_module_scope: 3262 if lenv.is_py_class_scope: 3263 pyclass = self.current_scope_node() 3264 locals_dict = ExprNodes.CloneNode(pyclass.dict) 3265 else: 3266 locals_dict = ExprNodes.GlobalsExprNode(pos) 3267 return ExprNodes.SortedDictKeysNode(locals_dict) 3268 local_names = sorted(var.name for var in lenv.entries.values() if var.name) 3269 items = [ExprNodes.IdentifierStringNode(pos, value=var) 3270 for var in local_names] 3271 return ExprNodes.ListNode(pos, args=items) 3272 3273 def visit_PrimaryCmpNode(self, node): 3274 # special case: for in/not-in test, we do not need to sort locals() 3275 self.visitchildren(node) 3276 if node.operator in 'not_in': # in/not_in 3277 if isinstance(node.operand2, ExprNodes.SortedDictKeysNode): 3278 arg = node.operand2.arg 3279 if isinstance(arg, ExprNodes.NoneCheckNode): 3280 arg = arg.arg 3281 node.operand2 = arg 3282 return node 3283 3284 def visit_CascadedCmpNode(self, node): 3285 return self.visit_PrimaryCmpNode(node) 3286 3287 def _inject_eval(self, node, func_name): 3288 lenv = self.current_env() 3289 entry = lenv.lookup(func_name) 3290 if len(node.args) != 1 or (entry and not entry.is_builtin): 3291 return node 3292 # Inject globals and locals 3293 node.args.append(ExprNodes.GlobalsExprNode(node.pos)) 3294 if not lenv.is_module_scope: 3295 node.args.append( 3296 ExprNodes.LocalsExprNode( 3297 node.pos, self.current_scope_node(), lenv)) 3298 return node 3299 3300 def _inject_super(self, node, func_name): 3301 lenv = self.current_env() 3302 entry = lenv.lookup_here(func_name) 3303 if entry or node.args: 3304 return node 3305 # Inject no-args super 3306 def_node = self.current_scope_node() 3307 if not isinstance(def_node, Nodes.DefNode) or not def_node.args or len(self.env_stack) < 2: 3308 return node 3309 class_node, class_scope = self.env_stack[-2] 3310 if class_scope.is_py_class_scope: 3311 def_node.requires_classobj = True 3312 class_node.class_cell.is_active = True 3313 node.args = [ 3314 ExprNodes.ClassCellNode( 3315 node.pos, is_generator=def_node.is_generator), 3316 ExprNodes.NameNode(node.pos, name=def_node.args[0].name) 3317 ] 3318 elif class_scope.is_c_class_scope: 3319 node.args = [ 3320 ExprNodes.NameNode( 3321 node.pos, name=class_node.scope.name, 3322 entry=class_node.entry), 3323 ExprNodes.NameNode(node.pos, name=def_node.args[0].name) 3324 ] 3325 return node 3326 3327 def visit_SimpleCallNode(self, node): 3328 # cython.foo 3329 function = node.function.as_cython_attribute() 3330 if function: 3331 if function in InterpretCompilerDirectives.unop_method_nodes: 3332 if len(node.args) != 1: 3333 error(node.function.pos, u"%s() takes exactly one argument" % function) 3334 else: 3335 node = InterpretCompilerDirectives.unop_method_nodes[function]( 3336 node.function.pos, operand=node.args[0]) 3337 elif function in InterpretCompilerDirectives.binop_method_nodes: 3338 if len(node.args) != 2: 3339 error(node.function.pos, u"%s() takes exactly two arguments" % function) 3340 else: 3341 node = InterpretCompilerDirectives.binop_method_nodes[function]( 3342 node.function.pos, operand1=node.args[0], operand2=node.args[1]) 3343 elif function == u'cast': 3344 if len(node.args) != 2: 3345 error(node.function.pos, 3346 u"cast() takes exactly two arguments and an optional typecheck keyword") 3347 else: 3348 type = node.args[0].analyse_as_type(self.current_env()) 3349 if type: 3350 node = ExprNodes.TypecastNode( 3351 node.function.pos, type=type, operand=node.args[1], typecheck=False) 3352 else: 3353 error(node.args[0].pos, "Not a type") 3354 elif function == u'sizeof': 3355 if len(node.args) != 1: 3356 error(node.function.pos, u"sizeof() takes exactly one argument") 3357 else: 3358 type = node.args[0].analyse_as_type(self.current_env()) 3359 if type: 3360 node = ExprNodes.SizeofTypeNode(node.function.pos, arg_type=type) 3361 else: 3362 node = ExprNodes.SizeofVarNode(node.function.pos, operand=node.args[0]) 3363 elif function == 'cmod': 3364 if len(node.args) != 2: 3365 error(node.function.pos, u"cmod() takes exactly two arguments") 3366 else: 3367 node = ExprNodes.binop_node(node.function.pos, '%', node.args[0], node.args[1]) 3368 node.cdivision = True 3369 elif function == 'cdiv': 3370 if len(node.args) != 2: 3371 error(node.function.pos, u"cdiv() takes exactly two arguments") 3372 else: 3373 node = ExprNodes.binop_node(node.function.pos, '/', node.args[0], node.args[1]) 3374 node.cdivision = True 3375 elif function == u'set': 3376 node.function = ExprNodes.NameNode(node.pos, name=EncodedString('set')) 3377 elif function == u'staticmethod': 3378 node.function = ExprNodes.NameNode(node.pos, name=EncodedString('staticmethod')) 3379 elif self.context.cython_scope.lookup_qualified_name(function): 3380 pass 3381 else: 3382 error(node.function.pos, 3383 u"'%s' not a valid cython language construct" % function) 3384 3385 self.visitchildren(node) 3386 3387 if isinstance(node, ExprNodes.SimpleCallNode) and node.function.is_name: 3388 func_name = node.function.name 3389 if func_name in ('dir', 'locals', 'vars'): 3390 return self._inject_locals(node, func_name) 3391 if func_name == 'eval': 3392 return self._inject_eval(node, func_name) 3393 if func_name == 'super': 3394 return self._inject_super(node, func_name) 3395 return node 3396 3397 def visit_GeneralCallNode(self, node): 3398 function = node.function.as_cython_attribute() 3399 if function == u'cast': 3400 # NOTE: assuming simple tuple/dict nodes for positional_args and keyword_args 3401 args = node.positional_args.args 3402 kwargs = node.keyword_args.compile_time_value(None) 3403 if (len(args) != 2 or len(kwargs) > 1 or 3404 (len(kwargs) == 1 and 'typecheck' not in kwargs)): 3405 error(node.function.pos, 3406 u"cast() takes exactly two arguments and an optional typecheck keyword") 3407 else: 3408 type = args[0].analyse_as_type(self.current_env()) 3409 if type: 3410 typecheck = kwargs.get('typecheck', False) 3411 node = ExprNodes.TypecastNode( 3412 node.function.pos, type=type, operand=args[1], typecheck=typecheck) 3413 else: 3414 error(args[0].pos, "Not a type") 3415 3416 self.visitchildren(node) 3417 return node 3418 3419 3420class ReplaceFusedTypeChecks(VisitorTransform): 3421 """ 3422 This is not a transform in the pipeline. It is invoked on the specific 3423 versions of a cdef function with fused argument types. It filters out any 3424 type branches that don't match. e.g. 3425 3426 if fused_t is mytype: 3427 ... 3428 elif fused_t in other_fused_type: 3429 ... 3430 """ 3431 def __init__(self, local_scope): 3432 super(ReplaceFusedTypeChecks, self).__init__() 3433 self.local_scope = local_scope 3434 # defer the import until now to avoid circular import time dependencies 3435 from .Optimize import ConstantFolding 3436 self.transform = ConstantFolding(reevaluate=True) 3437 3438 def visit_IfStatNode(self, node): 3439 """ 3440 Filters out any if clauses with false compile time type check 3441 expression. 3442 """ 3443 self.visitchildren(node) 3444 return self.transform(node) 3445 3446 def visit_GILStatNode(self, node): 3447 """ 3448 Fold constant condition of GILStatNode. 3449 """ 3450 self.visitchildren(node) 3451 return self.transform(node) 3452 3453 def visit_PrimaryCmpNode(self, node): 3454 with Errors.local_errors(ignore=True): 3455 type1 = node.operand1.analyse_as_type(self.local_scope) 3456 type2 = node.operand2.analyse_as_type(self.local_scope) 3457 3458 if type1 and type2: 3459 false_node = ExprNodes.BoolNode(node.pos, value=False) 3460 true_node = ExprNodes.BoolNode(node.pos, value=True) 3461 3462 type1 = self.specialize_type(type1, node.operand1.pos) 3463 op = node.operator 3464 3465 if op in ('is', 'is_not', '==', '!='): 3466 type2 = self.specialize_type(type2, node.operand2.pos) 3467 3468 is_same = type1.same_as(type2) 3469 eq = op in ('is', '==') 3470 3471 if (is_same and eq) or (not is_same and not eq): 3472 return true_node 3473 3474 elif op in ('in', 'not_in'): 3475 # We have to do an instance check directly, as operand2 3476 # needs to be a fused type and not a type with a subtype 3477 # that is fused. First unpack the typedef 3478 if isinstance(type2, PyrexTypes.CTypedefType): 3479 type2 = type2.typedef_base_type 3480 3481 if type1.is_fused: 3482 error(node.operand1.pos, "Type is fused") 3483 elif not type2.is_fused: 3484 error(node.operand2.pos, 3485 "Can only use 'in' or 'not in' on a fused type") 3486 else: 3487 types = PyrexTypes.get_specialized_types(type2) 3488 3489 for specialized_type in types: 3490 if type1.same_as(specialized_type): 3491 if op == 'in': 3492 return true_node 3493 else: 3494 return false_node 3495 3496 if op == 'not_in': 3497 return true_node 3498 3499 return false_node 3500 3501 return node 3502 3503 def specialize_type(self, type, pos): 3504 try: 3505 return type.specialize(self.local_scope.fused_to_specific) 3506 except KeyError: 3507 error(pos, "Type is not specific") 3508 return type 3509 3510 def visit_Node(self, node): 3511 self.visitchildren(node) 3512 return node 3513 3514 3515class DebugTransform(CythonTransform): 3516 """ 3517 Write debug information for this Cython module. 3518 """ 3519 3520 def __init__(self, context, options, result): 3521 super(DebugTransform, self).__init__(context) 3522 self.visited = set() 3523 # our treebuilder and debug output writer 3524 # (see Cython.Debugger.debug_output.CythonDebugWriter) 3525 self.tb = self.context.gdb_debug_outputwriter 3526 #self.c_output_file = options.output_file 3527 self.c_output_file = result.c_file 3528 3529 # Closure support, basically treat nested functions as if the AST were 3530 # never nested 3531 self.nested_funcdefs = [] 3532 3533 # tells visit_NameNode whether it should register step-into functions 3534 self.register_stepinto = False 3535 3536 def visit_ModuleNode(self, node): 3537 self.tb.module_name = node.full_module_name 3538 attrs = dict( 3539 module_name=node.full_module_name, 3540 filename=node.pos[0].filename, 3541 c_filename=self.c_output_file) 3542 3543 self.tb.start('Module', attrs) 3544 3545 # serialize functions 3546 self.tb.start('Functions') 3547 # First, serialize functions normally... 3548 self.visitchildren(node) 3549 3550 # ... then, serialize nested functions 3551 for nested_funcdef in self.nested_funcdefs: 3552 self.visit_FuncDefNode(nested_funcdef) 3553 3554 self.register_stepinto = True 3555 self.serialize_modulenode_as_function(node) 3556 self.register_stepinto = False 3557 self.tb.end('Functions') 3558 3559 # 2.3 compatibility. Serialize global variables 3560 self.tb.start('Globals') 3561 entries = {} 3562 3563 for k, v in node.scope.entries.items(): 3564 if (v.qualified_name not in self.visited and not 3565 v.name.startswith('__pyx_') and not 3566 v.type.is_cfunction and not 3567 v.type.is_extension_type): 3568 entries[k]= v 3569 3570 self.serialize_local_variables(entries) 3571 self.tb.end('Globals') 3572 # self.tb.end('Module') # end Module after the line number mapping in 3573 # Cython.Compiler.ModuleNode.ModuleNode._serialize_lineno_map 3574 return node 3575 3576 def visit_FuncDefNode(self, node): 3577 self.visited.add(node.local_scope.qualified_name) 3578 3579 if getattr(node, 'is_wrapper', False): 3580 return node 3581 3582 if self.register_stepinto: 3583 self.nested_funcdefs.append(node) 3584 return node 3585 3586 # node.entry.visibility = 'extern' 3587 if node.py_func is None: 3588 pf_cname = '' 3589 else: 3590 pf_cname = node.py_func.entry.func_cname 3591 3592 # For functions defined using def, cname will be pyfunc_cname=__pyx_pf_* 3593 # For functions defined using cpdef or cdef, cname will be func_cname=__pyx_f_* 3594 # In all cases, cname will be the name of the function containing the actual code 3595 cname = node.entry.pyfunc_cname or node.entry.func_cname 3596 3597 attrs = dict( 3598 name=node.entry.name or getattr(node, 'name', '<unknown>'), 3599 cname=cname, 3600 pf_cname=pf_cname, 3601 qualified_name=node.local_scope.qualified_name, 3602 lineno=str(node.pos[1])) 3603 3604 self.tb.start('Function', attrs=attrs) 3605 3606 self.tb.start('Locals') 3607 self.serialize_local_variables(node.local_scope.entries) 3608 self.tb.end('Locals') 3609 3610 self.tb.start('Arguments') 3611 for arg in node.local_scope.arg_entries: 3612 self.tb.start(arg.name) 3613 self.tb.end(arg.name) 3614 self.tb.end('Arguments') 3615 3616 self.tb.start('StepIntoFunctions') 3617 self.register_stepinto = True 3618 self.visitchildren(node) 3619 self.register_stepinto = False 3620 self.tb.end('StepIntoFunctions') 3621 self.tb.end('Function') 3622 3623 return node 3624 3625 def visit_NameNode(self, node): 3626 if (self.register_stepinto and 3627 node.type is not None and 3628 node.type.is_cfunction and 3629 getattr(node, 'is_called', False) and 3630 node.entry.func_cname is not None): 3631 # don't check node.entry.in_cinclude, as 'cdef extern: ...' 3632 # declared functions are not 'in_cinclude'. 3633 # This means we will list called 'cdef' functions as 3634 # "step into functions", but this is not an issue as they will be 3635 # recognized as Cython functions anyway. 3636 attrs = dict(name=node.entry.func_cname) 3637 self.tb.start('StepIntoFunction', attrs=attrs) 3638 self.tb.end('StepIntoFunction') 3639 3640 self.visitchildren(node) 3641 return node 3642 3643 def serialize_modulenode_as_function(self, node): 3644 """ 3645 Serialize the module-level code as a function so the debugger will know 3646 it's a "relevant frame" and it will know where to set the breakpoint 3647 for 'break modulename'. 3648 """ 3649 self._serialize_modulenode_as_function(node, dict( 3650 name=node.full_module_name.rpartition('.')[-1], 3651 cname=node.module_init_func_cname(), 3652 pf_cname='', 3653 # Ignore the qualified_name, breakpoints should be set using 3654 # `cy break modulename:lineno` for module-level breakpoints. 3655 qualified_name='', 3656 lineno='1', 3657 is_initmodule_function="True", 3658 )) 3659 3660 def _serialize_modulenode_as_function(self, node, attrs): 3661 self.tb.start('Function', attrs=attrs) 3662 3663 self.tb.start('Locals') 3664 self.serialize_local_variables(node.scope.entries) 3665 self.tb.end('Locals') 3666 3667 self.tb.start('Arguments') 3668 self.tb.end('Arguments') 3669 3670 self.tb.start('StepIntoFunctions') 3671 self.register_stepinto = True 3672 self.visitchildren(node) 3673 self.register_stepinto = False 3674 self.tb.end('StepIntoFunctions') 3675 3676 self.tb.end('Function') 3677 3678 def serialize_local_variables(self, entries): 3679 for entry in entries.values(): 3680 if not entry.cname: 3681 # not a local variable 3682 continue 3683 if entry.type.is_pyobject: 3684 vartype = 'PythonObject' 3685 else: 3686 vartype = 'CObject' 3687 3688 if entry.from_closure: 3689 # We're dealing with a closure where a variable from an outer 3690 # scope is accessed, get it from the scope object. 3691 cname = '%s->%s' % (Naming.cur_scope_cname, 3692 entry.outer_entry.cname) 3693 3694 qname = '%s.%s.%s' % (entry.scope.outer_scope.qualified_name, 3695 entry.scope.name, 3696 entry.name) 3697 elif entry.in_closure: 3698 cname = '%s->%s' % (Naming.cur_scope_cname, 3699 entry.cname) 3700 qname = entry.qualified_name 3701 else: 3702 cname = entry.cname 3703 qname = entry.qualified_name 3704 3705 if not entry.pos: 3706 # this happens for variables that are not in the user's code, 3707 # e.g. for the global __builtins__, __doc__, etc. We can just 3708 # set the lineno to 0 for those. 3709 lineno = '0' 3710 else: 3711 lineno = str(entry.pos[1]) 3712 3713 attrs = dict( 3714 name=entry.name, 3715 cname=cname, 3716 qualified_name=qname, 3717 type=vartype, 3718 lineno=lineno) 3719 3720 self.tb.start('LocalVar', attrs) 3721 self.tb.end('LocalVar') 3722