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 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 annotate function: 28 29 annotate(module, builtins) 30 31 The more complicated approach involves obtaining an Annotator: 32 33 annotator = Annotator() 34 35 Then, processing an existing module with it: 36 37 annotator.process(module) 38 39 If a module containing built-in classes and functions has already been 40 annotated, such a module should be passed in as an additional argument: 41 42 annotator.process(module, builtins) 43 """ 44 45 from simplified import * 46 import compiler 47 48 class System: 49 50 """ 51 A class maintaining the state of the annotation system. When the system 52 counter can no longer be incremented by any annotation operation, the 53 system may be considered stable and fully annotated. 54 """ 55 56 def __init__(self): 57 self.count = 0 58 def init(self, node): 59 if not hasattr(node, "types"): 60 node.types = [] 61 def annotate(self, node, types): 62 self.init(node) 63 for type in types: 64 if type not in node.types: 65 node.types.append(type) 66 self.count += 1 67 68 system = System() 69 70 # Exceptions. 71 72 class AnnotationError(SimplifiedError): 73 74 "An error in the annotation process." 75 76 pass 77 78 class AnnotationMessage(Exception): 79 80 "A lesser annotation error." 81 82 pass 83 84 # Annotation. 85 86 class Annotator(Visitor): 87 88 """ 89 The type annotator which traverses the program nodes, typically depth-first, 90 and maintains a record of the current set of types applying to the currently 91 considered operation. Such types are also recorded on the nodes, and a 92 special "system" record is maintained to monitor the level of annotation 93 activity with a view to recognising when no more annotations are possible. 94 95 Throughout the annotation activity, type information consists of lists of 96 Attribute objects where such objects retain information about the context of 97 the type (since a value in the program may be associated with an object or 98 class) and the actual type of the value being manipulated. Upon accessing 99 attribute information on namespaces, additional accessor information is also 100 exchanged - this provides a means of distinguishing between the different 101 types possible when the means of constructing the namespace may depend on 102 run-time behaviour. 103 """ 104 105 def __init__(self): 106 107 "Initialise the visitor." 108 109 Visitor.__init__(self) 110 self.system = system 111 112 # Satisfy visitor issues. 113 114 self.visitor = self 115 116 def process(self, module, builtins=None): 117 118 """ 119 Process the given 'module', using the optional 'builtins' to access 120 built-in classes and functions. 121 """ 122 123 self.subprograms = [] 124 self.current_subprograms = [] 125 self.current_namespaces = [] 126 127 # Give constants their own namespace. 128 129 for value, constant in module.simplifier.constants.items(): 130 constant.namespace = Namespace() 131 132 # Process the module, supplying builtins if possible. 133 134 self.builtins = builtins 135 self.global_namespace = Namespace() 136 137 if builtins is not None: 138 self.builtins_namespace = builtins.namespace 139 else: 140 self.builtins_namespace = self.global_namespace 141 142 return self.process_node(module) 143 144 def process_node(self, node, locals=None): 145 146 """ 147 Process a subprogram or module 'node', indicating any initial 'locals'. 148 Return an annotated subprogram or module. Note that this method may 149 mutate nodes in the original program. 150 """ 151 152 # Determine the namespace. 153 154 if locals is not None: 155 self.namespace = locals 156 else: 157 self.namespace = self.global_namespace 158 159 # Record the current subprogram and namespace. 160 161 self.current_subprograms.append(node) 162 self.current_namespaces.append(self.namespace) 163 164 # Add namespace details to any structure involved. 165 166 if getattr(node, "structure", None) is not None: 167 node.structure.namespace = Namespace() 168 169 # Initialise bases where appropriate. 170 171 if hasattr(node.structure, "bases"): 172 base_refs = [] 173 for base in node.structure.bases: 174 self.dispatch(base) 175 base_refs.append(self.namespace.types) 176 node.structure.base_refs = base_refs 177 178 # Dispatch to the code itself. 179 180 node.namespace = self.namespace 181 result = self.dispatch(node) 182 result.namespace = self.namespace 183 184 # Obtain the return values. 185 186 self.last_returns = self.namespace.returns 187 self.returned_locals = self.namespace.return_locals 188 189 # Restore the previous subprogram and namespace. 190 191 self.current_namespaces.pop() 192 if self.current_namespaces: 193 self.namespace = self.current_namespaces[-1] 194 195 self.current_subprograms.pop() 196 197 return result 198 199 def annotate(self, node, types=None): 200 201 """ 202 Annotate the given 'node' in the system, using either the optional 203 'types' or the namespace's current type information. 204 """ 205 206 self.system.annotate(node, types or self.namespace.types) 207 208 # Visitor methods. 209 210 def default(self, node): 211 212 """ 213 Process the given 'node', given that it does not have a specific 214 handler. 215 """ 216 217 for attr in ("expr", "lvalue", "test", "handler"): 218 value = getattr(node, attr, None) 219 if value is not None: 220 setattr(node, attr, self.dispatch(value)) 221 for attr in ("body", "else_", "finally_", "code"): 222 value = getattr(node, attr, None) 223 if value is not None: 224 setattr(node, attr, self.dispatches(value)) 225 return node 226 227 def dispatch(self, node, *args): 228 try: 229 return Visitor.dispatch(self, node, *args) 230 except AnnotationError, exc: 231 exc.add(node) 232 raise 233 except AnnotationMessage, exc: 234 raise AnnotationError(exc, node) 235 236 # Program structure/control-flow. 237 238 def visitConditional(self, conditional): 239 240 # Conditionals keep local namespace changes isolated. 241 # With Return nodes inside the body/else sections, the changes are 242 # communicated to the caller. 243 244 conditional.test = self.dispatch(conditional.test) 245 saved_namespace = self.namespace 246 247 self.namespace = Namespace() 248 self.namespace.merge_namespace(saved_namespace) 249 conditional.body = self.dispatches(conditional.body) 250 body_namespace = self.namespace 251 252 self.namespace = Namespace() 253 self.namespace.merge_namespace(saved_namespace) 254 conditional.else_ = self.dispatches(conditional.else_) 255 else_namespace = self.namespace 256 257 self.namespace = Namespace() 258 self.namespace.merge_namespace(body_namespace) 259 self.namespace.merge_namespace(else_namespace) 260 261 return conditional 262 263 # Namespace operations. 264 265 def visitLoadAttr(self, loadattr): 266 loadattr.expr = self.dispatch(loadattr.expr) 267 types = [] 268 accesses = {} 269 non_accesses = {} 270 for attr in self.namespace.types: 271 attributes = get_attributes(attr.type, loadattr.name) 272 if not attributes: 273 print "No attributes for", loadattr.name, "in", attr.type 274 for attribute, accessor in attributes: 275 if attribute is not None: 276 types.append(attribute) 277 if not accesses.has_key(attr.type): 278 accesses[attr.type] = [] 279 if not (attribute, accessor) in accesses[attr.type]: 280 accesses[attr.type].append((attribute, accessor)) 281 else: 282 print "Empty attribute", loadattr.name, "via accessor", accessor 283 if not non_accesses.has_key(attr.type): 284 non_accesses[attr.type] = [] 285 if not (attribute, accessor) in non_accesses[attr.type]: 286 non_accesses[attr.type].append((attribute, accessor)) 287 self.namespace.set_types(types) 288 loadattr.accesses = accesses 289 loadattr.non_accesses = non_accesses 290 self.annotate(loadattr) 291 return loadattr 292 293 def visitLoadName(self, loadname): 294 self.namespace.set_types(self.namespace.load(loadname.name)) 295 result = loadname 296 self.annotate(result) 297 return result 298 299 def visitLoadRef(self, loadref): 300 self.namespace.set_types([Attribute(None, loadref.ref)]) 301 self.annotate(loadref) 302 return loadref 303 304 def visitLoadTemp(self, loadtemp): 305 index = getattr(loadtemp, "index", None) 306 try: 307 if getattr(loadtemp, "release", 0): 308 self.namespace.set_types(self.namespace.temp[index].pop()) 309 else: 310 self.namespace.set_types(self.namespace.temp[index][-1]) 311 except KeyError: 312 raise AnnotationMessage, "Temporary store index '%s' not defined." % index 313 self.annotate(loadtemp) 314 return loadtemp 315 316 def visitRaise(self, raise_): 317 raise_.traceback = self.dispatch(raise_.traceback) 318 raise_.expr = self.dispatch(raise_.expr) 319 320 # Handle bare name exceptions by converting any classes to instances. 321 322 if not isinstance(raise_.expr, InvokeFunction): 323 raise_.pos_args = [] 324 raise_.kw_args = {} 325 raise_.star = None 326 raise_.dstar = None 327 types = [] 328 for attr in self.namespace.types: 329 if isinstance(attr.type, Class): 330 self._visitInvoke(raise_, [attr], have_args=0) 331 types += self.namespace.types 332 else: 333 types = self.namespace.types 334 335 combine(self.namespace.raises, types) 336 return raise_ 337 338 def visitReleaseTemp(self, releasetemp): 339 index = getattr(releasetemp, "index", None) 340 try: 341 self.namespace.temp[index].pop() 342 except KeyError: 343 raise AnnotationMessage, "Temporary store index '%s' not defined." % index 344 except IndexError: 345 pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index 346 return releasetemp 347 348 def visitReturn(self, return_): 349 if hasattr(return_, "expr"): 350 return_.expr = self.dispatch(return_.expr) 351 combine(self.namespace.returns, self.namespace.types) 352 self.annotate(return_) 353 self.namespace.snapshot() 354 return return_ 355 356 def visitStoreAttr(self, storeattr): 357 storeattr.expr = self.dispatch(storeattr.expr) 358 expr = self.namespace.types 359 storeattr.lvalue = self.dispatch(storeattr.lvalue) 360 writes = {} 361 for attr in self.namespace.types: 362 if attr is None: 363 print "Empty attribute storage attempt" 364 continue 365 attr.type.namespace.store(storeattr.name, expr) 366 writes[attr.type] = attr.type.namespace.load(storeattr.name) 367 storeattr.writes = writes 368 return storeattr 369 370 def visitStoreName(self, storename): 371 storename.expr = self.dispatch(storename.expr) 372 self.namespace.store(storename.name, self.namespace.types) 373 return storename 374 375 def visitStoreTemp(self, storetemp): 376 storetemp.expr = self.dispatch(storetemp.expr) 377 index = getattr(storetemp, "index", None) 378 if not self.namespace.temp.has_key(index): 379 self.namespace.temp[index] = [] 380 self.namespace.temp[index].append(self.namespace.types) 381 return storetemp 382 383 # Invocations are a chapter of their own. 384 385 def visitInvokeBlock(self, invoke): 386 387 # First find the callables. 388 389 invoke.expr = self.dispatch(invoke.expr) 390 invocation_types = self.namespace.types 391 return self._visitInvoke(invoke, invocation_types, have_args=0) 392 393 def visitInvokeFunction(self, invoke): 394 395 # First find the callables. 396 397 invoke.expr = self.dispatch(invoke.expr) 398 invocation_types = self.namespace.types 399 400 # Invocation processing starts with making sure that the arguments have 401 # been processed. 402 403 return self._visitInvoke(invoke, invocation_types, have_args=self.process_args(invoke)) 404 405 def _visitInvoke(self, invoke, invocation_types, have_args): 406 407 # Now locate and invoke the subprogram. This can be complicated because 408 # the target may be a class or object, and there may be many different 409 # related subprograms. 410 411 invocations = [] 412 413 # Visit each callable in turn, finding subprograms. 414 415 for attr in invocation_types: 416 417 # Deal with class invocations by providing instance objects. 418 # Here, each class is queried for the __init__ method, which may 419 # exist for some combinations of classes in a hierarchy but not for 420 # others. 421 422 if isinstance(attr.type, Class): 423 attributes = get_attributes(attr.type, "__init__") 424 425 # Deal with object invocations by using __call__ methods. 426 427 elif isinstance(attr.type, Instance): 428 attributes = get_attributes(attr.type, "__call__") 429 430 # Normal functions or methods are more straightforward. 431 # Here, we model them using an attribute with no context and with 432 # no associated accessor. 433 434 else: 435 attributes = [(attr, None)] 436 437 # Inspect each attribute and extract the subprogram. 438 439 for attribute, accessor in attributes: 440 441 # If a class is involved, presume that it must create a new 442 # object. 443 444 if isinstance(attr.type, Class): 445 446 # Instantiate the class. 447 # NOTE: Should probably only allocate a single instance. 448 449 instance = self.new_instance(invoke, "new", attr.type.full_name(), attr.type) 450 451 # For instantiations, switch the context. 452 453 if attribute is not None: 454 attribute = Attribute(instance, attribute.type) 455 456 # Skip cases where no callable is found. 457 458 if attribute is not None: 459 460 # If a subprogram is defined, invoke it. 461 462 self.invoke_subprogram(invoke, attribute) 463 if attribute.type not in invocations: 464 invocations.append(attribute.type) 465 466 elif not isinstance(attr.type, Class): 467 print "Invocation type is None for", accessor 468 469 else: 470 471 # Test to see if no arguments were supplied in cases where no 472 # initialiser was found. 473 474 if have_args: 475 raise AnnotationMessage, "No initialiser found for '%s' with arguments." % attr.type 476 477 # Special case: initialisation. 478 479 if isinstance(attr.type, Class): 480 481 # Associate the instance with the result of this invocation. 482 483 self.namespace.set_types([Attribute(None, instance)]) 484 self.annotate(invoke) 485 486 # Remember the invocations that were found, along with the return type 487 # information. 488 489 invoke.invocations = invocations 490 self.namespace.set_types(getattr(invoke, "types", [])) 491 return invoke 492 493 # Utility methods. 494 495 def new_instance(self, node, reason, target, type): 496 497 "Create, on the given 'node', a new instance with the given 'type'." 498 499 if not hasattr(node, "instances"): 500 node.instances = {} 501 502 if not node.instances.has_key((reason, target, type)): 503 504 # Insist on a single instance per type. 505 # NOTE: Strategy-dependent instantiation. 506 507 if len(type.instances) == 0: 508 instance = Instance() 509 instance.namespace = Namespace() 510 instance.namespace.store("__class__", [Attribute(None, type)]) 511 type.instances.append(instance) 512 else: 513 instance = type.instances[0] 514 515 node.instances[(reason, target, type)] = instance 516 517 return node.instances[(reason, target, type)] 518 519 def invoke_subprogram(self, invoke, subprogram): 520 521 "Invoke using the given 'invoke' node the given 'subprogram'." 522 523 # Test to see if anything has changed. 524 525 if hasattr(invoke, "syscount") and invoke.syscount == self.system.count: 526 return 527 528 # Remember the state of the system. 529 530 else: 531 invoke.syscount = self.system.count 532 533 # Test for context information, making it into a real attribute. 534 535 if subprogram.context is not None: 536 context = Attribute(None, subprogram.context) 537 target = subprogram.type 538 else: 539 context = None 540 target = subprogram.type 541 542 # Provide the correct namespace for the invocation. 543 544 if getattr(invoke, "share_locals", 0): 545 namespace = Namespace() 546 namespace.merge_namespace(self.namespace) 547 elif getattr(target, "structure", None): 548 namespace = Namespace() 549 else: 550 items = self.make_items(invoke, target, context) 551 namespace = self.make_namespace(items) 552 553 # Process the subprogram. 554 555 self.process_node(target, namespace) 556 557 # NOTE: Improve and verify this. 558 # If the invocation returns a value, acquire the return types. 559 560 if getattr(target, "returns_value", 0): 561 self.namespace.set_types(self.last_returns) 562 self.annotate(invoke) 563 564 # Otherwise, assuming it is a normal block, merge the locals. 565 566 elif getattr(invoke, "share_locals", 0): 567 for locals in self.returned_locals: 568 self.namespace.merge_namespace(locals) 569 570 def process_args(self, invocation): 571 572 """ 573 Process the arguments associated with an 'invocation'. Return whether 574 any arguments were processed. 575 """ 576 577 invocation.pos_args = self.dispatches(invocation.pos_args) 578 invocation.kw_args = self.dispatch_dict(invocation.kw_args) 579 580 # Get type information for star and dstar arguments. 581 582 if invocation.star is not None: 583 param, default = invocation.star 584 default = self.dispatch(default) 585 invocation.star = param, default 586 587 if invocation.dstar is not None: 588 param, default = invocation.dstar 589 default = self.dispatch(default) 590 invocation.dstar = param, default 591 592 if invocation.pos_args or invocation.kw_args or invocation.star or invocation.dstar: 593 return 1 594 else: 595 return 0 596 597 def make_items(self, invocation, subprogram, context): 598 599 """ 600 Make an items mapping for the 'invocation' of the 'subprogram' using the 601 given 'context' (which may be None). 602 """ 603 604 if context is not None: 605 pos_args = [Self(context)] + invocation.pos_args 606 else: 607 pos_args = invocation.pos_args 608 kw_args = invocation.kw_args 609 610 # Sort the arguments into positional and keyword arguments. 611 612 params = subprogram.params 613 items = [] 614 star_args = [] 615 616 # Match each positional argument, taking excess arguments as star args. 617 618 for arg in pos_args: 619 if params: 620 param, default = params[0] 621 if arg is None: 622 arg = default 623 items.append((param, arg.types)) 624 params = params[1:] 625 else: 626 star_args.append(arg) 627 628 # Collect the remaining defaults. 629 630 while params: 631 param, default = params[0] 632 if kw_args.has_key(param): 633 arg = kw_args[param] 634 del kw_args[param] 635 elif default is not None: 636 arg = self.dispatch(default) 637 else: 638 raise AnnotationMessage, "No argument supplied in '%s' for parameter '%s'." % (subprogram, param) 639 items.append((param, arg.types)) 640 params = params[1:] 641 642 dstar_args = kw_args.values() 643 644 # Construct temporary objects. 645 646 if star_args: 647 star_invocation = self.make_star_args(invocation, subprogram, star_args) 648 self.dispatch(star_invocation) 649 star_types = star_invocation.types 650 else: 651 star_types = None 652 653 if dstar_args: 654 dstar_invocation = self.make_dstar_args(invocation, subprogram, dstar_args) # NOTE: To be written! 655 self.dispatch(dstar_invocation) 656 dstar_types = dstar_invocation.types 657 else: 658 dstar_types = None 659 660 # NOTE: Merge the objects properly. 661 662 star_types = star_types or invocation.star and invocation.star.types 663 dstar_types = dstar_types or invocation.dstar and invocation.dstar.types 664 665 # Add star and dstar. 666 667 if star_types is not None: 668 if subprogram.star is not None: 669 param, default = subprogram.star 670 items.append((param, star_types)) 671 else: 672 raise AnnotationMessage, "Invocation provides unwanted *args." 673 elif subprogram.star is not None: 674 param, default = subprogram.star 675 arg = self.dispatch(default) # NOTE: Review reprocessing. 676 items.append((param, arg.types)) 677 678 if dstar_types is not None: 679 if subprogram.dstar is not None: 680 param, default = subprogram.dstar 681 items.append((param, dstar_types)) 682 else: 683 raise AnnotationMessage, "Invocation provides unwanted **args." 684 elif subprogram.dstar is not None: 685 param, default = subprogram.dstar 686 arg = self.dispatch(default) # NOTE: Review reprocessing. 687 items.append((param, arg.types)) 688 689 # Record the parameter types. 690 691 subprogram.paramtypes = {} 692 for param, types in items: 693 subprogram.paramtypes[param] = types 694 695 return items 696 697 def make_star_args(self, invocation, subprogram, star_args): 698 699 "Make a subprogram which initialises a list containing 'star_args'." 700 701 if not hasattr(invocation, "stars"): 702 invocation.stars = {} 703 704 if not invocation.stars.has_key(subprogram.full_name()): 705 code=[ 706 StoreTemp( 707 expr=InvokeFunction( 708 expr=LoadAttr( 709 expr=LoadRef( 710 ref=self.builtins 711 ), 712 name="list", 713 nstype="module", 714 ), 715 args=[], 716 star=None, 717 dstar=None 718 ) 719 ) 720 ] 721 722 for arg in star_args: 723 code.append( 724 InvokeFunction( 725 expr=LoadAttr( 726 expr=LoadTemp(), 727 name="append" 728 ), 729 args=[arg], 730 star=None, 731 dstar=None 732 ) 733 ) 734 735 code += [ 736 Return(expr=LoadTemp(release=1)) 737 ] 738 739 invocation.stars[subprogram.full_name()] = InvokeBlock( 740 produces_result=1, 741 expr=LoadRef( 742 ref=Subprogram( 743 name=None, 744 returns_value=1, 745 params=[], 746 star=None, 747 dstar=None, 748 code=code 749 ) 750 ) 751 ) 752 753 return invocation.stars[subprogram.full_name()] 754 755 def make_namespace(self, items): 756 namespace = Namespace() 757 namespace.merge_items(items) 758 return namespace 759 760 # Namespace-related abstractions. 761 762 class Namespace: 763 764 """ 765 A local namespace which may either relate to a genuine set of function 766 locals or the initialisation of a structure or module. 767 """ 768 769 def __init__(self): 770 771 """ 772 Initialise the namespace with a mapping of local names to possible 773 types, a list of return values and of possible returned local 774 namespaces. The namespace also tracks the "current" types and a mapping 775 of temporary value names to types. 776 """ 777 778 self.names = {} 779 self.returns = [] 780 self.return_locals = [] 781 self.raises = [] 782 self.temp = {} 783 self.types = [] 784 785 def set_types(self, types): 786 self.types = types 787 788 def store(self, name, types): 789 self.names[name] = types 790 791 __setitem__ = store 792 793 def load(self, name): 794 return self.names[name] 795 796 __getitem__ = load 797 798 def merge_namespace(self, namespace): 799 self.merge_items(namespace.names.items()) 800 combine(self.returns, namespace.returns) 801 self.temp = namespace.temp 802 803 def merge_items(self, items): 804 for name, types in items: 805 self.merge(name, types) 806 807 def merge(self, name, types): 808 if not self.names.has_key(name): 809 self.names[name] = types[:] 810 else: 811 existing = self.names[name] 812 combine(existing, types) 813 814 def snapshot(self): 815 816 "Make a snapshot of the locals and remember them." 817 818 namespace = Namespace() 819 namespace.merge_namespace(self) 820 self.return_locals.append(namespace) 821 822 def __repr__(self): 823 return repr(self.names) 824 825 class Attribute: 826 827 """ 828 An attribute abstraction, indicating the type of the attribute along with 829 its context or origin. 830 """ 831 832 def __init__(self, context, type): 833 self.context = context 834 self.type = type 835 836 def __eq__(self, other): 837 return hasattr(other, "type") and other.type == self.type or other == self.type 838 839 def __repr__(self): 840 return "Attribute(%s, %s)" % (repr(self.context), repr(self.type)) 841 842 class Self: 843 844 "A node encapsulating object/context information in an argument list." 845 846 def __init__(self, attribute): 847 self.types = [attribute] 848 849 def combine(target, additions): 850 851 """ 852 Merge into the 'target' sequence the given 'additions', preventing duplicate 853 items. 854 """ 855 856 for addition in additions: 857 if addition not in target: 858 target.append(addition) 859 860 def find_attributes(structure, name): 861 862 """ 863 Find for the given 'structure' all attributes for the given 'name', visiting 864 base classes where appropriate and returning the attributes in order of 865 descending precedence for all possible base classes. 866 867 The elements in the result list are 2-tuples which contain the attribute and 868 the structure involved in accessing the attribute. 869 """ 870 871 # First attempt to search the instance/class namespace. 872 873 try: 874 l = structure.namespace.load(name) 875 attributes = [] 876 for attribute in l: 877 attributes.append((attribute, structure)) 878 879 # If that does not work, attempt to investigate any class or base classes. 880 881 except KeyError: 882 attributes = [] 883 884 # Investigate any instance's implementing class. 885 886 if isinstance(structure, Instance): 887 for attr in structure.namespace.load("__class__"): 888 cls = attr.type 889 l = get_attributes(cls, name) 890 combine(attributes, l) 891 892 # Investigate any class's base classes. 893 894 elif isinstance(structure, Class): 895 896 # If no base classes exist, return an indicator that no attribute 897 # exists. 898 899 if not structure.base_refs: 900 return [(None, structure)] 901 902 # Otherwise, find all possible base classes. 903 904 for base_refs in structure.base_refs: 905 base_attributes = [] 906 907 # For each base class, find attributes either in the base 908 # class or its own base classes. 909 910 for base_ref in base_refs: 911 l = get_attributes(base_ref, name) 912 combine(base_attributes, l) 913 914 combine(attributes, base_attributes) 915 916 return attributes 917 918 def get_attributes(structure, name): 919 920 """ 921 Return all possible attributes for the given 'structure' having the given 922 'name', wrapping each attribute in an Attribute object which includes 923 context information for the attribute access. 924 925 The elements in the result list are 2-tuples which contain the attribute and 926 the structure involved in accessing the attribute. 927 """ 928 929 if isinstance(structure, Attribute): 930 structure = structure.type 931 results = [] 932 for attribute, accessor in find_attributes(structure, name): 933 if attribute is not None and isinstance(structure, Structure): 934 results.append((Attribute(structure, attribute.type), accessor)) 935 else: 936 results.append((attribute, accessor)) 937 return results 938 939 # Convenience functions. 940 941 def annotate(module, builtins=None): 942 943 """ 944 Annotate the given 'module', also employing the optional 'builtins' module, 945 if specified. 946 """ 947 948 annotator = Annotator() 949 if builtins is not None: 950 annotator.process(module, builtins) 951 else: 952 annotator.process(module) 953 954 def annotate_all(modules, builtins): 955 annotate(builtins) 956 for module in modules: 957 annotate(module, builtins) 958 959 # vim: tabstop=4 expandtab shiftwidth=4