1 #!/usr/bin/env python 2 3 """ 4 Common functions. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 7 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk> 8 9 This program is free software; you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free Software 11 Foundation; either version 3 of the License, or (at your option) any later 12 version. 13 14 This program is distributed in the hope that it will be useful, but WITHOUT 15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 details. 18 19 You should have received a copy of the GNU General Public License along with 20 this program. If not, see <http://www.gnu.org/licenses/>. 21 """ 22 23 from errors import * 24 from os import listdir, makedirs, remove 25 from os.path import exists, isdir, join, split 26 from results import ConstantValueRef, LiteralSequenceRef, NameRef 27 from compiler.transformer import Transformer 28 import compiler.ast 29 30 class CommonOutput: 31 32 "Common output functionality." 33 34 def check_output(self): 35 36 "Check the existing output and remove it if irrelevant." 37 38 if not exists(self.output): 39 makedirs(self.output) 40 41 details = self.importer.get_cache_details() 42 recorded_details = self.get_output_details() 43 44 if recorded_details != details: 45 self.remove_output() 46 47 writefile(self.get_output_details_filename(), details) 48 49 def get_output_details_filename(self): 50 51 "Return the output details filename." 52 53 return join(self.output, "$details") 54 55 def get_output_details(self): 56 57 "Return details of the existing output." 58 59 details_filename = self.get_output_details_filename() 60 61 if not exists(details_filename): 62 return None 63 else: 64 return readfile(details_filename) 65 66 def remove_output(self, dirname=None): 67 68 "Remove the output." 69 70 dirname = dirname or self.output 71 72 for filename in listdir(dirname): 73 path = join(dirname, filename) 74 if isdir(path): 75 self.remove_output(path) 76 else: 77 remove(path) 78 79 class CommonModule: 80 81 "A common module representation." 82 83 def __init__(self, name, importer): 84 85 """ 86 Initialise this module with the given 'name' and an 'importer' which is 87 used to provide access to other modules when required. 88 """ 89 90 self.name = name 91 self.importer = importer 92 self.filename = None 93 94 # Inspection-related attributes. 95 96 self.astnode = None 97 self.encoding = None 98 self.iterators = {} 99 self.temp = {} 100 self.lambdas = {} 101 102 # Constants, literals and values. 103 104 self.constants = {} 105 self.constant_values = {} 106 self.literals = {} 107 self.literal_types = {} 108 109 # Nested namespaces. 110 111 self.namespace_path = [] 112 self.in_function = False 113 114 # Retain the assignment value expression and track invocations. 115 116 self.in_assignment = None 117 self.in_invocation = False 118 119 # Attribute chain state management. 120 121 self.attrs = [] 122 self.chain_assignment = [] 123 self.chain_invocation = [] 124 125 def __repr__(self): 126 return "CommonModule(%r, %r)" % (self.name, self.importer) 127 128 def parse_file(self, filename): 129 130 "Parse the file with the given 'filename', initialising attributes." 131 132 self.filename = filename 133 134 # Use the Transformer directly to obtain encoding information. 135 136 t = Transformer() 137 f = open(filename) 138 139 try: 140 self.astnode = t.parsesuite(f.read() + "\n") 141 self.encoding = t.encoding 142 finally: 143 f.close() 144 145 # Module-relative naming. 146 147 def get_global_path(self, name): 148 return "%s.%s" % (self.name, name) 149 150 def get_namespace_path(self): 151 return ".".join([self.name] + self.namespace_path) 152 153 def get_object_path(self, name): 154 return ".".join([self.name] + self.namespace_path + [name]) 155 156 def get_parent_path(self): 157 return ".".join([self.name] + self.namespace_path[:-1]) 158 159 # Namespace management. 160 161 def enter_namespace(self, name): 162 163 "Enter the namespace having the given 'name'." 164 165 self.namespace_path.append(name) 166 167 def exit_namespace(self): 168 169 "Exit the current namespace." 170 171 self.namespace_path.pop() 172 173 # Constant reference naming. 174 175 def get_constant_name(self, value, value_type): 176 177 """ 178 Add a new constant to the current namespace for 'value' with 179 'value_type'. 180 """ 181 182 path = self.get_namespace_path() 183 init_item(self.constants, path, dict) 184 return "$c%d" % add_counter_item(self.constants[path], (value, value_type)) 185 186 # Literal reference naming. 187 188 def get_literal_name(self): 189 190 "Add a new literal to the current namespace." 191 192 path = self.get_namespace_path() 193 init_item(self.literals, path, lambda: 0) 194 return "$C%d" % self.literals[path] 195 196 def next_literal(self): 197 self.literals[self.get_namespace_path()] += 1 198 199 # Temporary iterator naming. 200 201 def get_iterator_path(self): 202 return self.in_function and self.get_namespace_path() or self.name 203 204 def get_iterator_name(self): 205 path = self.get_iterator_path() 206 init_item(self.iterators, path, lambda: 0) 207 return "$i%d" % self.iterators[path] 208 209 def next_iterator(self): 210 self.iterators[self.get_iterator_path()] += 1 211 212 # Temporary variable naming. 213 214 def get_temporary_name(self): 215 path = self.get_namespace_path() 216 init_item(self.temp, path, lambda: 0) 217 return "$t%d" % self.temp[path] 218 219 def next_temporary(self): 220 self.temp[self.get_namespace_path()] += 1 221 222 # Arbitrary function naming. 223 224 def get_lambda_name(self): 225 path = self.get_namespace_path() 226 init_item(self.lambdas, path, lambda: 0) 227 name = "$l%d" % self.lambdas[path] 228 self.lambdas[path] += 1 229 return name 230 231 def reset_lambdas(self): 232 self.lambdas = {} 233 234 # Constant and literal recording. 235 236 def get_constant_value(self, value, literal=None): 237 238 "Encode the 'value' if appropriate, returning a value and typename." 239 240 if isinstance(value, unicode): 241 return value.encode("utf-8"), "unicode" 242 243 # Attempt to convert plain strings to text. 244 245 elif isinstance(value, str) and self.encoding: 246 if not literal.startswith("b"): 247 try: 248 return unicode(value, self.encoding).encode("utf-8"), "unicode" 249 except UnicodeDecodeError: 250 pass 251 252 return value, value.__class__.__name__ 253 254 def get_constant_reference(self, ref, value): 255 256 "Return a constant reference for the given 'ref' type and 'value'." 257 258 constant_name = self.get_constant_name(value, ref.get_origin()) 259 260 # Return a reference for the constant. 261 262 objpath = self.get_object_path(constant_name) 263 name_ref = ConstantValueRef(constant_name, ref.instance_of(objpath), value) 264 265 # Record the value and type for the constant. 266 267 self._reserve_constant(objpath, name_ref.value, name_ref.get_origin()) 268 return name_ref 269 270 def reserve_constant(self, objpath, value, origin): 271 272 """ 273 Reserve a constant within 'objpath' with the given 'value' and having a 274 type with the given 'origin'. 275 """ 276 277 constant_name = self.get_constant_name(value, origin) 278 objpath = self.get_object_path(constant_name) 279 self._reserve_constant(objpath, value, origin) 280 281 def _reserve_constant(self, objpath, value, origin): 282 283 "Store a constant for 'objpath' with the given 'value' and 'origin'." 284 285 self.constant_values[objpath] = value, origin 286 287 def get_literal_reference(self, name, ref, items, cls): 288 289 """ 290 Return a literal reference for the given type 'name', literal 'ref', 291 node 'items' and employing the given 'cls' as the class of the returned 292 reference object. 293 """ 294 295 # Construct an invocation using the items as arguments. 296 297 typename = "$L%s" % name 298 299 invocation = compiler.ast.CallFunc( 300 compiler.ast.Name(typename), 301 items 302 ) 303 304 # Get a name for the actual literal. 305 306 instname = self.get_literal_name() 307 self.next_literal() 308 309 # Record the type for the literal. 310 311 objpath = self.get_object_path(instname) 312 self.literal_types[objpath] = ref.get_origin() 313 314 # Return a wrapper for the invocation exposing the items. 315 316 return cls( 317 instname, 318 ref.instance_of(), 319 self.process_structure_node(invocation), 320 invocation.args 321 ) 322 323 # Node handling. 324 325 def process_structure(self, node): 326 327 """ 328 Within the given 'node', process the program structure. 329 330 During inspection, this will process global declarations, adjusting the 331 module namespace, and import statements, building a module dependency 332 hierarchy. 333 334 During translation, this will consult deduced program information and 335 output translated code. 336 """ 337 338 l = [] 339 for n in node.getChildNodes(): 340 l.append(self.process_structure_node(n)) 341 return l 342 343 def process_augassign_node(self, n): 344 345 "Process the given augmented assignment node 'n'." 346 347 op = operator_functions[n.op] 348 349 if isinstance(n.node, compiler.ast.Getattr): 350 target = compiler.ast.AssAttr(n.node.expr, n.node.attrname, "OP_ASSIGN") 351 elif isinstance(n.node, compiler.ast.Name): 352 target = compiler.ast.AssName(n.node.name, "OP_ASSIGN") 353 else: 354 target = n.node 355 356 assignment = compiler.ast.Assign( 357 [target], 358 compiler.ast.CallFunc( 359 compiler.ast.Name("$op%s" % op), 360 [n.node, n.expr])) 361 362 return self.process_structure_node(assignment) 363 364 def process_assignment_for_object(self, original_name, source): 365 366 """ 367 Return an assignment operation making 'original_name' refer to the given 368 'source'. 369 """ 370 371 assignment = compiler.ast.Assign( 372 [compiler.ast.AssName(original_name, "OP_ASSIGN")], 373 source 374 ) 375 376 return self.process_structure_node(assignment) 377 378 def process_assignment_node_items(self, n, expr): 379 380 """ 381 Process the given assignment node 'n' whose children are to be assigned 382 items of 'expr'. 383 """ 384 385 name_ref = self.process_structure_node(expr) 386 387 # Either unpack the items and present them directly to each assignment 388 # node. 389 390 if isinstance(name_ref, LiteralSequenceRef): 391 self.process_literal_sequence_items(n, name_ref) 392 393 # Or have the assignment nodes access each item via the sequence API. 394 395 else: 396 self.process_assignment_node_items_by_position(n, expr, name_ref) 397 398 def process_assignment_node_items_by_position(self, n, expr, name_ref): 399 400 """ 401 Process the given sequence assignment node 'n', converting the node to 402 the separate assignment of each target using positional access on a 403 temporary variable representing the sequence. Use 'expr' as the assigned 404 value and 'name_ref' as the reference providing any existing temporary 405 variable. 406 """ 407 408 assignments = [] 409 410 if isinstance(name_ref, NameRef): 411 temp = name_ref.name 412 else: 413 temp = self.get_temporary_name() 414 self.next_temporary() 415 416 assignments.append( 417 compiler.ast.Assign([compiler.ast.AssName(temp, "OP_ASSIGN")], expr) 418 ) 419 420 for i, node in enumerate(n.nodes): 421 assignments.append( 422 compiler.ast.Assign([node], compiler.ast.Subscript( 423 compiler.ast.Name(temp), "OP_APPLY", [compiler.ast.Const(i, str(i))])) 424 ) 425 426 return self.process_structure_node(compiler.ast.Stmt(assignments)) 427 428 def process_literal_sequence_items(self, n, name_ref): 429 430 """ 431 Process the given assignment node 'n', obtaining from the given 432 'name_ref' the items to be assigned to the assignment targets. 433 """ 434 435 if len(n.nodes) == len(name_ref.items): 436 for node, item in zip(n.nodes, name_ref.items): 437 self.process_assignment_node(node, item) 438 else: 439 raise InspectError("In %s, item assignment needing %d items is given %d items." % ( 440 self.get_namespace_path(), len(n.nodes), len(name_ref.items))) 441 442 def process_compare_node(self, n): 443 444 """ 445 Process the given comparison node 'n', converting an operator sequence 446 from... 447 448 <expr1> <op1> <expr2> <op2> <expr3> 449 450 ...to... 451 452 <op1>(<expr1>, <expr2>) and <op2>(<expr2>, <expr3>) 453 """ 454 455 invocations = [] 456 last = n.expr 457 458 for op, op_node in n.ops: 459 op = operator_functions.get(op) 460 461 invocations.append(compiler.ast.CallFunc( 462 compiler.ast.Name("$op%s" % op), 463 [last, op_node])) 464 465 last = op_node 466 467 if len(invocations) > 1: 468 result = compiler.ast.And(invocations) 469 else: 470 result = invocations[0] 471 472 return self.process_structure_node(result) 473 474 def process_dict_node(self, node): 475 476 """ 477 Process the given dictionary 'node', returning a list of (key, value) 478 tuples. 479 """ 480 481 l = [] 482 for key, value in node.items: 483 l.append(( 484 self.process_structure_node(key), 485 self.process_structure_node(value))) 486 return l 487 488 def process_for_node(self, n): 489 490 """ 491 Generate attribute accesses for {n.list}.__iter__ and the next method on 492 the iterator, producing a replacement node for the original. 493 """ 494 495 node = compiler.ast.Stmt([ 496 497 # <iterator> = {n.list}.__iter__ 498 499 compiler.ast.Assign( 500 [compiler.ast.AssName(self.get_iterator_name(), "OP_ASSIGN")], 501 compiler.ast.CallFunc( 502 compiler.ast.Getattr(n.list, "__iter__"), 503 [] 504 )), 505 506 # try: 507 # while True: 508 # <var>... = <iterator>.next() 509 # ... 510 # except StopIteration: 511 # pass 512 513 compiler.ast.TryExcept( 514 compiler.ast.While( 515 compiler.ast.Name("True"), 516 compiler.ast.Stmt([ 517 compiler.ast.Assign( 518 [n.assign], 519 compiler.ast.CallFunc( 520 compiler.ast.Getattr(compiler.ast.Name(self.get_iterator_name()), "next"), 521 [] 522 )), 523 n.body]), 524 None), 525 [(compiler.ast.Name("StopIteration"), None, compiler.ast.Stmt([compiler.ast.Pass()]))], 526 None) 527 ]) 528 529 self.next_iterator() 530 self.process_structure_node(node) 531 532 def process_literal_sequence_node(self, n, name, ref, cls): 533 534 """ 535 Process the given literal sequence node 'n' as a function invocation, 536 with 'name' indicating the type of the sequence, and 'ref' being a 537 reference to the type. The 'cls' is used to instantiate a suitable name 538 reference. 539 """ 540 541 if name == "dict": 542 items = [] 543 for key, value in n.items: 544 items.append(compiler.ast.Tuple([key, value])) 545 else: # name in ("list", "tuple"): 546 items = n.nodes 547 548 return self.get_literal_reference(name, ref, items, cls) 549 550 def process_operator_node(self, n): 551 552 """ 553 Process the given operator node 'n' as an operator function invocation. 554 """ 555 556 op = operator_functions[n.__class__.__name__] 557 invocation = compiler.ast.CallFunc( 558 compiler.ast.Name("$op%s" % op), 559 list(n.getChildNodes()) 560 ) 561 return self.process_structure_node(invocation) 562 563 def process_print_node(self, n): 564 565 """ 566 Process the given print node 'n' as an invocation on a stream of the 567 form... 568 569 $print(dest, args, nl) 570 571 The special function name will be translated elsewhere. 572 """ 573 574 nl = isinstance(n, compiler.ast.Printnl) 575 invocation = compiler.ast.CallFunc( 576 compiler.ast.Name("$print"), 577 [n.dest or compiler.ast.Name("None"), 578 compiler.ast.List(list(n.nodes)), 579 nl and compiler.ast.Name("True") or compiler.ast.Name("False")] 580 ) 581 return self.process_structure_node(invocation) 582 583 def process_slice_node(self, n, expr=None): 584 585 """ 586 Process the given slice node 'n' as an operator function invocation. 587 """ 588 589 op = n.flags == "OP_ASSIGN" and "setslice" or "getslice" 590 invocation = compiler.ast.CallFunc( 591 compiler.ast.Name("$op%s" % op), 592 [n.expr, n.lower or compiler.ast.Name("None"), n.upper or compiler.ast.Name("None")] + 593 (expr and [expr] or []) 594 ) 595 return self.process_structure_node(invocation) 596 597 def process_sliceobj_node(self, n): 598 599 """ 600 Process the given slice object node 'n' as a slice constructor. 601 """ 602 603 op = "slice" 604 invocation = compiler.ast.CallFunc( 605 compiler.ast.Name("$op%s" % op), 606 n.nodes 607 ) 608 return self.process_structure_node(invocation) 609 610 def process_subscript_node(self, n, expr=None): 611 612 """ 613 Process the given subscript node 'n' as an operator function invocation. 614 """ 615 616 op = n.flags == "OP_ASSIGN" and "setitem" or "getitem" 617 invocation = compiler.ast.CallFunc( 618 compiler.ast.Name("$op%s" % op), 619 [n.expr] + list(n.subs) + (expr and [expr] or []) 620 ) 621 return self.process_structure_node(invocation) 622 623 def process_attribute_chain(self, n): 624 625 """ 626 Process the given attribute access node 'n'. Return a reference 627 describing the expression. 628 """ 629 630 # AssAttr/Getattr are nested with the outermost access being the last 631 # access in any chain. 632 633 self.attrs.insert(0, n.attrname) 634 attrs = self.attrs 635 636 # Break attribute chains where non-access nodes are found. 637 638 if not self.have_access_expression(n): 639 self.reset_attribute_chain() 640 641 # Descend into the expression, extending backwards any existing chain, 642 # or building another for the expression. 643 644 name_ref = self.process_structure_node(n.expr) 645 646 # Restore chain information applying to this node. 647 648 if not self.have_access_expression(n): 649 self.restore_attribute_chain(attrs) 650 651 # Return immediately if the expression was another access and thus a 652 # continuation backwards along the chain. The above processing will 653 # have followed the chain all the way to its conclusion. 654 655 if self.have_access_expression(n): 656 del self.attrs[0] 657 658 return name_ref 659 660 # Attribute chain handling. 661 662 def reset_attribute_chain(self): 663 664 "Reset the attribute chain for a subexpression of an attribute access." 665 666 self.attrs = [] 667 self.chain_assignment.append(self.in_assignment) 668 self.chain_invocation.append(self.in_invocation) 669 self.in_assignment = None 670 self.in_invocation = False 671 672 def restore_attribute_chain(self, attrs): 673 674 "Restore the attribute chain for an attribute access." 675 676 self.attrs = attrs 677 self.in_assignment = self.chain_assignment.pop() 678 self.in_invocation = self.chain_invocation.pop() 679 680 def have_access_expression(self, node): 681 682 "Return whether the expression associated with 'node' is Getattr." 683 684 return isinstance(node.expr, compiler.ast.Getattr) 685 686 def get_name_for_tracking(self, name, path=None): 687 688 """ 689 Return the name to be used for attribute usage observations involving 690 the given 'name' in the current namespace. If 'path' is indicated and 691 the name is being used outside a function, return the path value; 692 otherwise, return a path computed using the current namespace and the 693 given name. 694 695 The intention of this method is to provide a suitably-qualified name 696 that can be tracked across namespaces. Where globals are being 697 referenced in class namespaces, they should be referenced using their 698 path within the module, not using a path within each class. 699 700 It may not be possible to identify a global within a function at the 701 time of inspection (since a global may appear later in a file). 702 Consequently, globals are identified by their local name rather than 703 their module-qualified path. 704 """ 705 706 # For functions, use the appropriate local names. 707 708 if self.in_function: 709 return name 710 711 # For static namespaces, use the given qualified name. 712 713 elif path: 714 return path 715 716 # Otherwise, establish a name in the current namespace. 717 718 else: 719 return self.get_object_path(name) 720 721 def get_path_for_access(self): 722 723 "Outside functions, register accesses at the module level." 724 725 if not self.in_function: 726 return self.name 727 else: 728 return self.get_namespace_path() 729 730 def get_module_name(self, node): 731 732 """ 733 Using the given From 'node' in this module, calculate any relative import 734 information, returning a tuple containing a module to import along with any 735 names to import based on the node's name information. 736 737 Where the returned module is given as None, whole module imports should 738 be performed for the returned modules using the returned names. 739 """ 740 741 # Absolute import. 742 743 if node.level == 0: 744 return node.modname, node.names 745 746 # Relative to an ancestor of this module. 747 748 else: 749 path = self.name.split(".") 750 level = node.level 751 752 # Relative imports treat package roots as submodules. 753 754 if split(self.filename)[-1] == "__init__.py": 755 level -= 1 756 757 if level > len(path): 758 raise InspectError("Relative import %r involves too many levels up from module %r" % ( 759 ("%s%s" % ("." * node.level, node.modname or "")), self.name)) 760 761 basename = ".".join(path[:len(path)-level]) 762 763 # Name imports from a module. 764 765 if node.modname: 766 return "%s.%s" % (basename, node.modname), node.names 767 768 # Relative whole module imports. 769 770 else: 771 return basename, node.names 772 773 def get_argnames(args): 774 775 """ 776 Return a list of all names provided by 'args'. Since tuples may be 777 employed, the arguments are traversed depth-first. 778 """ 779 780 l = [] 781 for arg in args: 782 if isinstance(arg, tuple): 783 l += get_argnames(arg) 784 else: 785 l.append(arg) 786 return l 787 788 # Dictionary utilities. 789 790 def init_item(d, key, fn): 791 792 """ 793 Add to 'd' an entry for 'key' using the callable 'fn' to make an initial 794 value where no entry already exists. 795 """ 796 797 if not d.has_key(key): 798 d[key] = fn() 799 return d[key] 800 801 def dict_for_keys(d, keys): 802 803 "Return a new dictionary containing entries from 'd' for the given 'keys'." 804 805 nd = {} 806 for key in keys: 807 if d.has_key(key): 808 nd[key] = d[key] 809 return nd 810 811 def make_key(s): 812 813 "Make sequence 's' into a tuple-based key, first sorting its contents." 814 815 l = list(s) 816 l.sort() 817 return tuple(l) 818 819 def add_counter_item(d, key): 820 821 """ 822 Make a mapping in 'd' for 'key' to the number of keys added before it, thus 823 maintaining a mapping of keys to their order of insertion. 824 """ 825 826 if not d.has_key(key): 827 d[key] = len(d.keys()) 828 return d[key] 829 830 def remove_items(d1, d2): 831 832 "Remove from 'd1' all items from 'd2'." 833 834 for key in d2.keys(): 835 if d1.has_key(key): 836 del d1[key] 837 838 # Set utilities. 839 840 def first(s): 841 return list(s)[0] 842 843 def same(s1, s2): 844 return set(s1) == set(s2) 845 846 # General input/output. 847 848 def readfile(filename): 849 850 "Return the contents of 'filename'." 851 852 f = open(filename) 853 try: 854 return f.read() 855 finally: 856 f.close() 857 858 def writefile(filename, s): 859 860 "Write to 'filename' the string 's'." 861 862 f = open(filename, "w") 863 try: 864 f.write(s) 865 finally: 866 f.close() 867 868 # General encoding. 869 870 def sorted_output(x): 871 872 "Sort sequence 'x' and return a string with commas separating the values." 873 874 x = map(str, x) 875 x.sort() 876 return ", ".join(x) 877 878 # Attribute chain decoding. 879 880 def get_attrnames(attrnames): 881 882 """ 883 Split the qualified attribute chain 'attrnames' into its components, 884 handling special attributes starting with "#" that indicate type 885 conformance. 886 """ 887 888 if attrnames.startswith("#"): 889 return [attrnames] 890 else: 891 return attrnames.split(".") 892 893 def get_attrname_from_location(location): 894 895 """ 896 Extract the first attribute from the attribute names employed in a 897 'location'. 898 """ 899 900 path, name, attrnames, access = location 901 if not attrnames: 902 return attrnames 903 return get_attrnames(attrnames)[0] 904 905 def get_name_path(path, name): 906 907 "Return a suitable qualified name from the given 'path' and 'name'." 908 909 if "." in name: 910 return name 911 else: 912 return "%s.%s" % (path, name) 913 914 # Usage-related functions. 915 916 def get_types_for_usage(attrnames, objects): 917 918 """ 919 Identify the types that can support the given 'attrnames', using the 920 given 'objects' as the catalogue of type details. 921 """ 922 923 types = [] 924 for name, _attrnames in objects.items(): 925 if set(attrnames).issubset(_attrnames): 926 types.append(name) 927 return types 928 929 def get_invoked_attributes(usage): 930 931 "Obtain invoked attribute from the given 'usage'." 932 933 invoked = [] 934 if usage: 935 for attrname, invocation, assignment in usage: 936 if invocation: 937 invoked.append(attrname) 938 return invoked 939 940 def get_assigned_attributes(usage): 941 942 "Obtain assigned attribute from the given 'usage'." 943 944 assigned = [] 945 if usage: 946 for attrname, invocation, assignment in usage: 947 if assignment: 948 assigned.append(attrname) 949 return assigned 950 951 # Type and module functions. 952 953 def get_builtin_module(name): 954 955 "Return the module name containing the given type 'name'." 956 957 # NOTE: This makes assumptions about the __builtins__ structure. 958 959 if name == "string": 960 return "str" 961 elif name == "utf8string": 962 return "unicode" 963 elif name == "NoneType": 964 return "none" 965 else: 966 return name 967 968 def get_builtin_type(name): 969 970 "Return the type name provided by the given Python value 'name'." 971 972 if name == "str": 973 return "string" 974 elif name == "unicode": 975 return "utf8string" 976 else: 977 return name 978 979 # Useful data. 980 981 predefined_constants = "False", "None", "NotImplemented", "True" 982 983 operator_functions = { 984 985 # Fundamental operations. 986 987 "is" : "is_", 988 "is not" : "is_not", 989 990 # Binary operations. 991 992 "in" : "in_", 993 "not in" : "not_in", 994 "Add" : "add", 995 "Bitand" : "and_", 996 "Bitor" : "or_", 997 "Bitxor" : "xor", 998 "Div" : "div", 999 "FloorDiv" : "floordiv", 1000 "LeftShift" : "lshift", 1001 "Mod" : "mod", 1002 "Mul" : "mul", 1003 "Power" : "pow", 1004 "RightShift" : "rshift", 1005 "Sub" : "sub", 1006 1007 # Unary operations. 1008 1009 "Invert" : "invert", 1010 "UnaryAdd" : "pos", 1011 "UnarySub" : "neg", 1012 1013 # Augmented assignment. 1014 1015 "+=" : "iadd", 1016 "-=" : "isub", 1017 "*=" : "imul", 1018 "/=" : "idiv", 1019 "//=" : "ifloordiv", 1020 "%=" : "imod", 1021 "**=" : "ipow", 1022 "<<=" : "ilshift", 1023 ">>=" : "irshift", 1024 "&=" : "iand", 1025 "^=" : "ixor", 1026 "|=" : "ior", 1027 1028 # Comparisons. 1029 1030 "==" : "eq", 1031 "!=" : "ne", 1032 "<" : "lt", 1033 "<=" : "le", 1034 ">=" : "ge", 1035 ">" : "gt", 1036 } 1037 1038 # vim: tabstop=4 expandtab shiftwidth=4