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