1 #!/usr/bin/env python 2 3 """ 4 Inspect and obtain module structure. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 7 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk> 8 9 This program is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free Software 11 Foundation; either version 3 of the License, or (at your option) any later 12 version. 13 14 This program is distributed in the hope that it will be useful, but WITHOUT 15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 details. 18 19 You should have received a copy of the GNU General Public License along with 20 this program. If not, see <http://www.gnu.org/licenses/>. 21 """ 22 23 from branching import BranchTracker 24 from common import * 25 from modules import * 26 from os import listdir 27 from os.path import extsep, split, splitext 28 from referencing import Reference 29 import compiler 30 import sys 31 32 class AccessRef(Result): 33 34 """ 35 A reference to an attribute access that is generally only returned from a 36 processed access for possible initialiser resolution for assignments. 37 """ 38 39 def __init__(self, original_name, attrnames, number): 40 self.original_name = original_name 41 self.attrnames = attrnames 42 self.number = number 43 44 def reference(self): 45 return None 46 47 def __repr__(self): 48 return "AccessRef(%r, %r, %r)" % (self.original_name, self.attrnames, self.number) 49 50 class InvocationRef(Result): 51 52 "An invocation of a name reference." 53 54 def __init__(self, name_ref): 55 self.name_ref = name_ref 56 57 def __repr__(self): 58 return "InvocationRef(%r)" % self.name_ref 59 60 class InspectedModule(BasicModule, CacheWritingModule): 61 62 "A module inspector." 63 64 def __init__(self, name, importer): 65 BasicModule.__init__(self, name, importer) 66 self.in_class = False 67 self.in_conditional = False 68 self.global_attr_accesses = {} 69 70 # Usage tracking. 71 72 self.trackers = [] 73 self.attr_accessor_branches = {} 74 75 def __repr__(self): 76 return "InspectedModule(%r, %r)" % (self.name, self.importer) 77 78 def parse(self, filename): 79 80 "Parse the file having the given 'filename'." 81 82 self.parse_file(filename) 83 84 # Inspect the module. 85 86 self.start_tracking_in_module() 87 88 # Detect and record imports and globals declared in the module. 89 90 self.assign_general_local("__name__", self.get_constant("str", self.name)) 91 self.assign_general_local("__file__", self.get_constant("str", filename)) 92 self.process_structure(self.astnode) 93 94 # Set the class of the module after the definition has occurred. 95 96 ref = self.get_builtin("object") 97 self.set_name("__class__", ref) 98 99 # Get module-level attribute usage details. 100 101 self.stop_tracking_in_module() 102 103 # Check names used and resolve them. 104 105 self.register_submodules() 106 self.loaded = True 107 108 def register_submodules(self): 109 110 "For package modules add submodule details." 111 112 if splitext(split(self.filename)[1])[0] == "__init__": 113 for subname in listdir(split(self.filename)[0]): 114 name, ext = splitext(subname) 115 116 # Add modules whose names are not already defined as globals. 117 118 if ext == ("%spy" % extsep) and name != "__init__" and not self.get_global(name): 119 module_name = self.get_global_path(name) 120 top, submodule = self.get_module(module_name, True) 121 self.set_module(name, submodule, hidden=True) 122 123 def check_special(self): 124 125 "Check special names." 126 127 for name, value in self.special.items(): 128 if value.has_kind("<depends>"): 129 self.find_imported_name(name, self.name) 130 self.special[name] = self.get_object(value.get_origin()) 131 132 def check_names_used(self): 133 134 "Check the names used by each function." 135 136 for path in self.names_used.keys(): 137 self.check_names_used_for_path(path) 138 139 def check_names_used_for_path(self, path): 140 141 "Check the names used by the given 'path'." 142 143 names = self.names_used.get(path) 144 if not names: 145 return 146 147 in_function = self.function_locals.has_key(path) 148 149 for name in names: 150 if name in predefined_constants or in_function and name in self.function_locals[path]: 151 continue 152 153 # Resolve names that have been imported locally. 154 155 self.find_imported_name(name, path) 156 157 # Find local definitions. 158 159 key = "%s.%s" % (path, name) 160 ref = self.get_object(key) 161 if ref: 162 self.name_references[key] = ref.final() or key 163 self.resolve_accesses(path, name, ref) 164 continue 165 166 # Resolve names that have been imported globally. 167 168 self.find_imported_name(name, self.name) 169 170 # Find global or built-in definitions. 171 172 ref = self.get_global_or_builtin(name) 173 objpath = ref and (ref.final() or ref.get_name()) 174 if objpath: 175 self.name_references[key] = objpath 176 self.resolve_accesses(path, name, ref) 177 continue 178 179 print >>sys.stderr, "Name not recognised: %s in %s" % (name, path) 180 init_item(self.names_missing, path, set) 181 self.names_missing[path].add(name) 182 183 def resolve_members(self): 184 185 "Resolve any members referring to deferred references." 186 187 for name, ref in self.objects.items(): 188 if ref.has_kind("<depends>"): 189 ref = self.get_object(ref.get_origin()) 190 ref = ref.alias(name) 191 self.importer.objects[name] = self.objects[name] = ref 192 193 def resolve_accesses(self, path, name, ref): 194 195 """ 196 Resolve any unresolved accesses in the function at the given 'path' 197 for the given 'name' corresponding to the indicated 'ref'. Note that 198 this mechanism cannot resolve things like inherited methods because 199 they are not recorded as program objects in their inherited locations. 200 """ 201 202 attr_accesses = self.global_attr_accesses.get(path) 203 all_attrnames = attr_accesses and attr_accesses.get(name) 204 205 if not all_attrnames: 206 return 207 208 # Insist on constant accessors. 209 210 if not ref.has_kind(["<class>", "<module>"]): 211 return 212 213 found_attrnames = set() 214 215 for attrnames in all_attrnames: 216 217 # Start with the resolved name, adding attributes. 218 219 attrs = ref.get_path() 220 remaining = attrnames.split(".") 221 last_ref = ref 222 223 # Add each component, testing for a constant object. 224 225 while remaining: 226 attrname = remaining[0] 227 attrs.append(attrname) 228 del remaining[0] 229 230 # Find any constant object reference. 231 232 attr_ref = self.get_object(".".join(attrs)) 233 234 # Non-constant accessors terminate the traversal. 235 236 if not attr_ref.has_kind(["<class>", "<module>", "<function>"]): 237 238 # Provide the furthest constant accessor unless the final 239 # access can be resolved. 240 241 if remaining: 242 remaining.insert(0, attrs.pop()) 243 else: 244 last_ref = attr_ref 245 break 246 247 # A module may expose an attribute imported from a hidden 248 # module. 249 250 elif last_ref.has_kind("<module>"): 251 module, leaf_module = self.get_module(last_ref.get_origin()) 252 self.find_imported_name(attrname, module.name, module) 253 254 # Follow any reference to a constant object. 255 # Where the given name refers to an object in another location, 256 # switch to the other location in order to be able to test its 257 # attributes. 258 259 last_ref = attr_ref 260 attrs = attr_ref.get_path() 261 262 # Record a constant accessor only if an object was found 263 # that is different from the namespace involved. 264 265 if last_ref: 266 objpath = ".".join(attrs) 267 if objpath != path: 268 269 # Establish a constant access. 270 271 init_item(self.const_accesses, path, dict) 272 self.const_accesses[path][(name, attrnames)] = (objpath, last_ref, ".".join(remaining)) 273 274 if len(attrs) > 1: 275 found_attrnames.add(attrs[1]) 276 277 # Remove any usage records for the name. 278 279 if found_attrnames: 280 281 # NOTE: Should be only one name version. 282 283 versions = [] 284 for version in self.attr_usage[path][name]: 285 new_usage = set() 286 for usage in version: 287 if found_attrnames.intersection(usage): 288 new_usage.add(tuple(set(usage).difference(found_attrnames))) 289 else: 290 new_usage.add(usage) 291 versions.append(new_usage) 292 293 self.attr_usage[path][name] = versions 294 295 def resolve_initialisers(self): 296 297 "Resolve initialiser values for names." 298 299 # Get the initialisers in each namespace. 300 301 for path, name_initialisers in self.name_initialisers.items(): 302 const_accesses = self.const_accesses.get(path) 303 304 # Resolve values for each name in a scope. 305 306 for name, values in name_initialisers.items(): 307 if path == self.name: 308 assigned_path = name 309 else: 310 assigned_path = "%s.%s" % (path, name) 311 312 initialised_names = {} 313 aliased_names = {} 314 315 for i, name_ref in enumerate(values): 316 317 # Unwrap invocations. 318 319 if isinstance(name_ref, InvocationRef): 320 invocation = True 321 name_ref = name_ref.name_ref 322 else: 323 invocation = False 324 325 # Obtain a usable reference from names or constants. 326 327 if isinstance(name_ref, ResolvedNameRef): 328 if not name_ref.reference(): 329 continue 330 ref = name_ref.reference() 331 332 # Obtain a reference from instances. 333 334 elif isinstance(name_ref, InstanceRef): 335 if not name_ref.reference(): 336 continue 337 ref = name_ref.reference() 338 339 # Resolve accesses that employ constants. 340 341 elif isinstance(name_ref, AccessRef): 342 ref = None 343 344 if const_accesses: 345 resolved_access = const_accesses.get((name_ref.original_name, name_ref.attrnames)) 346 if resolved_access: 347 objpath, ref, remaining_attrnames = resolved_access 348 if remaining_attrnames: 349 ref = None 350 351 # Accesses that do not employ constants cannot be resolved, 352 # but they may be resolvable later. 353 354 if not ref: 355 if not invocation: 356 aliased_names[i] = name_ref.original_name, name_ref.attrnames, name_ref.number 357 continue 358 359 # Attempt to resolve a plain name reference. 360 361 elif isinstance(name_ref, LocalNameRef): 362 key = "%s.%s" % (path, name_ref.name) 363 origin = self.name_references.get(key) 364 365 # Accesses that do not refer to known static objects 366 # cannot be resolved, but they may be resolvable later. 367 368 if not origin: 369 if not invocation: 370 aliased_names[i] = name_ref.name, None, name_ref.number 371 continue 372 373 ref = self.get_object(origin) 374 if not ref: 375 continue 376 377 elif isinstance(name_ref, NameRef): 378 key = "%s.%s" % (path, name_ref.name) 379 origin = self.name_references.get(key) 380 if not origin: 381 continue 382 383 ref = self.get_object(origin) 384 if not ref: 385 continue 386 387 else: 388 continue 389 390 # Convert class invocations to instances. 391 392 if invocation: 393 ref = ref.has_kind("<class>") and ref.instance_of() or None 394 395 if ref: 396 initialised_names[i] = ref 397 398 if initialised_names: 399 self.initialised_names[assigned_path] = initialised_names 400 if aliased_names: 401 self.aliased_names[assigned_path] = aliased_names 402 403 def resolve_literals(self): 404 405 "Resolve constant value types." 406 407 # Get the constants defined in each namespace. 408 409 for path, constants in self.constants.items(): 410 for constant, n in constants.items(): 411 objpath = "%s.$c%d" % (path, n) 412 _constant, value_type = self.constant_values[objpath] 413 self.initialised_names[objpath] = {0 : Reference("<instance>", value_type)} 414 415 # Get the literals defined in each namespace. 416 417 for path, literals in self.literals.items(): 418 for n in range(0, literals): 419 objpath = "%s.$C%d" % (path, n) 420 value_type = self.literal_types[objpath] 421 self.initialised_names[objpath] = {0 : Reference("<instance>", value_type)} 422 423 def remove_redundant_accessors(self): 424 425 "Remove now-redundant modifier and accessor information." 426 427 for path, const_accesses in self.const_accesses.items(): 428 accesses = self.attr_accessors.get(path) 429 modifiers = self.attr_access_modifiers.get(path) 430 if not accesses: 431 continue 432 for access in const_accesses.keys(): 433 if accesses.has_key(access): 434 del accesses[access] 435 if modifiers and modifiers.has_key(access): 436 del modifiers[access] 437 438 def set_invocation_usage(self): 439 440 """ 441 Discard the current invocation storage figures, retaining the maximum 442 values. 443 """ 444 445 for path, (current, maximum) in self.function_targets.items(): 446 self.importer.function_targets[path] = self.function_targets[path] = maximum 447 448 for path, (current, maximum) in self.function_arguments.items(): 449 self.importer.function_arguments[path] = self.function_arguments[path] = maximum 450 451 # Module structure traversal. 452 453 def process_structure_node(self, n): 454 455 "Process the individual node 'n'." 456 457 # Module global detection. 458 459 if isinstance(n, compiler.ast.Global): 460 self.process_global_node(n) 461 462 # Module import declarations. 463 464 elif isinstance(n, compiler.ast.From): 465 self.process_from_node(n) 466 467 elif isinstance(n, compiler.ast.Import): 468 self.process_import_node(n) 469 470 # Nodes using operator module functions. 471 472 elif isinstance(n, compiler.ast.Operator): 473 return self.process_operator_node(n) 474 475 elif isinstance(n, compiler.ast.AugAssign): 476 self.process_augassign_node(n) 477 478 elif isinstance(n, compiler.ast.Compare): 479 return self.process_compare_node(n) 480 481 elif isinstance(n, compiler.ast.Slice): 482 return self.process_slice_node(n) 483 484 elif isinstance(n, compiler.ast.Sliceobj): 485 return self.process_sliceobj_node(n) 486 487 elif isinstance(n, compiler.ast.Subscript): 488 return self.process_subscript_node(n) 489 490 # Namespaces within modules. 491 492 elif isinstance(n, compiler.ast.Class): 493 self.process_class_node(n) 494 495 elif isinstance(n, compiler.ast.Function): 496 self.process_function_node(n, n.name) 497 498 elif isinstance(n, compiler.ast.Lambda): 499 return self.process_lambda_node(n) 500 501 # Assignments. 502 503 elif isinstance(n, compiler.ast.Assign): 504 505 # Handle each assignment node. 506 507 for node in n.nodes: 508 self.process_assignment_node(node, n.expr) 509 510 # Assignments within non-Assign nodes. 511 512 elif isinstance(n, compiler.ast.AssName): 513 self.process_assignment_node(n, None) 514 515 elif isinstance(n, compiler.ast.AssAttr): 516 self.process_attribute_access(n) 517 518 # Accesses. 519 520 elif isinstance(n, compiler.ast.Getattr): 521 return self.process_attribute_access(n) 522 523 # Name recording for later testing. 524 525 elif isinstance(n, compiler.ast.Name): 526 return self.process_name_node(n) 527 528 # Conditional statement tracking. 529 530 elif isinstance(n, compiler.ast.For): 531 self.process_for_node(n) 532 533 elif isinstance(n, compiler.ast.While): 534 self.process_while_node(n) 535 536 elif isinstance(n, compiler.ast.If): 537 self.process_if_node(n) 538 539 elif isinstance(n, (compiler.ast.And, compiler.ast.Or)): 540 return self.process_logical_node(n) 541 542 # Exception control-flow tracking. 543 544 elif isinstance(n, compiler.ast.TryExcept): 545 self.process_try_node(n) 546 547 elif isinstance(n, compiler.ast.TryFinally): 548 self.process_try_finally_node(n) 549 550 # Control-flow modification statements. 551 552 elif isinstance(n, compiler.ast.Break): 553 self.trackers[-1].suspend_broken_branch() 554 555 elif isinstance(n, compiler.ast.Continue): 556 self.trackers[-1].suspend_continuing_branch() 557 558 elif isinstance(n, compiler.ast.Raise): 559 self.process_structure(n) 560 self.trackers[-1].abandon_branch() 561 562 elif isinstance(n, compiler.ast.Return): 563 self.process_structure(n) 564 self.trackers[-1].abandon_returning_branch() 565 566 # Invocations. 567 568 elif isinstance(n, compiler.ast.CallFunc): 569 return self.process_invocation_node(n) 570 571 # Constant usage. 572 573 elif isinstance(n, compiler.ast.Const): 574 return self.get_literal_instance(n, n.value.__class__.__name__) 575 576 elif isinstance(n, compiler.ast.Dict): 577 return self.get_literal_instance(n, "dict") 578 579 elif isinstance(n, compiler.ast.List): 580 return self.get_literal_instance(n, "list") 581 582 elif isinstance(n, compiler.ast.Tuple): 583 return self.get_literal_instance(n, "tuple") 584 585 # List comprehensions and if expressions. 586 587 elif isinstance(n, compiler.ast.ListComp): 588 self.process_listcomp_node(n) 589 590 elif isinstance(n, compiler.ast.IfExp): 591 self.process_ifexp_node(n) 592 593 # All other nodes are processed depth-first. 594 595 else: 596 self.process_structure(n) 597 598 # By default, no expression details are returned. 599 600 return None 601 602 # Specific node handling. 603 604 def process_assignment_node(self, n, expr): 605 606 "Process the individual node 'n' to be assigned the contents of 'expr'." 607 608 # Names and attributes are assigned the entire expression. 609 610 if isinstance(n, compiler.ast.AssName): 611 612 name_ref = expr and self.process_structure_node(expr) 613 614 # Name assignments populate either function namespaces or the 615 # general namespace hierarchy. 616 617 self.assign_general_local(n.name, name_ref) 618 619 # Record usage of the name. 620 621 self.record_name(n.name) 622 623 elif isinstance(n, compiler.ast.AssAttr): 624 if expr: self.process_structure_node(expr) 625 self.process_attribute_access(n) 626 627 # Lists and tuples are matched against the expression and their 628 # items assigned to expression items. 629 630 elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)): 631 self.process_assignment_node_items(n, expr) 632 633 # Slices and subscripts are permitted within assignment nodes. 634 635 elif isinstance(n, compiler.ast.Slice): 636 self.process_slice_node(n, expr) 637 638 elif isinstance(n, compiler.ast.Subscript): 639 self.process_subscript_node(n, expr) 640 641 def process_attribute_access(self, n): 642 643 "Process the given attribute access node 'n'." 644 645 # Obtain any completed chain and return the reference to it. 646 647 name_ref = self.process_attribute_chain(n) 648 if self.have_access_expression(n): 649 return name_ref 650 651 # Where the start of the chain of attributes has been reached, determine 652 # the complete access. 653 654 # Given a non-access node, this chain can be handled in its entirety, 655 # either being name-based and thus an access rooted on a name, or being 656 # based on some other node and thus an anonymous access of some kind. 657 658 path = self.get_namespace_path() 659 660 # Start with the the full attribute chain. 661 662 remaining = self.attrs 663 attrnames = ".".join(remaining) 664 665 # If the accessor cannot be identified, or where attributes 666 # remain in an attribute chain, record the anonymous accesses. 667 668 if not isinstance(name_ref, NameRef): # includes ResolvedNameRef 669 670 assignment = isinstance(n, compiler.ast.AssAttr) 671 672 init_item(self.attr_accesses, path, set) 673 self.attr_accesses[path].add(attrnames) 674 675 self.record_access_details(None, attrnames, assignment) 676 del self.attrs[0] 677 return 678 679 # Name-based accesses will handle the first attribute in a 680 # chain. 681 682 else: 683 attrname = remaining[0] 684 685 # Attribute assignments are used to identify instance attributes. 686 687 if isinstance(n, compiler.ast.AssAttr) and \ 688 self.in_class and self.in_function and n.expr.name == "self": 689 690 self.set_instance_attr(attrname) 691 692 # Record attribute usage using any name local to this namespace, 693 # if assigned in the namespace, or using an external name 694 # (presently just globals within classes). 695 696 name = self.get_name_for_tracking(name_ref.name, name_ref.final()) 697 tracker = self.trackers[-1] 698 699 immediate_access = len(self.attrs) == 1 700 assignment = immediate_access and isinstance(n, compiler.ast.AssAttr) 701 702 del self.attrs[0] 703 704 # Record global-based chains for subsequent resolution. 705 706 is_global = self.in_function and not self.function_locals[path].has_key(name) or \ 707 not self.in_function 708 709 if is_global: 710 self.record_global_access_details(name, attrnames) 711 712 # Make sure the name is being tracked: global names will not 713 # already be initialised in a branch and must be added 714 # explicitly. 715 716 if not tracker.have_name(name): 717 tracker.assign_names([name]) 718 if self.in_function: 719 self.scope_globals[path].add(name) 720 721 # Record attribute usage in the tracker, and record the branch 722 # information for the access. 723 724 branches = tracker.use_attribute(name, attrname) 725 726 if not branches: 727 print >>sys.stderr, "In %s, name %s is accessed using %s before an assignment." % ( 728 path, name, attrname) 729 branches = tracker.use_attribute(name, attrname) 730 731 self.record_branches_for_access(branches, name, attrnames) 732 access_number = self.record_access_details(name, attrnames, assignment) 733 return AccessRef(name, attrnames, access_number) 734 735 def process_class_node(self, n): 736 737 "Process the given class node 'n'." 738 739 path = self.get_namespace_path() 740 741 # To avoid notions of class "versions" where the same definition 742 # might be parameterised with different state and be referenced 743 # elsewhere (as base classes, for example), classes in functions or 744 # conditions are forbidden. 745 746 if self.in_function or self.in_conditional: 747 print >>sys.stderr, "In %s, class %s in function or conditional statement ignored." % ( 748 path, n.name) 749 return 750 751 # Resolve base classes. 752 753 bases = [] 754 755 for base in n.bases: 756 base_class = self.get_class(base) 757 758 if not base_class: 759 print >>sys.stderr, "In %s, class %s has unidentifiable base classes." % ( 760 path, n.name) 761 return 762 else: 763 bases.append(base_class) 764 765 # Record bases for the class and retain the class name. 766 767 class_name = self.get_object_path(n.name) 768 769 if not bases and class_name != "__builtins__.core.object": 770 ref = self.get_object("__builtins__.object") 771 bases.append(ref) 772 773 self.importer.classes[class_name] = self.classes[class_name] = bases 774 self.importer.subclasses[class_name] = set() 775 self.scope_globals[class_name] = set() 776 777 # Set the definition before entering the namespace rather than 778 # afterwards because methods may reference it. In normal Python, 779 # a class is not accessible until the definition is complete, but 780 # methods can generally reference it since upon being called the 781 # class will already exist. 782 783 self.set_definition(n.name, "<class>") 784 785 in_class = self.in_class 786 self.in_class = class_name 787 self.set_instance_attr("__class__", Reference("<class>", class_name)) 788 self.enter_namespace(n.name) 789 self.set_name("__fn__") # special instantiator attribute 790 self.set_name("__args__") # special instantiator attribute 791 self.assign_general_local("__name__", self.get_constant("str", class_name)) 792 self.process_structure_node(n.code) 793 self.exit_namespace() 794 self.in_class = in_class 795 796 def process_from_node(self, n): 797 798 "Process the given node 'n', importing from another module." 799 800 path = self.get_namespace_path() 801 802 modname, names = self.get_module_name(n) 803 804 # Load the mentioned module. 805 806 top, module = self.get_module(modname, True) 807 self.set_module(None, module, hidden=True) 808 809 if not module: 810 print >>sys.stderr, "In %s, from statement importing from %s failed." % ( 811 path, modname) 812 813 # Attempt to obtain the referenced objects. 814 815 for name, alias in n.names: 816 817 # NOTE: Package submodules are not implicitly imported. 818 819 if name == "*": 820 if module: 821 822 # Warn about a circular import that probably doesn't find 823 # the names. 824 825 if not module.loaded: 826 print >>sys.stderr, "In %s, from statement performs circular import %s of %s." % ( 827 path, modname) 828 829 for name, value in module.get_globals().items(): 830 if name != "__name__": 831 value = ResolvedNameRef(name, value) 832 self.set_general_local(name, value) 833 self.set_imported_name(name, modname) 834 break 835 836 # Explicit names. 837 838 ref = self.import_name_from_module(name, modname, module, alias) 839 value = ResolvedNameRef(alias or name, ref) 840 self.set_general_local(alias or name, value) 841 self.set_imported_name(name, modname, alias) 842 843 def import_name_from_module(self, name, modname, module, alias=None): 844 845 """ 846 Import 'name' from the module having the given 'modname', with 'module' 847 having been obtained for the module name, using 'alias' for the imported 848 name in the current namespace. 849 """ 850 851 path = self.get_namespace_path() 852 853 if module and module.get_global(name): 854 value = module.get_global(name) 855 856 # Warn about an import that fails to provide a name, perhaps due 857 # to a circular import. 858 859 if not value: 860 print >>sys.stderr, "In %s, from statement cannot import %s from %s%s." % ( 861 path, name, modname, not module.loaded and "(circular import)") 862 863 return value 864 865 # Record the name as a dependency. 866 867 else: 868 return Reference("<depends>", "%s.%s" % (modname, name)) 869 870 def process_function_node(self, n, name): 871 872 """ 873 Process the given function or lambda node 'n' with the given 'name'. 874 """ 875 876 is_lambda = isinstance(n, compiler.ast.Lambda) 877 878 # Where a function is declared conditionally, use a separate name for 879 # the definition, and assign the definition to the stated name. 880 881 if (self.in_conditional or self.in_function) and not is_lambda: 882 original_name = name 883 name = self.get_lambda_name() 884 else: 885 original_name = None 886 887 # Initialise argument and local records. 888 889 function_name = self.get_object_path(name) 890 891 argnames = self.importer.function_parameters[function_name] = \ 892 self.function_parameters[function_name] = get_argnames(n.argnames) 893 locals = self.function_locals[function_name] = {} 894 895 for argname in argnames: 896 locals[argname] = Reference("<var>") 897 898 globals = self.scope_globals[function_name] = set() 899 900 # Process the defaults. 901 902 defaults = self.importer.function_defaults[function_name] = \ 903 self.function_defaults[function_name] = [] 904 905 for argname, default in compiler.ast.get_defaults(n): 906 if default: 907 908 # Obtain any reference for the default. 909 910 name_ref = self.process_structure_node(default) 911 defaults.append((argname, name_ref.is_name() and name_ref.reference() or Reference("<var>"))) 912 913 # Reset conditional tracking to focus on the function contents. 914 915 in_conditional = self.in_conditional 916 self.in_conditional = False 917 918 in_function = self.in_function 919 self.in_function = function_name 920 921 self.enter_namespace(name) 922 923 # Track attribute usage within the namespace. 924 925 path = self.get_namespace_path() 926 927 self.start_tracking(locals) 928 self.process_structure_node(n.code) 929 self.stop_tracking() 930 931 # Exit to the parent. 932 933 self.exit_namespace() 934 935 # Update flags. 936 937 self.in_function = in_function 938 self.in_conditional = in_conditional 939 940 # Define the function using the appropriate name. 941 942 self.set_definition(name, "<function>") 943 944 # Where a function is set conditionally, assign the name. 945 946 if original_name: 947 self.process_assignment_for_function(original_name, name) 948 949 def process_global_node(self, n): 950 951 """ 952 Process the given "global" node 'n'. 953 """ 954 955 path = self.get_namespace_path() 956 957 if path != self.name: 958 self.scope_globals[path].update(n.names) 959 960 def process_if_node(self, n): 961 962 """ 963 Process the given "if" node 'n'. 964 """ 965 966 tracker = self.trackers[-1] 967 tracker.new_branchpoint() 968 969 for test, body in n.tests: 970 self.process_structure_node(test) 971 972 tracker.new_branch() 973 974 in_conditional = self.in_conditional 975 self.in_conditional = True 976 self.process_structure_node(body) 977 self.in_conditional = in_conditional 978 979 tracker.shelve_branch() 980 981 # Maintain a branch for the else clause. 982 983 tracker.new_branch() 984 if n.else_: 985 self.process_structure_node(n.else_) 986 tracker.shelve_branch() 987 988 tracker.merge_branches() 989 990 def process_ifexp_node(self, n): 991 992 "Process the given if expression node 'n'." 993 994 name_ref = self.process_structure_node(self.convert_ifexp_node(n)) 995 996 path = self.get_namespace_path() 997 self.allocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 998 self.deallocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 999 1000 return InvocationRef(name_ref) 1001 1002 def process_import_node(self, n): 1003 1004 "Process the given import node 'n'." 1005 1006 path = self.get_namespace_path() 1007 1008 # Load the mentioned module. 1009 1010 for name, alias in n.names: 1011 module, leaf_module = self.get_module(name, alias) 1012 1013 if not module: 1014 print >>sys.stderr, "In %s, import statement importing from %s failed." % ( 1015 path, name) 1016 if module and not module.loaded: 1017 print >>sys.stderr, "In %s, import statement performs circular import of %s." % ( 1018 path, name) 1019 1020 self.set_module(alias or name.split(".")[0], module, leaf_module) 1021 1022 def process_invocation_node(self, n): 1023 1024 "Process the given invocation node 'n'." 1025 1026 path = self.get_namespace_path() 1027 1028 self.allocate_arguments(path, n.args) 1029 1030 try: 1031 # Process the expression, obtaining any identified reference. 1032 1033 name_ref = self.process_structure_node(n.node) 1034 1035 # Process the arguments. 1036 1037 for arg in n.args: 1038 self.process_structure_node(arg) 1039 1040 # Detect class invocations. 1041 1042 if isinstance(name_ref, ResolvedNameRef) and name_ref.has_kind("<class>"): 1043 return InstanceRef(name_ref.reference().instance_of()) 1044 1045 elif isinstance(name_ref, NameRef): 1046 return InvocationRef(name_ref) 1047 1048 return None 1049 1050 finally: 1051 self.deallocate_arguments(path, n.args) 1052 1053 def process_lambda_node(self, n): 1054 1055 "Process the given lambda node 'n'." 1056 1057 name = self.get_lambda_name() 1058 self.process_function_node(n, name) 1059 1060 origin = self.get_object_path(name) 1061 return ResolvedNameRef(name, Reference("<function>", origin)) 1062 1063 def process_listcomp_node(self, n): 1064 1065 "Process the given list comprehension node 'n'." 1066 1067 name_ref = self.process_structure_node(self.convert_listcomp_node(n)) 1068 1069 path = self.get_namespace_path() 1070 self.allocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 1071 self.deallocate_arguments(path, self.function_defaults[name_ref.get_origin()]) 1072 1073 return InvocationRef(name_ref) 1074 1075 def process_logical_node(self, n): 1076 1077 "Process the given operator node 'n'." 1078 1079 self.process_operator_chain(n.nodes, self.process_structure_node) 1080 1081 def process_name_node(self, n): 1082 1083 "Process the given name node 'n'." 1084 1085 path = self.get_namespace_path() 1086 1087 # Special names. 1088 1089 if n.name.startswith("$"): 1090 value = self.get_special(n.name) 1091 if value: 1092 return value 1093 1094 # Special case for operator functions introduced through code 1095 # transformations. 1096 1097 if n.name.startswith("$op"): 1098 1099 # Obtain the location of the actual function defined in the operator 1100 # package. 1101 1102 op = n.name[len("$op"):] 1103 1104 # Access the operator module. 1105 1106 top, module = self.get_module("operator", True) 1107 self.set_module(None, module, hidden=True) 1108 1109 # Link the operation to the operator module definition in this 1110 # module. 1111 1112 self.set_imported_name(op, "operator", n.name, self.name) 1113 1114 # Attempt to get a reference. 1115 1116 ref = self.import_name_from_module(op, "operator", module) 1117 ref = self.get_object("operator.%s" % op) or ref 1118 1119 # Record the imported name and provide the resolved name reference. 1120 1121 value = ResolvedNameRef(n.name, ref) 1122 self.set_special(n.name, value) 1123 return value 1124 1125 # Record usage of the name. 1126 1127 self.record_name(n.name) 1128 1129 # Search for unknown names in non-function scopes immediately. 1130 # External names in functions are resolved later. 1131 1132 ref = self.find_name(n.name) 1133 if ref: 1134 return ResolvedNameRef(n.name, ref) 1135 1136 # Global name. 1137 1138 elif self.in_function and n.name in self.scope_globals[path]: 1139 return NameRef(n.name) 1140 1141 # Examine other names. 1142 1143 else: 1144 tracker = self.trackers[-1] 1145 1146 # Check local names. 1147 1148 branches = tracker.tracking_name(n.name) 1149 1150 # Local name. 1151 1152 if branches: 1153 self.record_branches_for_access(branches, n.name, None) 1154 access_number = self.record_access_details(n.name, None, False) 1155 return LocalNameRef(n.name, access_number) 1156 1157 # Possible global name. 1158 1159 else: 1160 return NameRef(n.name) 1161 1162 def process_operator_chain(self, nodes, fn): 1163 1164 """ 1165 Process the given chain of 'nodes', applying 'fn' to each node or item. 1166 Each node starts a new conditional region, effectively making a deeply- 1167 nested collection of if-like statements. 1168 """ 1169 1170 tracker = self.trackers[-1] 1171 1172 for item in nodes: 1173 tracker.new_branchpoint() 1174 tracker.new_branch() 1175 fn(item) 1176 1177 for item in nodes[:-1]: 1178 tracker.shelve_branch() 1179 tracker.new_branch() 1180 tracker.shelve_branch() 1181 tracker.merge_branches() 1182 1183 tracker.shelve_branch() 1184 tracker.merge_branches() 1185 1186 def process_try_node(self, n): 1187 1188 """ 1189 Process the given "try...except" node 'n'. 1190 """ 1191 1192 tracker = self.trackers[-1] 1193 tracker.new_branchpoint() 1194 1195 self.process_structure_node(n.body) 1196 1197 for name, var, handler in n.handlers: 1198 if name is not None: 1199 self.process_structure_node(name) 1200 1201 # Any abandoned branches from the body can now be resumed in a new 1202 # branch. 1203 1204 tracker.resume_abandoned_branches() 1205 1206 # Establish the local for the handler. 1207 1208 if var is not None: 1209 self.process_structure_node(var) 1210 if handler is not None: 1211 self.process_structure_node(handler) 1212 1213 tracker.shelve_branch() 1214 1215 # The else clause maintains the usage from the body but without the 1216 # abandoned branches since they would never lead to the else clause 1217 # being executed. 1218 1219 if n.else_: 1220 tracker.new_branch() 1221 self.process_structure_node(n.else_) 1222 tracker.shelve_branch() 1223 1224 # Without an else clause, a null branch propagates the successful 1225 # outcome. 1226 1227 else: 1228 tracker.new_branch() 1229 tracker.shelve_branch() 1230 1231 tracker.merge_branches() 1232 1233 def process_try_finally_node(self, n): 1234 1235 """ 1236 Process the given "try...finally" node 'n'. 1237 """ 1238 1239 tracker = self.trackers[-1] 1240 self.process_structure_node(n.body) 1241 1242 # Any abandoned branches from the body can now be resumed. 1243 1244 branches = tracker.resume_all_abandoned_branches() 1245 self.process_structure_node(n.final) 1246 1247 # At the end of the finally clause, abandoned branches are discarded. 1248 1249 tracker.restore_active_branches(branches) 1250 1251 def process_while_node(self, n): 1252 1253 "Process the given while node 'n'." 1254 1255 tracker = self.trackers[-1] 1256 tracker.new_branchpoint(loop_node=True) 1257 1258 # Evaluate any test or iterator outside the loop. 1259 1260 self.process_structure_node(n.test) 1261 1262 # Propagate attribute usage to branches. 1263 1264 tracker.new_branch(loop_node=True) 1265 1266 # Enter the loop. 1267 1268 in_conditional = self.in_conditional 1269 self.in_conditional = True 1270 self.process_structure_node(n.body) 1271 self.in_conditional = in_conditional 1272 1273 # Continuing branches are resumed before any test. 1274 1275 tracker.resume_continuing_branches() 1276 1277 # Evaluate any continuation test within the body. 1278 1279 self.process_structure_node(n.test) 1280 1281 tracker.shelve_branch(loop_node=True) 1282 1283 # Support the non-looping condition. 1284 1285 tracker.new_branch() 1286 tracker.shelve_branch() 1287 1288 tracker.merge_branches() 1289 1290 # Evaluate any else clause outside branches. 1291 1292 if n.else_: 1293 self.process_structure_node(n.else_) 1294 1295 # Connect broken branches to the code after any loop. 1296 1297 tracker.resume_broken_branches() 1298 1299 # Branch tracking methods. 1300 1301 def start_tracking(self, names): 1302 1303 """ 1304 Start tracking attribute usage for names in the current namespace, 1305 immediately registering the given 'names'. 1306 """ 1307 1308 path = self.get_namespace_path() 1309 parent = self.trackers[-1] 1310 tracker = BranchTracker() 1311 self.trackers.append(tracker) 1312 1313 # Record the given names established as new branches. 1314 1315 tracker.assign_names(names) 1316 1317 def assign_name(self, name, name_ref): 1318 1319 "Assign to 'name' the given 'name_ref' in the current namespace." 1320 1321 name = self.get_name_for_tracking(name) 1322 self.trackers[-1].assign_names([name], [name_ref]) 1323 1324 def stop_tracking(self): 1325 1326 """ 1327 Stop tracking attribute usage, recording computed usage for the current 1328 namespace. 1329 """ 1330 1331 path = self.get_namespace_path() 1332 tracker = self.trackers.pop() 1333 self.record_assignments_for_access(tracker) 1334 1335 self.attr_usage[path] = tracker.get_all_usage() 1336 self.name_initialisers[path] = tracker.get_all_values() 1337 1338 def start_tracking_in_module(self): 1339 1340 "Start tracking attribute usage in the module." 1341 1342 tracker = BranchTracker() 1343 self.trackers.append(tracker) 1344 1345 def stop_tracking_in_module(self): 1346 1347 "Stop tracking attribute usage in the module." 1348 1349 tracker = self.trackers[0] 1350 self.record_assignments_for_access(tracker) 1351 self.attr_usage[self.name] = tracker.get_all_usage() 1352 self.name_initialisers[self.name] = tracker.get_all_values() 1353 1354 def record_assignments_for_access(self, tracker): 1355 1356 """ 1357 For the current path, use the given 'tracker' to record assignment 1358 version information for attribute accesses. 1359 """ 1360 1361 path = self.get_path_for_access() 1362 1363 if not self.attr_accessor_branches.has_key(path): 1364 return 1365 1366 init_item(self.attr_accessors, path, dict) 1367 attr_accessors = self.attr_accessors[path] 1368 1369 # Obtain the branches applying during each access. 1370 1371 for access, all_branches in self.attr_accessor_branches[path].items(): 1372 name, attrnames = access 1373 init_item(attr_accessors, access, list) 1374 1375 # Obtain the assignments applying to each branch. 1376 1377 for branches in all_branches: 1378 positions = tracker.get_assignment_positions_for_branches(name, branches) 1379 1380 # Detect missing name information. 1381 1382 if None in positions: 1383 globals = self.global_attr_accesses.get(path) 1384 accesses = globals and globals.get(name) 1385 if not accesses: 1386 print >>sys.stderr, "In %s, %s may not be defined when used." % ( 1387 self.get_namespace_path(), name) 1388 positions.remove(None) 1389 1390 attr_accessors[access].append(positions) 1391 1392 def record_branches_for_access(self, branches, name, attrnames): 1393 1394 """ 1395 Record the given 'branches' for an access involving the given 'name' and 1396 'attrnames'. 1397 """ 1398 1399 access = name, attrnames 1400 path = self.get_path_for_access() 1401 1402 init_item(self.attr_accessor_branches, path, dict) 1403 attr_accessor_branches = self.attr_accessor_branches[path] 1404 1405 init_item(attr_accessor_branches, access, list) 1406 attr_accessor_branches[access].append(branches) 1407 1408 def record_access_details(self, name, attrnames, assignment): 1409 1410 """ 1411 For the given 'name' and 'attrnames', record an access indicating 1412 whether 'assignment' is occurring. 1413 1414 These details correspond to accesses otherwise recorded by the attribute 1415 accessor and attribute access dictionaries. 1416 """ 1417 1418 access = name, attrnames 1419 path = self.get_path_for_access() 1420 1421 init_item(self.attr_access_modifiers, path, dict) 1422 init_item(self.attr_access_modifiers[path], access, list) 1423 1424 access_number = len(self.attr_access_modifiers[path][access]) 1425 self.attr_access_modifiers[path][access].append(assignment) 1426 return access_number 1427 1428 def record_global_access_details(self, name, attrnames): 1429 1430 """ 1431 Record details of a global access via the given 'name' involving the 1432 indicated 'attrnames'. 1433 """ 1434 1435 path = self.get_namespace_path() 1436 1437 init_item(self.global_attr_accesses, path, dict) 1438 init_item(self.global_attr_accesses[path], name, set) 1439 self.global_attr_accesses[path][name].add(attrnames) 1440 1441 # Namespace modification. 1442 1443 def record_name(self, name): 1444 1445 "Record the use of 'name' in a namespace." 1446 1447 path = self.get_namespace_path() 1448 init_item(self.names_used, path, set) 1449 self.names_used[path].add(name) 1450 1451 def set_module(self, name, module, leaf_module=None, hidden=False): 1452 1453 """ 1454 Set a module in the current namespace using the given 'name' and 1455 corresponding 'module' object, with the 'leaf_module' being recorded 1456 if different. If 'hidden' is a true value, the modules are recorded as 1457 not necessarily being exposed by this module. This module is, however, 1458 recorded as accessing the given modules and is thus dependent on them. 1459 """ 1460 1461 if name: 1462 self.set_general_local(name, module and Reference("<module>", module.name) or None) 1463 if module: 1464 if hidden: 1465 self.imported_hidden.add(module) 1466 if leaf_module and leaf_module is not module: 1467 self.imported_hidden.add(leaf_module) 1468 else: 1469 self.imported.add(module) 1470 module.accessing_modules.add(self.name) 1471 if leaf_module and leaf_module is not module: 1472 self.imported.add(leaf_module) 1473 leaf_module.accessing_modules.add(self.name) 1474 1475 def set_definition(self, name, kind): 1476 1477 """ 1478 Set the definition having the given 'name' and 'kind'. 1479 1480 Definitions are set in the static namespace hierarchy, but they can also 1481 be recorded for function locals. 1482 """ 1483 1484 if self.is_global(name): 1485 print >>sys.stderr, "In %s, %s is defined as being global." % ( 1486 self.get_namespace_path(), name) 1487 1488 path = self.get_object_path(name) 1489 self.set_object(path, kind) 1490 1491 ref = self.get_object(path) 1492 if ref.get_kind() == "<var>": 1493 print >>sys.stderr, "In %s, %s is defined more than once." % ( 1494 self.get_namespace_path(), name) 1495 1496 if not self.is_global(name) and self.in_function: 1497 self.set_function_local(name, ref) 1498 1499 def set_function_local(self, name, ref=None): 1500 1501 "Set the local with the given 'name' and optional 'ref'." 1502 1503 locals = self.function_locals[self.get_namespace_path()] 1504 multiple = not ref or locals.has_key(name) and locals[name] != ref 1505 locals[name] = multiple and Reference("<var>") or ref 1506 1507 def assign_general_local(self, name, name_ref): 1508 1509 """ 1510 Set for 'name' the given 'name_ref', recording the name for attribute 1511 usage tracking. 1512 """ 1513 1514 self.set_general_local(name, name_ref) 1515 self.assign_name(name, name_ref) 1516 1517 def set_general_local(self, name, value=None): 1518 1519 """ 1520 Set the 'name' with optional 'value' in any kind of local namespace, 1521 where the 'value' should be a reference if specified. 1522 """ 1523 1524 init_value = self.get_initialising_value(value) 1525 1526 # Module global names. 1527 1528 if self.is_global(name): 1529 path = self.get_global_path(name) 1530 self.set_object(path, init_value) 1531 1532 # Function local names. 1533 1534 elif self.in_function: 1535 path = self.get_object_path(name) 1536 self.set_function_local(name, init_value) 1537 1538 # Other namespaces (classes). 1539 1540 else: 1541 path = self.get_object_path(name) 1542 self.set_name(name, init_value) 1543 1544 def set_name(self, name, ref=None): 1545 1546 "Attach the 'name' with optional 'ref' to the current namespace." 1547 1548 self.set_object(self.get_object_path(name), ref) 1549 1550 def set_instance_attr(self, name, ref=None): 1551 1552 """ 1553 Add an instance attribute of the given 'name' to the current class, 1554 using the optional 'ref'. 1555 """ 1556 1557 init_item(self.instance_attrs, self.in_class, set) 1558 self.instance_attrs[self.in_class].add(name) 1559 1560 if ref: 1561 init_item(self.instance_attr_constants, self.in_class, dict) 1562 self.instance_attr_constants[self.in_class][name] = ref 1563 1564 def get_initialising_value(self, value): 1565 1566 "Return a suitable initialiser reference for 'value'." 1567 1568 if isinstance(value, (NameRef, AccessRef, InstanceRef)): # includes LiteralSequenceRef, ResolvedNameRef 1569 return value.reference() 1570 1571 # In general, invocations do not produce known results. However, the 1572 # name initialisers are resolved once a module has been inspected. 1573 1574 elif isinstance(value, InvocationRef): 1575 return None 1576 1577 else: 1578 return value 1579 1580 # Static, program-relative naming. 1581 1582 def find_name(self, name): 1583 1584 """ 1585 Return the qualified name for the given 'name' used in the current 1586 non-function namespace. 1587 """ 1588 1589 path = self.get_namespace_path() 1590 ref = None 1591 1592 if not self.in_function and name not in predefined_constants: 1593 if self.in_class: 1594 ref = self.get_object(self.get_object_path(name)) 1595 if not ref: 1596 ref = self.get_global_or_builtin(name) 1597 1598 return ref 1599 1600 def get_class(self, node): 1601 1602 """ 1603 Use the given 'node' to obtain the identity of a class. Return a 1604 reference for the class. Unresolved dependencies are permitted and must 1605 be resolved later. 1606 """ 1607 1608 ref = self._get_class(node) 1609 return ref.has_kind(["<class>", "<depends>"]) and ref or None 1610 1611 def _get_class(self, node): 1612 1613 """ 1614 Use the given 'node' to find a class definition. Return a reference to 1615 the class. 1616 """ 1617 1618 if isinstance(node, compiler.ast.Getattr): 1619 1620 # Obtain the identity of the access target. 1621 1622 ref = self._get_class(node.expr) 1623 1624 # Where the target is a class or module, obtain the identity of the 1625 # attribute. 1626 1627 if ref.has_kind(["<function>", "<var>"]): 1628 return None 1629 else: 1630 attrname = "%s.%s" % (ref.get_origin(), node.attrname) 1631 return self.get_object(attrname) 1632 1633 # Names can be module-level or built-in. 1634 1635 elif isinstance(node, compiler.ast.Name): 1636 1637 # Record usage of the name and attempt to identify it. 1638 1639 self.record_name(node.name) 1640 return self.get_global_or_builtin(node.name) 1641 else: 1642 return None 1643 1644 def get_constant(self, name, value): 1645 1646 "Return a constant reference for the given type 'name' and 'value'." 1647 1648 ref = self.get_literal_builtin(name) 1649 return self.get_constant_reference(ref, value) 1650 1651 def get_literal_instance(self, n, name): 1652 1653 "For node 'n', return a reference to an instance of 'name'." 1654 1655 # Get a class reference. 1656 1657 ref = self.get_literal_builtin(name) 1658 1659 # Obtain the details of the literal itself. 1660 # An alias to the type is generated for sequences. 1661 1662 if name in ("dict", "list", "tuple"): 1663 self.set_special_literal(name, ref) 1664 return self.process_literal_sequence_node(n, name, ref, LiteralSequenceRef) 1665 1666 # Constant values are independently recorded. 1667 1668 else: 1669 return self.get_constant_reference(ref, n.value) 1670 1671 def get_literal_builtin(self, name): 1672 1673 "Return a reference for a built-in literal type of the given 'name'." 1674 1675 ref = self.get_builtin(name) 1676 true_origin = "__builtins__.%s.%s" % (name, name) 1677 exposed_origin = "__builtins__.%s" % name 1678 1679 # Obtain fully-imported built-in class references. 1680 1681 if ref and ref.has_kind("<class>"): 1682 pass 1683 1684 # Early-stage references need explicit references. 1685 1686 elif ref: 1687 ref = Reference("<class>", true_origin) 1688 1689 # Otherwise, the normal locations can be used. 1690 1691 else: 1692 ref = Reference("<class>", true_origin, exposed_origin) 1693 1694 return ref 1695 1696 # Functions and invocations. 1697 1698 def allocate_arguments(self, path, args): 1699 1700 """ 1701 Allocate temporary argument storage using current and maximum 1702 requirements for the given 'path' and 'args'. 1703 """ 1704 1705 init_item(self.function_targets, path, lambda: [0, 0]) 1706 t = self.function_targets[path] 1707 t[0] += 1 1708 t[1] = max(t[0], t[1]) 1709 1710 init_item(self.function_arguments, path, lambda: [0, 0]) 1711 t = self.function_arguments[path] 1712 t[0] += len(args) + 1 1713 t[1] = max(t[0], t[1]) 1714 1715 def deallocate_arguments(self, path, args): 1716 1717 "Deallocate temporary argument storage for the given 'path' and 'args'." 1718 1719 self.function_targets[path][0] -= 1 1720 self.function_arguments[path][0] -= len(args) + 1 1721 1722 # vim: tabstop=4 expandtab shiftwidth=4