1 #!/usr/bin/env python 2 3 """ 4 Annotate program node structures. The code in this module operates upon nodes 5 which are produced when simplifying AST node trees originating from the compiler 6 module. 7 8 Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk> 9 10 This software is free software; you can redistribute it and/or 11 modify it under the terms of the GNU General Public License as 12 published by the Free Software Foundation; either version 2 of 13 the License, or (at your option) any later version. 14 15 This software is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public 21 License along with this library; see the file LICENCE.txt 22 If not, write to the Free Software Foundation, Inc., 23 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 24 25 -------- 26 27 To use this module, the easiest approach is to use the load function: 28 29 load(filename, builtins) 30 31 To control module importing, an importer should be constructed and employed. 32 Here, the standard path for module searching is used: 33 34 importer = Importer(sys.path) 35 load(filename, builtins, importer) 36 37 Underneath the load function, the annotate function provides support for 38 annotating modules already processed by simplify and fixnames: 39 40 annotate(module, builtins) 41 42 And at the most basic level, the most intricate approach involves obtaining an 43 Annotator object: 44 45 annotator = Annotator() 46 47 Then, processing an existing module with it: 48 49 annotator.process(module) 50 51 If a module containing built-in classes and functions has already been 52 annotated, such a module should be passed in as an additional argument: 53 54 annotator.process(module, builtins) 55 """ 56 57 from simplified import * 58 import simplify, fixnames # for the load function 59 import compiler 60 import os 61 62 class System: 63 64 """ 65 A class maintaining the state of the annotation system. When the system 66 counter can no longer be incremented by any annotation operation, the 67 system may be considered stable and fully annotated. 68 """ 69 70 def __init__(self): 71 self.count = 0 72 73 def init(self, node): 74 75 "Initialise a 'node' for annotation." 76 77 if not hasattr(node, "types"): 78 node.types = [] 79 80 def annotate(self, node, types): 81 82 "Annotate the given 'node' with the given 'types'." 83 84 self.init(node) 85 self.combine(node.types, types) 86 87 def combine(self, target, types): 88 89 """ 90 Combine the 'target' list with the given 'types', counting new members. 91 """ 92 93 for type in types: 94 if type not in target: 95 target.append(type) 96 self.count += 1 97 98 system = System() 99 100 # Exceptions. 101 102 class AnnotationError(SimplifiedError): 103 104 "An error in the annotation process." 105 106 pass 107 108 class AnnotationMessage(Exception): 109 110 "A lesser annotation error." 111 112 pass 113 114 # Annotation. 115 116 class Annotator(Visitor): 117 118 """ 119 The type annotator which traverses the program nodes, typically depth-first, 120 and maintains a record of the current set of types applying to the currently 121 considered operation. Such types are also recorded on the nodes, and a 122 special "system" record is maintained to monitor the level of annotation 123 activity with a view to recognising when no more annotations are possible. 124 125 Throughout the annotation activity, type information consists of lists of 126 Attribute objects where such objects retain information about the context of 127 the type (since a value in the program may be associated with an object or 128 class) and the actual type of the value being manipulated. Upon accessing 129 attribute information on namespaces, additional accessor information is also 130 exchanged - this provides a means of distinguishing between the different 131 types possible when the means of constructing the namespace may depend on 132 run-time behaviour. 133 134 Covered: Assign, CheckType, Conditional, Global, Import, InvokeRef, 135 InvokeFunction, LoadAttr, LoadExc, LoadName, LoadRef, LoadTemp, 136 Module, Not, Pass, Raise, ReleaseTemp, ReturnFromBlock, 137 ReturnFromFunction, StoreAttr, StoreName, StoreTemp, Subprogram, 138 Try. 139 """ 140 141 def __init__(self, importer=None): 142 143 "Initialise the visitor with an optional 'importer'." 144 145 Visitor.__init__(self) 146 self.system = system 147 self.importer = importer or Importer() 148 149 # Satisfy visitor issues. 150 151 self.visitor = self 152 153 def process(self, module, builtins=None): 154 155 """ 156 Process the given 'module', using the optional 'builtins' to access 157 built-in classes and functions. 158 """ 159 160 self.subprograms = [] 161 self.current_subprograms = [] 162 self.current_namespaces = [] 163 self.rerun_subprograms = {} 164 self.namespace = None 165 self.module = module 166 167 # Give constants their own namespace. 168 169 for value, constant in module.simplifier.constants.items(): 170 constant.namespace = Namespace() 171 172 # Process the module, supplying builtins if possible. 173 174 self.builtins = builtins 175 self.global_namespace = Namespace() 176 177 if builtins is not None: 178 self.builtins_namespace = builtins.namespace 179 else: 180 self.builtins_namespace = self.global_namespace 181 182 return self.process_node(module, self.global_namespace) 183 184 def process_node(self, node, locals): 185 186 """ 187 Process a subprogram or module 'node', indicating the initial 'locals'. 188 Return an annotated subprogram or module. Note that this method may 189 mutate nodes in the original program. 190 """ 191 192 # Recursion test. 193 194 if node in self.current_subprograms: 195 if not self.rerun_subprograms.has_key(node): 196 self.rerun_subprograms[node] = [] 197 self.rerun_subprograms[node].append(locals) 198 return node 199 200 # Record the current subprogram and namespace. 201 202 self.current_subprograms.append(node) 203 204 # Determine the namespace. 205 206 self.current_namespaces.append(self.namespace) 207 self.namespace = locals 208 209 # Add namespace details to any structure involved. 210 211 if getattr(node, "structure", None) is not None: 212 node.structure.namespace = Namespace() 213 214 # Initialise bases where appropriate. 215 216 if hasattr(node.structure, "bases"): 217 base_refs = [] 218 for base in node.structure.bases: 219 self.dispatch(base) 220 base_refs.append(self.namespace.types) 221 node.structure.base_refs = base_refs 222 223 # Dispatch to the code itself. 224 225 node.namespace = self.namespace 226 result = self.dispatch(node) 227 228 while self.rerun_subprograms.has_key(node): 229 all_rerun_locals = self.rerun_subprograms[node] 230 del self.rerun_subprograms[node] 231 for rerun_locals in all_rerun_locals: 232 print "Re-running", node, "with", rerun_locals 233 node.namespace = self.namespace = rerun_locals 234 result = self.dispatch(node) 235 236 result.namespace = self.namespace 237 if not hasattr(result, "raises"): 238 result.raises = [] 239 combine(result.raises, self.namespace.raises) 240 241 # Obtain the return values. 242 243 self.last_returns = self.namespace.returns 244 self.last_raises = self.namespace.raises 245 self.returned_locals = self.namespace.return_locals 246 247 # Restore the previous subprogram and namespace. 248 249 self.namespace = self.current_namespaces.pop() 250 self.current_subprograms.pop() 251 252 return result 253 254 def annotate(self, node, types=None): 255 256 """ 257 Annotate the given 'node' in the system, using either the optional 258 'types' or the namespace's current type information. 259 """ 260 261 self.system.annotate(node, types or self.namespace.types) 262 263 def annotate_parameters(self, node, items): 264 265 """ 266 Annotate the given 'node' using the given 'items' and updating the 267 system's annotation counter. 268 """ 269 270 if not hasattr(node, "paramtypes"): 271 node.paramtypes = {} 272 273 for param, types in items: 274 if not node.paramtypes.has_key(param): 275 node.paramtypes[param] = [] 276 self.system.combine(node.paramtypes[param], types) 277 278 # Visitor methods. 279 280 def default(self, node): 281 282 """ 283 Process the given 'node', given that it does not have a specific 284 handler. 285 """ 286 287 raise AnnotationMessage, "Node '%s' not supported." % node 288 289 def dispatch(self, node, *args): 290 try: 291 return Visitor.dispatch(self, node, *args) 292 except AnnotationError, exc: 293 exc.add(node) 294 raise 295 except AnnotationMessage, exc: 296 raise AnnotationError(exc, node) 297 298 # Specific node methods. 299 300 def visitAssign(self, assign): 301 302 """ 303 Return the 'assign' node whose contents (merely a group of nodes) have 304 been processed. 305 """ 306 307 assign.code = self.dispatches(assign.code) 308 return assign 309 310 def visitCheckType(self, checktype): 311 312 """ 313 Return the 'checktype' node, processing the expression to find the 314 possible types of the exception, and processing each choice to build a 315 list of checked types for the exception. 316 """ 317 318 inverted = getattr(checktype, "inverted", 0) 319 checktype.expr = self.dispatch(checktype.expr) 320 321 expr_types = self.namespace.types 322 choice_types = [] 323 choices = [] 324 325 for choice in checktype.choices: 326 choices.append(self.dispatch(choice)) 327 choice_types += self.namespace.types 328 329 for expr_type in expr_types: 330 in_choices = expr_type.type.get_class() in choice_types 331 332 # Filter out types not in the choices list unless the operation is 333 # inverted; in which case, filter out types in the choices list. 334 335 if not inverted and not in_choices or inverted and in_choices: 336 self._prune_non_accesses(checktype.expr, expr_type) 337 338 return checktype 339 340 def visitConditional(self, conditional): 341 342 """ 343 Return the 'conditional' node, processing the test, body and else 344 clauses and recording their processed forms. The body and else clauses 345 are processed within their own namespaces, and the test is also 346 processed in its own namespace if 'isolate_test' is set on the 347 'conditional' node. 348 """ 349 350 # Conditionals keep local namespace changes isolated. 351 # With Return nodes inside the body/else sections, the changes are 352 # communicated to the caller. 353 354 is_module = self.namespace is self.module.namespace 355 356 # Where the test is closely associated with the body, save the namespace 357 # before entering the test. 358 359 if conditional.isolate_test: 360 saved_namespace = self.namespace 361 self.namespace = Namespace() 362 if is_module: 363 self.module.namespace = self.namespace 364 self.namespace.merge_namespace(saved_namespace) 365 366 conditional.test = self.dispatch(conditional.test) 367 368 # Where the test may affect the body and the else clause, save the 369 # namespace after processing the test. 370 371 if not conditional.isolate_test: 372 saved_namespace = self.namespace 373 self.namespace = Namespace() 374 if is_module: 375 self.module.namespace = self.namespace 376 self.namespace.merge_namespace(saved_namespace) 377 378 # NOTE: Exception recording. 379 380 else: 381 test_raises = [] 382 combine(test_raises, self.namespace.raises) 383 384 # Process the body clause. 385 386 conditional.body = self.dispatches(conditional.body) 387 body_namespace = self.namespace 388 389 # Use the saved namespace as a template for the else clause. 390 391 self.namespace = Namespace() 392 if is_module: 393 self.module.namespace = self.namespace 394 self.namespace.merge_namespace(saved_namespace) 395 396 # Process the else clause. 397 398 conditional.else_ = self.dispatches(conditional.else_) 399 else_namespace = self.namespace 400 401 # Merge the body and else namespaces. 402 403 self.namespace = Namespace() 404 if is_module: 405 self.module.namespace = self.namespace 406 self.namespace.merge_namespace(body_namespace) 407 self.namespace.merge_namespace(else_namespace) 408 409 # NOTE: Test of exception type pruning based on the test/body. 410 # Note that the checked exceptions are tested for re-raising. 411 412 if conditional.isolate_test: 413 for exc_type in test_raises: 414 if exc_type not in body_namespace.raises: 415 self.namespace.revoke_exception_type(exc_type) 416 417 return conditional 418 419 def visitGlobal(self, global_): 420 421 """ 422 Return the 'global_' node unprocessed since namespaces should have 423 already been altered to take global names into consideration. 424 """ 425 426 return global_ 427 428 def visitImport(self, import_): 429 430 """ 431 Return the 'import_' node, importing the module with the stated name 432 and storing details on the node. 433 """ 434 435 module = self.importer.load(import_.name, self.builtins, getattr(import_, "alias", None)) 436 if module is not None: 437 self.namespace.set_types([module]) 438 else: 439 self.namespace.set_types([]) 440 self.annotate(import_) # mainly for viewing purposes 441 return import_ 442 443 def _visitInvoke(self, invoke, invocation_types, have_args): 444 445 """ 446 Return the processed 'invoke' node, using the given 'invocation_types' 447 as the list of callables to be investigated for instantiation or for the 448 invocation of functions or blocks. If 'have_args' is a true value, any 449 invocation or instantiation will involve arguments. 450 """ 451 452 # Now locate and invoke the subprogram. This can be complicated because 453 # the target may be a class or object, and there may be many different 454 # related subprograms. 455 456 invocations = [] 457 458 # Visit each callable in turn, finding subprograms. 459 460 for attr in invocation_types: 461 462 # Deal with class invocations by providing instance objects. 463 # Here, each class is queried for the __init__ method, which may 464 # exist for some combinations of classes in a hierarchy but not for 465 # others. 466 467 if isinstance(attr.type, Class): 468 attributes = get_attributes(attr.type, "__init__") 469 470 # Deal with object invocations by using __call__ methods. 471 472 elif isinstance(attr.type, Instance): 473 attributes = get_attributes(attr.type, "__call__") 474 475 # Normal functions or methods are more straightforward. 476 # Here, we model them using an attribute with no context and with 477 # no associated accessor. 478 479 else: 480 attributes = [(attr, None)] 481 482 # Inspect each attribute and extract the subprogram. 483 484 for attribute, accessor in attributes: 485 486 # If a class is involved, presume that it must create a new 487 # object. 488 489 if isinstance(attr.type, Class): 490 491 # Instantiate the class. 492 493 instance = self.new_instance(invoke, attr.type) 494 495 # For instantiations, switch the context. 496 497 if attribute is not None: 498 attribute = Attribute(instance, attribute.type) 499 500 # Request an instance-specific initialiser. 501 502 attribute = attr.type.get_attribute_for_instance(attribute, instance) 503 504 # Skip cases where no callable is found. 505 506 if attribute is not None: 507 508 # If a subprogram is defined, invoke it. 509 510 self.invoke_subprogram(invoke, attribute) 511 if attribute.type not in invocations: 512 invocations.append(attribute.type) 513 514 elif not isinstance(attr.type, Class): 515 print "Invocation type is None for", accessor 516 517 else: 518 519 # Test to see if no arguments were supplied in cases where no 520 # initialiser was found. 521 522 if have_args: 523 raise AnnotationMessage, "No initialiser found for '%s' with arguments." % attr.type 524 525 # Special case: initialisation. 526 527 if isinstance(attr.type, Class): 528 529 # Associate the instance with the result of this invocation. 530 531 self.namespace.set_types([Attribute(None, instance)]) 532 self.annotate(invoke) 533 534 # Remember the invocations that were found, along with the return type 535 # information. 536 537 invoke.invocations = invocations 538 self.namespace.set_types(getattr(invoke, "types", [])) 539 return invoke 540 541 def visitInvokeRef(self, invoke): 542 543 """ 544 Return the processed 'invoke' node, first finding the callables 545 indicated by the reference. 546 """ 547 548 # Where the invocation belongs to an instance but the invoked subprogram 549 # does not, request a special copy. 550 551 instance = getattr(invoke, "instance", None) 552 if instance is not None and getattr(invoke.ref, "instance", None) is None: 553 if invoke.ref.copies.has_key(instance): 554 invoke.ref = invoke.ref.copies[instance] 555 else: 556 invoke.ref = invoke.ref.copy(instance) 557 print "Created", invoke.ref, "for", getattr(invoke.ref, "instance", None) 558 invoke.ref.module.simplifier.subnames[invoke.ref.full_name()] = invoke.ref 559 invocation_types = [Attribute(None, invoke.ref)] 560 return self._visitInvoke(invoke, invocation_types, have_args=0) 561 562 def visitInvokeFunction(self, invoke): 563 564 """ 565 Return the processed 'invoke' node, first finding the callables 566 indicated by the expression. 567 """ 568 569 invoke.expr = self.dispatch(invoke.expr) 570 invocation_types = self.namespace.types 571 572 # Invocation processing starts with making sure that the arguments have 573 # been processed. 574 575 return self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke)) 576 577 def visitLoadAttr(self, loadattr): 578 579 """ 580 Return the 'loadattr' node, processing and storing the expression, and 581 using the expression's types to construct records of accesses and 582 non-accesses using the stated attribute name. 583 """ 584 585 loadattr.expr = self.dispatch(loadattr.expr) 586 types = [] 587 non_accesses = [] 588 accesses = {} 589 for attr in self.namespace.types: 590 attributes = get_attributes(attr.type, loadattr.name) 591 if not attributes: 592 if not attr in non_accesses: 593 non_accesses.append(attr) 594 combine(self.namespace.raises, self.get_builtin_instances(loadattr, "AttributeError")) 595 596 # Revoke this type from any name involved. 597 598 self._prune_non_accesses(loadattr.expr, attr) 599 600 for attribute, accessor in attributes: 601 if attribute is not None: 602 types.append(attribute) 603 if not accesses.has_key(attr.type): 604 accesses[attr.type] = [] 605 if not (attribute, accessor) in accesses[attr.type]: 606 accesses[attr.type].append((attribute, accessor)) 607 else: 608 if not attr in non_accesses: 609 non_accesses.append(attr) 610 combine(self.namespace.raises, self.get_builtin_instances(loadattr, "AttributeError")) 611 612 # Revoke this type from any name involved. 613 614 self._prune_non_accesses(loadattr.expr, attr) 615 616 if not types: 617 print "No attribute found for", loadattr.name, "given", self.namespace.types 618 self.namespace.set_types(types) 619 loadattr.non_accesses = non_accesses 620 loadattr.accesses = accesses 621 self.annotate(loadattr) 622 return loadattr 623 624 def _prune_non_accesses(self, expr, attr): 625 626 """ 627 Prune type information from 'expr' where the given 'attr' has been 628 shown to be a non-access. 629 """ 630 631 if isinstance(expr, LoadName): 632 self.namespace.revoke(expr.name, attr) 633 elif isinstance(expr, LoadExc): 634 self.namespace.revoke_exception_type(attr) 635 elif isinstance(expr, LoadTemp): 636 self.namespace.revoke_temp_type(getattr(expr, "index", None), attr) 637 638 # LoadAttr cannot be pruned since this might unintentionally prune 639 # legitimate types from other applications of the referenced type, it 640 # almost certainly doesn't take "concurrent" mutation into 641 # consideration (where in a running program, the pruned type is actually 642 # reintroduced, making the pruning invalid), and there is no easy way of 643 # preserving the meaning of a namespace without either creating lots of 644 # specialised instances, and even then... 645 646 #elif isinstance(expr, LoadAttr): 647 # for expr_attr in expr.expr.types: 648 # if hasattr(expr_attr.type, "namespace"): 649 # expr_attr.type.namespace.revoke(expr.name, attr) 650 651 def visitLoadExc(self, loadexc): 652 653 """ 654 Return the 'loadexc' node, discovering the possible exception types 655 raised. 656 """ 657 658 self.namespace.set_types(self.namespace.raises[:]) 659 self.annotate(loadexc) 660 return loadexc 661 662 def visitLoadName(self, loadname): 663 664 """ 665 Return the 'loadname' node, processing the name information on the node 666 to determine which types are involved with the name. 667 """ 668 669 self.namespace.set_types(self.namespace.load(loadname.name)) 670 result = loadname 671 self.annotate(result) 672 return result 673 674 def visitLoadRef(self, loadref): 675 676 """ 677 Return the 'loadref' node, obtaining type information about the 678 reference stated on the node. 679 """ 680 681 self.namespace.set_types([Attribute(None, loadref.ref)]) 682 self.annotate(loadref) 683 return loadref 684 685 def visitLoadTemp(self, loadtemp): 686 687 """ 688 Return the 'loadtemp' node, obtaining type information about the 689 temporary variable accessed, and removing variable information where the 690 'release' attribute has been set on the node. 691 """ 692 693 index = getattr(loadtemp, "index", None) 694 try: 695 if getattr(loadtemp, "release", 0): 696 self.namespace.set_types(self.namespace.temp[index].pop()) 697 else: 698 self.namespace.set_types(self.namespace.temp[index][-1]) 699 except KeyError: 700 raise AnnotationMessage, "Temporary store index '%s' not defined." % index 701 self.annotate(loadtemp) 702 return loadtemp 703 704 def visitModule(self, module): 705 706 """ 707 Return the processed 'module' whose contents (merely a group of nodes) 708 are processed. 709 """ 710 711 module.code = self.dispatches(module.code) 712 return module 713 714 def visitNot(self, not_): 715 716 "Return the 'not_' node whose expression is processed." 717 718 not_.expr = self.dispatch(not_.expr) 719 return not_ 720 721 def visitPass(self, pass_): 722 723 "Return the unprocessed 'pass_' node." 724 725 return pass_ 726 727 def visitRaise(self, raise_): 728 729 """ 730 Return the 'raise_' node, processing any traceback information along 731 with the raised exception expression, converting the node into a kind of 732 invocation where the expression is found not to be an invocation itself. 733 This node affects the namespace, adding exception types to the list of 734 those raised in the namespace. 735 """ 736 737 if getattr(raise_, "traceback", None) is not None: 738 raise_.traceback = self.dispatch(raise_.traceback) 739 raise_.expr = self.dispatch(raise_.expr) 740 741 # Handle bare name exceptions by converting any classes to instances. 742 743 if not isinstance(raise_.expr, InvokeFunction): 744 raise_.pos_args = [] 745 raise_.kw_args = {} 746 raise_.star = None 747 raise_.dstar = None 748 types = [] 749 for attr in self.namespace.types: 750 if isinstance(attr.type, Class): 751 self._visitInvoke(raise_, [attr], have_args=0) 752 types += self.namespace.types 753 else: 754 types = self.namespace.types 755 756 combine(self.namespace.raises, types) 757 return raise_ 758 759 def visitReleaseTemp(self, releasetemp): 760 761 """ 762 Return the 'releasetemp' node, removing temporary variable information 763 from the current namespace. 764 """ 765 766 index = getattr(releasetemp, "index", None) 767 try: 768 self.namespace.temp[index].pop() 769 except KeyError: 770 raise AnnotationMessage, "Temporary store index '%s' not defined." % index 771 except IndexError: 772 pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index 773 return releasetemp 774 775 def visitResetExc(self, resetexc): 776 self.namespace.raises = [] 777 return resetexc 778 779 def visitReturn(self, return_): 780 781 """ 782 Return the 'return_' node, processing any expression and obtaining type 783 information to be accumulated in the current namespace's list of return 784 types. A snapshot of the namespace is taken for the purposes of 785 reconciling or merging namespaces where subprograms actually share 786 locals with their callers. 787 """ 788 789 if hasattr(return_, "expr"): 790 return_.expr = self.dispatch(return_.expr) 791 combine(self.namespace.returns, self.namespace.types) 792 self.annotate(return_) 793 self.namespace.snapshot() 794 return return_ 795 796 visitReturnFromBlock = visitReturn 797 visitReturnFromFunction = visitReturn 798 799 def visitStoreAttr(self, storeattr): 800 801 """ 802 Return the 'storeattr' node, processing the expression and target, and 803 using the type information obtained to build records of legitimate 804 writes to the stated attribute, along with "impossible" non-writes to 805 the attribute. 806 """ 807 808 storeattr.expr = self.dispatch(storeattr.expr) 809 expr = self.namespace.types 810 storeattr.lvalue = self.dispatch(storeattr.lvalue) 811 writes = {} 812 non_writes = [] 813 for attr in self.namespace.types: 814 # NOTE: Impose "atomic" constraints on certain types. 815 if attr is None: 816 if not attr in non_writes: 817 non_writes.append(attr) 818 continue 819 attr.type.namespace.add(storeattr.name, expr) 820 writes[attr.type] = attr.type.namespace.load(storeattr.name) 821 if not writes: 822 print "Unable to store attribute", storeattr.name, "given", self.namespace.types 823 storeattr.writes = writes 824 storeattr.non_writes = non_writes 825 return storeattr 826 827 def visitStoreName(self, storename): 828 829 """ 830 Return the 'storename' node, processing the expression on the node and 831 associating the type information obtained with the stated name in the 832 current namespace. 833 """ 834 835 storename.expr = self.dispatch(storename.expr) 836 self.namespace.store(storename.name, self.namespace.types) 837 self.annotate(storename) 838 return storename 839 840 def visitStoreTemp(self, storetemp): 841 842 """ 843 Return the 'storetemp' node, processing the expression on the node and 844 associating the type information obtained with a temporary variable in 845 the current namespace. 846 """ 847 848 storetemp.expr = self.dispatch(storetemp.expr) 849 index = getattr(storetemp, "index", None) 850 if not self.namespace.temp.has_key(index): 851 self.namespace.temp[index] = [] 852 self.namespace.temp[index].append(self.namespace.types) 853 return storetemp 854 855 def visitSubprogram(self, subprogram): 856 857 """ 858 Return the 'subprogram' node, processing its contents (a group of nodes 859 comprising the subprogram). 860 """ 861 862 subprogram.code = self.dispatches(subprogram.code) 863 return subprogram 864 865 def visitTry(self, try_): 866 867 """ 868 Return the 'try_' node, processing the body clause in its own namespace 869 derived from the current namespace, processing any handler clause using 870 the namespace information accumulated in the body, and processing any 871 else and finally clauses, attempting to supply each with appropriate 872 namespace information. 873 """ 874 875 is_module = self.namespace is self.module.namespace 876 877 try_.body = self.dispatches(try_.body) 878 879 # Save the namespace from the body. 880 881 body_namespace = Namespace() 882 body_namespace.merge_namespace(self.namespace) 883 884 # Process the handler. 885 886 if hasattr(try_, "handler"): 887 try_.handler = self.dispatches(try_.handler) 888 889 # Save the namespace from the handler. 890 891 handler_namespace = Namespace() 892 handler_namespace.merge_namespace(self.namespace) 893 894 # Remember the raised exceptions encountered so far. 895 896 raises = self.namespace.raises 897 898 # Process the else clause. 899 900 if hasattr(try_, "else_"): 901 902 # Restore the body namespace for the else clause. 903 904 self.namespace = body_namespace 905 if is_module: 906 self.module.namespace = self.namespace 907 908 # Empty the raised exceptions for the else clause. 909 910 self.namespace.raises = [] 911 try_.else_ = self.dispatches(try_.else_) 912 self.namespace.raises = raises 913 914 # Merge the namespaces. 915 916 self.namespace = Namespace() 917 if is_module: 918 self.module.namespace = self.namespace 919 self.namespace.merge_namespace(body_namespace) 920 self.namespace.merge_namespace(handler_namespace) 921 922 # Process the finally clause, if any. 923 924 try_.finally_ = self.dispatches(try_.finally_) 925 return try_ 926 927 def visitYield(self, yield_): 928 raise NotImplementedError, "The yield statement is not currently supported." 929 930 # Utility methods. 931 932 def get_builtin_instances(self, node, name): 933 return [Attribute(None, self.new_instance(node, attr.type)) for attr in self.builtins.namespace[name]] 934 935 def new_instance(self, node, type): 936 937 "For the given 'node', obtain an instance from the given 'type'." 938 939 if not type.has_instance(node): 940 instance = Instance() 941 instance.namespace = Namespace() 942 instance.namespace.store("__class__", [Attribute(None, type)]) 943 type.add_instance(node, instance) 944 else: 945 instance = type.get_instance(node) 946 947 return instance 948 949 def invoke_subprogram(self, invoke, attribute): 950 951 """ 952 Invoke using the given 'invoke' node the subprogram represented by the 953 given 'attribute'. 954 """ 955 956 # Test for context information, making it into a real attribute. 957 958 if attribute.context is not None: 959 context = Attribute(None, attribute.context) 960 target = attribute.type 961 else: 962 context = None 963 target = attribute.type 964 965 # Test to see if anything has changed. 966 967 if hasattr(invoke, "syscount") and invoke.syscount.has_key(target) and invoke.syscount[target] == self.system.count: 968 return 969 970 # Remember the state of the system. 971 972 else: 973 if not hasattr(invoke, "syscount"): 974 invoke.syscount = {} 975 invoke.syscount[target] = self.system.count 976 977 # Provide the correct namespace for the invocation. 978 # This may be a "shared" namespace... 979 980 if getattr(invoke, "share_locals", 0): 981 namespace = Namespace() 982 namespace.merge_namespace(self.namespace, everything=0) 983 using_module_namespace = self.namespace is self.module.namespace 984 985 # Or it may be a structure... 986 987 elif getattr(target, "structure", None): 988 namespace = Namespace() 989 using_module_namespace = 0 990 991 # Or it may be a new namespace populated with the supplied parameters. 992 993 else: 994 items = self.make_items(invoke, target, context) 995 namespace = Namespace() 996 namespace.merge_items(items) 997 using_module_namespace = 0 998 999 # NOTE: Avoid PEP 227 (nested scopes) whilst permitting references to a 1000 # NOTE: subprogram within itself. Do not define the name of the function 1001 # NOTE: within a method definition. 1002 1003 if getattr(target, "name", None) is not None and not getattr(target, "is_method", 0): 1004 namespace.store(target.name, [Attribute(None, target)]) 1005 1006 # Process the subprogram. 1007 # In order to keep global accesses working, the module namespace must be 1008 # adjusted. 1009 1010 if using_module_namespace: 1011 self.module.namespace = namespace 1012 1013 self.process_node(target, namespace) 1014 1015 # NOTE: Improve and verify this. 1016 # If the invocation returns a value, acquire the return types. 1017 1018 if getattr(target, "returns_value", 0): 1019 self.namespace.set_types(self.last_returns) 1020 self.annotate(invoke) 1021 1022 # If it is a normal block, merge the locals. 1023 # This can happen in addition to the above because for things like 1024 # logical expressions, the namespace can be modified whilst values are 1025 # returned as results. 1026 1027 if getattr(invoke, "share_locals", 0): 1028 self.namespace.reset() 1029 1030 # Merge the locals snapshots. 1031 1032 for locals in self.returned_locals: 1033 1034 # For blocks returning values (such as operations), do not merge 1035 # snapshots or results. 1036 1037 if getattr(target, "returns_value", 0): 1038 self.namespace.merge_namespace(locals, everything=0) 1039 1040 # For blocks not returning values (such as loops), merge 1041 # snapshots and results since they contain details of genuine 1042 # returns. 1043 1044 else: 1045 self.namespace.merge_namespace(locals) 1046 1047 # Incorporate any raised exceptions. 1048 1049 if not hasattr(invoke, "raises"): 1050 invoke.raises = [] 1051 combine(invoke.raises, self.last_raises) 1052 combine(self.namespace.raises, self.last_raises) 1053 1054 # In order to keep global accesses working, the module namespace must be 1055 # adjusted. 1056 1057 if using_module_namespace: 1058 self.module.namespace = self.namespace 1059 1060 def process_args(self, invocation): 1061 1062 """ 1063 Process the arguments associated with an 'invocation'. Return whether 1064 any arguments were processed. 1065 """ 1066 1067 invocation.pos_args = self.dispatches(invocation.pos_args) 1068 invocation.kw_args = self.dispatch_dict(invocation.kw_args) 1069 1070 # Get type information for star and dstar arguments. 1071 1072 if invocation.star is not None: 1073 param, default = invocation.star 1074 default = self.dispatch(default) 1075 invocation.star = param, default 1076 1077 if invocation.dstar is not None: 1078 param, default = invocation.dstar 1079 default = self.dispatch(default) 1080 invocation.dstar = param, default 1081 1082 if invocation.pos_args or invocation.kw_args or invocation.star or invocation.dstar: 1083 return 1 1084 else: 1085 return 0 1086 1087 def make_items(self, invocation, subprogram, context): 1088 1089 """ 1090 Make an items mapping for the 'invocation' of the 'subprogram' using the 1091 given 'context' (which may be None). 1092 """ 1093 1094 if context is not None: 1095 pos_args = [Self(context)] + invocation.pos_args 1096 else: 1097 pos_args = invocation.pos_args 1098 1099 # Duplicate the keyword arguments - we remove them in processing below. 1100 1101 kw_args = {} 1102 kw_args.update(invocation.kw_args) 1103 1104 # Sort the arguments into positional and keyword arguments. 1105 1106 params = subprogram.params 1107 items = [] 1108 star_args = [] 1109 1110 # Match each positional argument, taking excess arguments as star args. 1111 1112 for arg in pos_args: 1113 if params: 1114 param, default = params[0] 1115 if arg is None: 1116 arg = default 1117 if hasattr(arg, "types"): 1118 items.append((param, arg.types)) 1119 else: 1120 items.append((param, [])) # Annotation has not succeeded. 1121 params = params[1:] 1122 else: 1123 star_args.append(arg) 1124 1125 # Collect the remaining defaults. 1126 1127 while params: 1128 param, default = params[0] 1129 if kw_args.has_key(param): 1130 arg = kw_args[param] 1131 del kw_args[param] 1132 elif default is not None: 1133 arg = self.dispatch(default) 1134 else: 1135 raise AnnotationMessage, "No argument supplied in '%s' for parameter '%s'." % (subprogram, param) 1136 if hasattr(arg, "types"): 1137 items.append((param, arg.types)) 1138 else: 1139 items.append((param, [])) # Annotation has not succeeded. 1140 params = params[1:] 1141 1142 dstar_args = kw_args.items() 1143 1144 # Construct temporary objects. 1145 1146 if star_args: 1147 star_invocation = self.make_star_args(invocation, subprogram, star_args) 1148 self.dispatch(star_invocation) 1149 star_types = star_invocation.types 1150 else: 1151 star_types = None 1152 1153 if dstar_args: 1154 dstar_invocation = self.make_dstar_args(invocation, subprogram, dstar_args) 1155 self.dispatch(dstar_invocation) 1156 dstar_types = dstar_invocation.types 1157 else: 1158 dstar_types = None 1159 1160 # NOTE: Merge the objects properly. 1161 1162 star_types = star_types or invocation.star and invocation.star.types 1163 dstar_types = dstar_types or invocation.dstar and invocation.dstar.types 1164 1165 # Add star and dstar. 1166 1167 if star_types is not None: 1168 if subprogram.star is not None: 1169 param, default = subprogram.star 1170 items.append((param, star_types)) 1171 else: 1172 raise AnnotationMessage, "Invocation provides unwanted *args." 1173 elif subprogram.star is not None: 1174 param, default = subprogram.star 1175 if not hasattr(default, "types"): 1176 subprogram.star = param, self.dispatch(default) # NOTE: Review reprocessing. 1177 items.append((param, default.types)) 1178 1179 if dstar_types is not None: 1180 if subprogram.dstar is not None: 1181 param, default = subprogram.dstar 1182 items.append((param, dstar_types)) 1183 else: 1184 raise AnnotationMessage, "Invocation provides unwanted **args." 1185 elif subprogram.dstar is not None: 1186 param, default = subprogram.dstar 1187 if not hasattr(default, "types"): 1188 subprogram.dstar = param, self.dispatch(default) # NOTE: Review reprocessing. 1189 items.append((param, default.types)) 1190 1191 # Record the parameter types. 1192 1193 self.annotate_parameters(subprogram, items) 1194 return subprogram.paramtypes.items() 1195 1196 def make_star_args(self, invocation, subprogram, star_args): 1197 1198 "Make a subprogram which initialises a list containing 'star_args'." 1199 1200 if not hasattr(invocation, "stars"): 1201 invocation.stars = {} 1202 1203 if not invocation.stars.has_key(subprogram.full_name()): 1204 instance = getattr(invocation, "instance", None) 1205 1206 code=[ 1207 StoreTemp( 1208 instance=instance, 1209 expr=InvokeFunction( 1210 invocation.original, 1211 instance=instance, 1212 expr=LoadAttr( 1213 instance=instance, 1214 expr=LoadRef( 1215 instance=instance, 1216 ref=self.builtins 1217 ), 1218 name="list", 1219 nstype="module", 1220 ), 1221 args=[], 1222 star=None, 1223 dstar=None 1224 ) 1225 ) 1226 ] 1227 1228 for arg in star_args: 1229 code.append( 1230 InvokeFunction( 1231 invocation.original, 1232 instance=instance, 1233 expr=LoadAttr( 1234 instance=instance, 1235 expr=LoadTemp( 1236 instance=instance 1237 ), 1238 name="append" 1239 ), 1240 args=[arg], 1241 star=None, 1242 dstar=None 1243 ) 1244 ) 1245 1246 code += [ 1247 Return( 1248 instance=instance, 1249 expr=LoadTemp( 1250 instance=instance, 1251 release=1 1252 ) 1253 ) 1254 ] 1255 1256 invocation.stars[subprogram.full_name()] = InvokeRef( 1257 invocation.original, 1258 instance=instance, 1259 produces_result=1, 1260 ref=Subprogram( 1261 instance=instance, 1262 name=None, 1263 returns_value=1, 1264 params=[], 1265 star=None, 1266 dstar=None, 1267 code=code 1268 ) 1269 ) 1270 1271 return invocation.stars[subprogram.full_name()] 1272 1273 def make_dstar_args(self, invocation, subprogram, dstar_args): 1274 1275 """ 1276 Make a subprogram which initialises a dictionary built from the given 1277 'dstar_args'. 1278 """ 1279 1280 if not hasattr(invocation, "dstars"): 1281 invocation.dstars = {} 1282 1283 if not invocation.dstars.has_key(subprogram.full_name()): 1284 instance = getattr(invocation, "instance", None) 1285 1286 code=[ 1287 StoreTemp( 1288 instance=instance, 1289 expr=InvokeFunction( 1290 invocation.original, 1291 instance=instance, 1292 expr=LoadAttr( 1293 instance=instance, 1294 expr=LoadRef( 1295 instance=instance, 1296 ref=self.builtins 1297 ), 1298 name="dict", 1299 nstype="module", 1300 ) 1301 ) 1302 ) 1303 ] 1304 1305 for arg, value in dstar_args: 1306 1307 # NOTE: Constant not added to table. 1308 1309 constant = Constant( 1310 instance=instance, 1311 name=repr(arg), value=arg, namespace=Namespace() 1312 ) 1313 #constant.namespace.store("__class__", self.get_builtin_instances(invocation, constant.typename)) 1314 code += [ 1315 StoreTemp( 1316 instance=instance, 1317 expr=LoadRef( 1318 instance=instance, 1319 ref=constant 1320 ), 1321 index="const" 1322 ), 1323 StoreAttr( 1324 instance=instance, 1325 lvalue=LoadTemp( 1326 instance=instance, 1327 index="const" 1328 ), 1329 name="__class__", 1330 expr=LoadAttr( 1331 instance=instance, 1332 expr=LoadRef( 1333 instance=instance, 1334 ref=self.builtins 1335 ), 1336 name=constant.typename, 1337 nstype="module", 1338 ) 1339 ), 1340 InvokeFunction( 1341 invocation.original, 1342 instance=instance, 1343 expr=LoadAttr( 1344 instance=instance, 1345 expr=LoadTemp( 1346 instance=instance 1347 ), 1348 name="__setitem__" 1349 ), 1350 args=[ 1351 LoadTemp( 1352 instance=instance, 1353 index="const", 1354 release=1 1355 ), 1356 value 1357 ] 1358 ) 1359 ] 1360 1361 code += [ 1362 Return( 1363 instance=instance, 1364 expr=LoadTemp( 1365 instance=instance, 1366 release=1 1367 ) 1368 ) 1369 ] 1370 1371 invocation.dstars[subprogram.full_name()] = InvokeRef( 1372 invocation.original, 1373 instance=instance, 1374 produces_result=1, 1375 ref=Subprogram( 1376 instance=instance, 1377 name=None, 1378 returns_value=1, 1379 params=[], 1380 star=None, 1381 dstar=None, 1382 code=code 1383 ) 1384 ) 1385 1386 return invocation.dstars[subprogram.full_name()] 1387 1388 # Namespace-related abstractions. 1389 1390 class Namespace: 1391 1392 """ 1393 A local namespace which may either relate to a genuine set of function 1394 locals or the initialisation of a structure or module. 1395 """ 1396 1397 def __init__(self): 1398 1399 """ 1400 Initialise the namespace with a mapping of local names to possible 1401 types, a list of return values and of possible returned local 1402 namespaces. The namespace also tracks the "current" types and a mapping 1403 of temporary value names to types. 1404 """ 1405 1406 self.names = {} 1407 self.returns = [] 1408 self.return_locals = [] 1409 self.raises = [] 1410 self.temp = {} 1411 self.types = [] 1412 1413 def set_types(self, types): 1414 1415 "Set the current collection of 'types'." 1416 1417 self.types = types 1418 1419 def add(self, name, types): 1420 1421 "Add to the entry with the given 'name' the specified 'types'." 1422 1423 if self.names.has_key(name): 1424 combine(self.names[name], types) 1425 else: 1426 self.store(name, types) 1427 1428 def store(self, name, types): 1429 1430 "Store in (or associate with) the given 'name' the specified 'types'." 1431 1432 self.names[name] = types 1433 1434 __setitem__ = store 1435 1436 def load(self, name): 1437 1438 "Load the types associated with the given 'name'." 1439 1440 return self.names[name] 1441 1442 __getitem__ = load 1443 1444 def has_key(self, name): 1445 return self.names.has_key(name) 1446 1447 def revoke(self, name, type): 1448 1449 "Revoke from the entry for the given 'name' the specified 'type'." 1450 1451 new_types = self.names[name][:] 1452 new_types.remove(type) 1453 self.names[name] = new_types 1454 1455 def revoke_exception_type(self, type): 1456 1457 "Revoke the given 'type' from the collection of exception types." 1458 1459 self.raises.remove(type) 1460 1461 def revoke_temp_type(self, index, type): 1462 1463 "Revoke from the temporary variable 'index' the given 'type'." 1464 1465 new_types = self.temp[index][-1][:] 1466 new_types.remove(type) 1467 self.temp[index][-1] = new_types 1468 1469 def merge_namespace(self, namespace, everything=1): 1470 1471 """ 1472 Merge items from the given 'namespace' with this namespace. When the 1473 optional 'everything' parameter is set to a false value (unlike the 1474 default), return values and locals snapshots will not be copied to this 1475 namespace. 1476 """ 1477 1478 self.merge_items(namespace.names.items()) 1479 if everything: 1480 combine(self.returns, namespace.returns) 1481 combine(self.return_locals, namespace.return_locals) 1482 combine(self.raises, namespace.raises) 1483 for name, values in namespace.temp.items(): 1484 if values: 1485 if not self.temp.has_key(name) or not self.temp[name]: 1486 self.temp[name] = [[]] 1487 combine(self.temp[name][-1], values[-1]) 1488 1489 def merge_items(self, items): 1490 1491 "Merge the given 'items' with this namespace." 1492 1493 for name, types in items: 1494 self.merge(name, types) 1495 1496 def merge(self, name, types): 1497 1498 "Merge the entry for the given 'name' and 'types' with this namespace." 1499 1500 if not self.names.has_key(name): 1501 self.names[name] = types[:] 1502 else: 1503 existing = self.names[name] 1504 combine(existing, types) 1505 1506 def snapshot(self): 1507 1508 "Make a snapshot of the locals and remember them." 1509 1510 namespace = Namespace() 1511 namespace.merge_namespace(self) 1512 self.return_locals.append(namespace) 1513 1514 def reset(self): 1515 1516 "Reset a namespace in preparation for merging with returned locals." 1517 1518 self.names = {} 1519 1520 def __repr__(self): 1521 return repr(self.names) 1522 1523 class Importer: 1524 1525 "An import machine, searching for and loading modules." 1526 1527 def __init__(self, path=None): 1528 1529 """ 1530 Initialise the importer with the given search 'path' - a list of 1531 directories to search for Python modules. 1532 """ 1533 1534 self.path = path or [os.getcwd()] 1535 self.modules = {} 1536 1537 def find_in_path(self, name): 1538 1539 """ 1540 Find the given module 'name' in the search path, returning None where no 1541 such module could be found, or a 2-tuple from the 'find' method 1542 otherwise. 1543 """ 1544 1545 for d in self.path: 1546 m = self.find(d, name) 1547 if m: return m 1548 return None 1549 1550 def find(self, d, name): 1551 1552 """ 1553 In the directory 'd', find the given module 'name', where 'name' can 1554 either refer to a single file module or to a package. Return None if the 1555 'name' cannot be associated with either a file or a package directory, 1556 or a 2-tuple from '_find_package' or '_find_module' otherwise. 1557 """ 1558 1559 m = self._find_package(d, name) 1560 if m: return m 1561 m = self._find_module(d, name) 1562 if m: return m 1563 return None 1564 1565 def _find_module(self, d, name): 1566 1567 """ 1568 In the directory 'd', find the given module 'name', returning None where 1569 no suitable file exists in the directory, or a 2-tuple consisting of 1570 None (indicating that no package directory is involved) and a filename 1571 indicating the location of the module. 1572 """ 1573 1574 name_py = name + os.extsep + "py" 1575 filename = self._find_file(d, name_py) 1576 if filename: 1577 return None, filename 1578 return None 1579 1580 def _find_package(self, d, name): 1581 1582 """ 1583 In the directory 'd', find the given package 'name', returning None 1584 where no suitable package directory exists, or a 2-tuple consisting of 1585 a directory (indicating the location of the package directory itself) 1586 and a filename indicating the location of the __init__.py module which 1587 declares the package's top-level contents. 1588 """ 1589 1590 filename = self._find_file(d, name) 1591 if filename: 1592 init_py = "__init__" + os.path.extsep + "py" 1593 init_py_filename = self._find_file(filename, init_py) 1594 if init_py_filename: 1595 return filename, init_py_filename 1596 return None 1597 1598 def _find_file(self, d, filename): 1599 1600 """ 1601 Return the filename obtained when searching the directory 'd' for the 1602 given 'filename', or None if no actual file exists for the filename. 1603 """ 1604 1605 filename = os.path.join(d, filename) 1606 if os.path.exists(filename): 1607 return filename 1608 else: 1609 return None 1610 1611 def load(self, name, builtins, alias=None): 1612 1613 """ 1614 Load the module or package with the given 'name' and using the specified 1615 'builtins'. Return an Attribute object referencing the loaded module or 1616 package, or None if no such module or package exists. 1617 """ 1618 1619 if self.modules.has_key(name): 1620 return Attribute(None, self.modules[name]) 1621 1622 path = name.split(".") 1623 m = self.find_in_path(path[0]) 1624 if not m: 1625 return None # NOTE: Import error. 1626 d, filename = m 1627 1628 if self.modules.has_key(path[0]): 1629 top = module = self.modules[path[0]] 1630 else: 1631 top = module = self.modules[path[0]] = load(filename, builtins, path[0], self, no_annotate=1) 1632 annotate(module, builtins, self) 1633 1634 if len(path) > 1: 1635 path_so_far = path[:1] 1636 for p in path[1:]: 1637 path_so_far.append(p) 1638 m = self.find(d, p) 1639 if not m: 1640 return None # NOTE: Import error. 1641 d, filename = m 1642 module_name = ".".join(path_so_far) 1643 1644 if self.modules.has_key(module_name): 1645 submodule = self.modules[module_name] 1646 else: 1647 submodule = self.modules[module_name] = load(filename, builtins, module_name, self, no_annotate=1) 1648 annotate(submodule, builtins, self) 1649 1650 # Store the submodule within its parent module. 1651 1652 module.namespace[p] = [Attribute(None, submodule)] 1653 module = submodule 1654 1655 if alias: 1656 return Attribute(None, module) 1657 else: 1658 return Attribute(None, top) 1659 1660 def combine(target, additions): 1661 1662 """ 1663 Merge into the 'target' sequence the given 'additions', preventing duplicate 1664 items. 1665 """ 1666 1667 for addition in additions: 1668 if addition not in target: 1669 target.append(addition) 1670 1671 def find_attributes(structure, name): 1672 1673 """ 1674 Find for the given 'structure' all attributes for the given 'name', visiting 1675 base classes where appropriate and returning the attributes in order of 1676 descending precedence for all possible base classes. 1677 1678 The elements in the result list are 2-tuples which contain the attribute and 1679 the structure involved in accessing the attribute. 1680 """ 1681 1682 # First attempt to search the instance/class namespace. 1683 1684 try: 1685 l = structure.namespace.load(name) 1686 attributes = [] 1687 for attribute in l: 1688 attributes.append((attribute, structure)) 1689 1690 # If that does not work, attempt to investigate any class or base classes. 1691 1692 except KeyError: 1693 attributes = [] 1694 1695 # Investigate any instance's implementing class. 1696 1697 if isinstance(structure, Instance): 1698 for attr in structure.namespace.load("__class__"): 1699 cls = attr.type 1700 l = get_attributes(cls, name) 1701 combine(attributes, l) 1702 1703 # Investigate any class's base classes. 1704 1705 elif isinstance(structure, Class): 1706 1707 # If no base classes exist, return an indicator that no attribute 1708 # exists. 1709 1710 if not structure.base_refs: 1711 return [(None, structure)] 1712 1713 # Otherwise, find all possible base classes. 1714 1715 for base_refs in structure.base_refs: 1716 base_attributes = [] 1717 1718 # For each base class, find attributes either in the base 1719 # class or its own base classes. 1720 1721 for base_ref in base_refs: 1722 l = get_attributes(base_ref, name) 1723 combine(base_attributes, l) 1724 1725 combine(attributes, base_attributes) 1726 1727 return attributes 1728 1729 def get_attributes(structure, name): 1730 1731 """ 1732 Return all possible attributes for the given 'structure' having the given 1733 'name', wrapping each attribute in an Attribute object which includes 1734 context information for the attribute access. 1735 1736 The elements in the result list are 2-tuples which contain the attribute and 1737 the structure involved in accessing the attribute. 1738 """ 1739 1740 if isinstance(structure, Attribute): 1741 structure = structure.type 1742 results = [] 1743 for attribute, accessor in find_attributes(structure, name): 1744 1745 # Detect class attribute access via instances. 1746 1747 if attribute is not None and isinstance(structure, Instance) and isinstance(accessor, Class): 1748 attribute = accessor.get_attribute_for_instance(attribute, structure) 1749 1750 # Produce an attribute with the appropriate context. 1751 1752 if attribute is not None and isinstance(structure, Structure): 1753 results.append((Attribute(structure, attribute.type), accessor)) 1754 else: 1755 results.append((attribute, accessor)) 1756 1757 return results 1758 1759 # Convenience functions. 1760 1761 def load(name, builtins=None, module_name=None, importer=None, no_annotate=0): 1762 1763 """ 1764 Load the module with the given 'name' (which may be a full module path), 1765 using the optional 'builtins' to resolve built-in names, and using the 1766 optional 'importer' to provide a means of finding and loading modules. 1767 """ 1768 1769 module = simplify.simplify(name, builtins is None, module_name) 1770 fixnames.fix(module, builtins) 1771 if not no_annotate: 1772 annotate(module, builtins, importer) 1773 return module 1774 1775 def annotate(module, builtins=None, importer=None): 1776 1777 """ 1778 Annotate the given 'module', also employing the optional 'builtins' module, 1779 if specified. If the optional 'importer' is given, use that to find and load 1780 modules. 1781 """ 1782 1783 annotator = Annotator(importer) 1784 if builtins is not None: 1785 annotator.process(module, builtins) 1786 else: 1787 annotator.process(module) 1788 1789 # vim: tabstop=4 expandtab shiftwidth=4