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