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 def visitLoadRef(self, loadref): 237 self.namespace.set_types([Attribute(None, loadref.ref)]) 238 self.annotate(loadref) 239 return loadref 240 241 def visitLoadName(self, loadname): 242 self.namespace.set_types(self.namespace.load(loadname.name)) 243 result = loadname 244 self.annotate(result) 245 return result 246 247 def visitStoreName(self, storename): 248 storename.expr = self.dispatch(storename.expr) 249 self.namespace.store(storename.name, self.namespace.types) 250 return storename 251 252 def visitLoadTemp(self, loadtemp): 253 index = getattr(loadtemp, "index", None) 254 try: 255 if getattr(loadtemp, "release", 0): 256 self.namespace.set_types(self.namespace.temp[index].pop()) 257 else: 258 self.namespace.set_types(self.namespace.temp[index][-1]) 259 except KeyError: 260 raise AnnotationMessage, "Temporary store index '%s' not defined." % index 261 self.annotate(loadtemp) 262 return loadtemp 263 264 def visitStoreTemp(self, storetemp): 265 storetemp.expr = self.dispatch(storetemp.expr) 266 index = getattr(storetemp, "index", None) 267 if not self.namespace.temp.has_key(index): 268 self.namespace.temp[index] = [] 269 self.namespace.temp[index].append(self.namespace.types) 270 return storetemp 271 272 def visitReleaseTemp(self, releasetemp): 273 index = getattr(releasetemp, "index", None) 274 try: 275 self.namespace.temp[index].pop() 276 except KeyError: 277 raise AnnotationMessage, "Temporary store index '%s' not defined." % index 278 except IndexError: 279 pass #raise AnnotationMessage, "Temporary store index '%s' is empty." % index 280 return releasetemp 281 282 def visitLoadAttr(self, loadattr): 283 loadattr.expr = self.dispatch(loadattr.expr) 284 types = [] 285 accesses = {} 286 non_accesses = {} 287 for attr in self.namespace.types: 288 attributes = get_attributes(attr.type, loadattr.name) 289 if not attributes: 290 print "No attributes for", loadattr.name, "in", attr.type 291 for attribute, accessor in attributes: 292 if attribute is not None: 293 types.append(attribute) 294 if not accesses.has_key(attr.type): 295 accesses[attr.type] = [] 296 if not (attribute, accessor) in accesses[attr.type]: 297 accesses[attr.type].append((attribute, accessor)) 298 else: 299 print "Empty attribute", loadattr.name, "via accessor", accessor 300 if not non_accesses.has_key(attr.type): 301 non_accesses[attr.type] = [] 302 if not (attribute, accessor) in non_accesses[attr.type]: 303 non_accesses[attr.type].append((attribute, accessor)) 304 self.namespace.set_types(types) 305 loadattr.accesses = accesses 306 loadattr.non_accesses = non_accesses 307 self.annotate(loadattr) 308 return loadattr 309 310 def visitStoreAttr(self, storeattr): 311 storeattr.expr = self.dispatch(storeattr.expr) 312 expr = self.namespace.types 313 storeattr.lvalue = self.dispatch(storeattr.lvalue) 314 writes = {} 315 for attr in self.namespace.types: 316 if attr is None: 317 print "Empty attribute storage attempt" 318 continue 319 attr.type.namespace.store(storeattr.name, expr) 320 writes[attr.type] = attr.type.namespace.load(storeattr.name) 321 storeattr.writes = writes 322 return storeattr 323 324 def visitConditional(self, conditional): 325 326 # Conditionals keep local namespace changes isolated. 327 # With Return nodes inside the body/else sections, the changes are 328 # communicated to the caller. 329 330 conditional.test = self.dispatch(conditional.test) 331 saved_namespace = self.namespace 332 333 self.namespace = Namespace() 334 self.namespace.merge_namespace(saved_namespace) 335 conditional.body = self.dispatches(conditional.body) 336 body_namespace = self.namespace 337 338 self.namespace = Namespace() 339 self.namespace.merge_namespace(saved_namespace) 340 conditional.else_ = self.dispatches(conditional.else_) 341 else_namespace = self.namespace 342 343 self.namespace = Namespace() 344 self.namespace.merge_namespace(body_namespace) 345 self.namespace.merge_namespace(else_namespace) 346 347 return conditional 348 349 def visitReturn(self, return_): 350 if hasattr(return_, "expr"): 351 return_.expr = self.dispatch(return_.expr) 352 combine(self.namespace.returns, self.namespace.types) 353 self.annotate(return_) 354 self.namespace.snapshot() 355 return return_ 356 357 def visitInvoke(self, invoke): 358 359 # First find the callables. 360 361 invoke.expr = self.dispatch(invoke.expr) 362 invocation_types = self.namespace.types 363 364 # Invocation processing starts with making sure that the arguments have 365 # been processed. 366 367 if isinstance(invoke, InvokeFunction): 368 self.process_args(invoke) 369 370 # Now locate and invoke the subprogram. This can be complicated because 371 # the target may be a class or object, and there may be many different 372 # related subprograms. 373 374 invocations = [] 375 376 # Visit each callable in turn, finding subprograms. 377 378 for attr in invocation_types: 379 380 # Deal with class invocations by providing instance objects. 381 # Here, each class is queried for the __init__ method, which may 382 # exist for some combinations of classes in a hierarchy but not for 383 # others. 384 385 if isinstance(attr.type, Class): 386 attributes = get_attributes(attr.type, "__init__") 387 388 # Deal with object invocations by using __call__ methods. 389 390 elif isinstance(attr.type, Instance): 391 attributes = get_attributes(attr.type, "__call__") 392 393 # Normal functions or methods are more straightforward. 394 # Here, we model them using an attribute with no context and with 395 # no associated accessor. 396 397 else: 398 attributes = [(attr, None)] 399 400 # Inspect each attribute and extract the subprogram. 401 402 for attribute, accessor in attributes: 403 404 # If a class is involved, presume that it must create a new 405 # object. 406 407 if isinstance(attr.type, Class): 408 409 # Instantiate the class. 410 # NOTE: Should probably only allocate a single instance. 411 412 instance = self.new_instance(invoke, "new", attr.type.full_name(), attr.type) 413 414 # For instantiations, switch the context. 415 416 if attribute is not None: 417 attribute = Attribute(instance, attribute.type) 418 419 # Skip cases where no callable is found. 420 421 if attribute is not None: 422 423 # If a subprogram is defined, invoke it. 424 425 self.invoke_subprogram(invoke, attribute) 426 if attribute.type not in invocations: 427 invocations.append(attribute.type) 428 429 elif not isinstance(attr.type, Class): 430 print "Invocation type is None for", accessor 431 432 # Special case: initialisation. 433 434 if isinstance(attr.type, Class): 435 436 # Associate the instance with the result of this invocation. 437 438 self.namespace.set_types([Attribute(None, instance)]) 439 self.annotate(invoke) 440 441 # Remember the invocations that were found, along with the return type 442 # information. 443 444 invoke.invocations = invocations 445 self.namespace.set_types(getattr(invoke, "types", [])) 446 return invoke 447 448 visitInvokeFunction = visitInvoke 449 visitInvokeBlock = visitInvoke 450 451 # Utility methods. 452 453 def new_instance(self, node, reason, target, type): 454 455 "Create, on the given 'node', a new instance with the given 'type'." 456 457 if not hasattr(node, "instances"): 458 node.instances = {} 459 460 if not node.instances.has_key((reason, target, type)): 461 462 # Insist on a single instance per type. 463 # NOTE: Strategy-dependent instantiation. 464 465 if len(type.instances) == 0: 466 instance = Instance() 467 instance.namespace = Namespace() 468 instance.namespace.store("__class__", [Attribute(None, type)]) 469 type.instances.append(instance) 470 else: 471 instance = type.instances[0] 472 473 node.instances[(reason, target, type)] = instance 474 475 return node.instances[(reason, target, type)] 476 477 def invoke_subprogram(self, invoke, subprogram): 478 479 "Invoke using the given 'invoke' node the given 'subprogram'." 480 481 # Test to see if anything has changed. 482 483 if hasattr(invoke, "syscount") and invoke.syscount == self.system.count: 484 return 485 486 # Remember the state of the system. 487 488 else: 489 invoke.syscount = self.system.count 490 491 # Test for context information, making it into a real attribute. 492 493 if subprogram.context is not None: 494 context = Attribute(None, subprogram.context) 495 target = subprogram.type 496 else: 497 context = None 498 target = subprogram.type 499 500 # Provide the correct namespace for the invocation. 501 502 if isinstance(invoke, InvokeBlock): 503 namespace = Namespace() 504 namespace.merge_namespace(self.namespace) 505 else: 506 items = self.make_items(invoke, target, context) 507 namespace = self.make_namespace(items) 508 509 # Process the subprogram. 510 511 self.process_node(target, namespace) 512 513 # NOTE: Improve and verify this. 514 # If the invocation returns a value, acquire the return types. 515 516 if getattr(target, "returns_value", 0): 517 self.namespace.set_types(self.last_returns) 518 self.annotate(invoke) 519 520 # Otherwise, if it is a normal block, merge the locals. 521 522 elif isinstance(invoke, InvokeBlock): 523 for locals in self.returned_locals: 524 self.namespace.merge_namespace(locals) 525 526 def process_args(self, invocation): 527 528 "Process the arguments associated with an 'invocation'." 529 530 invocation.pos_args = self.dispatches(invocation.pos_args) 531 invocation.kw_args = self.dispatch_dict(invocation.kw_args) 532 533 # Get type information for star and dstar arguments. 534 535 if invocation.star is not None: 536 param, default = invocation.star 537 default = self.dispatch(default) 538 invocation.star = param, default 539 540 if invocation.dstar is not None: 541 param, default = invocation.dstar 542 default = self.dispatch(default) 543 invocation.dstar = param, default 544 545 def make_items(self, invocation, subprogram, context): 546 547 """ 548 Make an items mapping for the 'invocation' of the 'subprogram' using the 549 given 'context' (which may be None). 550 """ 551 552 if context is not None: 553 pos_args = [Self(context)] + invocation.pos_args 554 else: 555 pos_args = invocation.pos_args 556 kw_args = invocation.kw_args 557 558 # Sort the arguments into positional and keyword arguments. 559 560 params = subprogram.params 561 items = [] 562 star_args = [] 563 564 # Match each positional argument, taking excess arguments as star args. 565 566 for arg in pos_args: 567 if params: 568 param, default = params[0] 569 if arg is None: 570 arg = default 571 items.append((param, arg.types)) 572 params = params[1:] 573 else: 574 star_args.append(arg) 575 576 # Collect the remaining defaults. 577 578 while params: 579 param, default = params[0] 580 if kw_args.has_key(param): 581 arg = kw_args[param] 582 del kw_args[param] 583 elif default is not None: 584 arg = self.dispatch(default) 585 else: 586 raise AnnotationMessage, "No argument supplied in '%s' for parameter '%s'." % (subprogram, param) 587 items.append((param, arg.types)) 588 params = params[1:] 589 590 dstar_args = kw_args.values() 591 592 # Construct temporary objects. 593 594 if star_args: 595 star_invocation = self.make_star_args(invocation, subprogram, star_args) 596 self.dispatch(star_invocation) 597 star_types = star_invocation.types 598 else: 599 star_types = None 600 601 if dstar_args: 602 dstar_invocation = self.make_dstar_args(invocation, subprogram, dstar_args) # NOTE: To be written! 603 self.dispatch(dstar_invocation) 604 dstar_types = dstar_invocation.types 605 else: 606 dstar_types = None 607 608 # NOTE: Merge the objects properly. 609 610 star_types = star_types or invocation.star and invocation.star.types 611 dstar_types = dstar_types or invocation.dstar and invocation.dstar.types 612 613 # Add star and dstar. 614 615 if star_types is not None: 616 if subprogram.star is not None: 617 param, default = subprogram.star 618 items.append((param, star_types)) 619 else: 620 raise AnnotationMessage, "Invocation provides unwanted *args." 621 elif subprogram.star is not None: 622 param, default = subprogram.star 623 arg = self.dispatch(default) # NOTE: Review reprocessing. 624 items.append((param, arg.types)) 625 626 if dstar_types is not None: 627 if subprogram.dstar is not None: 628 param, default = subprogram.dstar 629 items.append((param, dstar_types)) 630 else: 631 raise AnnotationMessage, "Invocation provides unwanted **args." 632 elif subprogram.dstar is not None: 633 param, default = subprogram.dstar 634 arg = self.dispatch(default) # NOTE: Review reprocessing. 635 items.append((param, arg.types)) 636 637 # Record the parameter types. 638 639 subprogram.paramtypes = {} 640 for param, types in items: 641 subprogram.paramtypes[param] = types 642 643 return items 644 645 def make_star_args(self, invocation, subprogram, star_args): 646 647 "Make a subprogram which initialises a list containing 'star_args'." 648 649 if not hasattr(invocation, "stars"): 650 invocation.stars = {} 651 652 if not invocation.stars.has_key(subprogram.full_name()): 653 code=[ 654 StoreTemp( 655 expr=InvokeFunction( 656 expr=LoadAttr( 657 expr=LoadRef( 658 ref=self.builtins 659 ), 660 name="list", 661 nstype="module", 662 ), 663 args=[], 664 star=None, 665 dstar=None 666 ) 667 ) 668 ] 669 670 for arg in star_args: 671 code.append( 672 InvokeFunction( 673 expr=LoadAttr( 674 expr=LoadTemp(), 675 name="append" 676 ), 677 args=[arg], 678 star=None, 679 dstar=None 680 ) 681 ) 682 683 code += [ 684 Return(expr=LoadTemp(release=1)) 685 ] 686 687 invocation.stars[subprogram.full_name()] = InvokeBlock( 688 produces_result=1, 689 expr=LoadRef( 690 ref=Subprogram( 691 name=None, 692 returns_value=1, 693 params=[], 694 star=None, 695 dstar=None, 696 code=code 697 ) 698 ) 699 ) 700 701 return invocation.stars[subprogram.full_name()] 702 703 def make_namespace(self, items): 704 namespace = Namespace() 705 namespace.merge_items(items) 706 return namespace 707 708 # Namespace-related abstractions. 709 710 class Namespace: 711 712 """ 713 A local namespace which may either relate to a genuine set of function 714 locals or the initialisation of a structure or module. 715 """ 716 717 def __init__(self): 718 719 """ 720 Initialise the namespace with a mapping of local names to possible 721 types, a list of return values and of possible returned local 722 namespaces. The namespace also tracks the "current" types and a mapping 723 of temporary value names to types. 724 """ 725 726 self.names = {} 727 self.returns = [] 728 self.return_locals = [] 729 self.temp = {} 730 self.types = [] 731 732 def set_types(self, types): 733 self.types = types 734 735 def store(self, name, types): 736 self.names[name] = types 737 738 __setitem__ = store 739 740 def load(self, name): 741 return self.names[name] 742 743 __getitem__ = load 744 745 def merge_namespace(self, namespace): 746 self.merge_items(namespace.names.items()) 747 combine(self.returns, namespace.returns) 748 self.temp = namespace.temp 749 750 def merge_items(self, items): 751 for name, types in items: 752 self.merge(name, types) 753 754 def merge(self, name, types): 755 if not self.names.has_key(name): 756 self.names[name] = types[:] 757 else: 758 existing = self.names[name] 759 combine(existing, types) 760 761 def snapshot(self): 762 763 "Make a snapshot of the locals and remember them." 764 765 namespace = Namespace() 766 namespace.merge_namespace(self) 767 self.return_locals.append(namespace) 768 769 def __repr__(self): 770 return repr(self.names) 771 772 class Attribute: 773 774 """ 775 An attribute abstraction, indicating the type of the attribute along with 776 its context or origin. 777 """ 778 779 def __init__(self, context, type): 780 self.context = context 781 self.type = type 782 783 def __eq__(self, other): 784 return hasattr(other, "type") and other.type == self.type or other == self.type 785 786 def __repr__(self): 787 return "Attribute(%s, %s)" % (repr(self.context), repr(self.type)) 788 789 class Self: 790 791 "A node encapsulating object/context information in an argument list." 792 793 def __init__(self, attribute): 794 self.types = [attribute] 795 796 def combine(target, additions): 797 798 """ 799 Merge into the 'target' sequence the given 'additions', preventing duplicate 800 items. 801 """ 802 803 for addition in additions: 804 if addition not in target: 805 target.append(addition) 806 807 def find_attributes(structure, name): 808 809 """ 810 Find for the given 'structure' all attributes for the given 'name', visiting 811 base classes where appropriate and returning the attributes in order of 812 descending precedence for all possible base classes. 813 814 The elements in the result list are 2-tuples which contain the attribute and 815 the structure involved in accessing the attribute. 816 """ 817 818 # First attempt to search the instance/class namespace. 819 820 try: 821 l = structure.namespace.load(name) 822 attributes = [] 823 for attribute in l: 824 attributes.append((attribute, structure)) 825 826 # If that does not work, attempt to investigate any class or base classes. 827 828 except KeyError: 829 attributes = [] 830 831 # Investigate any instance's implementing class. 832 833 if isinstance(structure, Instance): 834 for attr in structure.namespace.load("__class__"): 835 cls = attr.type 836 l = get_attributes(cls, name) 837 combine(attributes, l) 838 839 # Investigate any class's base classes. 840 841 elif isinstance(structure, Class): 842 843 # If no base classes exist, return an indicator that no attribute 844 # exists. 845 846 if not structure.base_refs: 847 return [(None, structure)] 848 849 # Otherwise, find all possible base classes. 850 851 for base_refs in structure.base_refs: 852 base_attributes = [] 853 854 # For each base class, find attributes either in the base 855 # class or its own base classes. 856 857 for base_ref in base_refs: 858 l = get_attributes(base_ref, name) 859 combine(base_attributes, l) 860 861 combine(attributes, base_attributes) 862 863 return attributes 864 865 def get_attributes(structure, name): 866 867 """ 868 Return all possible attributes for the given 'structure' having the given 869 'name', wrapping each attribute in an Attribute object which includes 870 context information for the attribute access. 871 872 The elements in the result list are 2-tuples which contain the attribute and 873 the structure involved in accessing the attribute. 874 """ 875 876 if isinstance(structure, Attribute): 877 structure = structure.type 878 results = [] 879 for attribute, accessor in find_attributes(structure, name): 880 if attribute is not None and isinstance(structure, Structure): 881 results.append((Attribute(structure, attribute.type), accessor)) 882 else: 883 results.append((attribute, accessor)) 884 return results 885 886 # Convenience functions. 887 888 def annotate(module, builtins=None): 889 890 """ 891 Annotate the given 'module', also employing the optional 'builtins' module, 892 if specified. 893 """ 894 895 annotator = Annotator() 896 if builtins is not None: 897 annotator.process(module, builtins) 898 else: 899 annotator.process(module) 900 901 def annotate_all(modules, builtins): 902 annotate(builtins) 903 for module in modules: 904 annotate(module, builtins) 905 906 # vim: tabstop=4 expandtab shiftwidth=4