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 InspectError 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, encoding=None): 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, encoding)) 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 """ 239 Encode the 'value' if appropriate, returning a value, a typename and any 240 encoding. 241 """ 242 243 if isinstance(value, unicode): 244 return value.encode("utf-8"), "unicode", self.encoding 245 246 # Attempt to convert plain strings to text. 247 248 elif isinstance(value, str) and self.encoding: 249 if not literal.startswith("b"): 250 try: 251 return unicode(value, self.encoding).encode("utf-8"), "unicode", self.encoding 252 except UnicodeDecodeError: 253 pass 254 255 return value, value.__class__.__name__, None 256 257 def get_constant_reference(self, ref, value, encoding=None): 258 259 """ 260 Return a constant reference for the given 'ref' type and 'value', with 261 the optional 'encoding' applying to text values. 262 """ 263 264 constant_name = self.get_constant_name(value, ref.get_origin(), encoding) 265 266 # Return a reference for the constant. 267 268 objpath = self.get_object_path(constant_name) 269 name_ref = ConstantValueRef(constant_name, ref.instance_of(objpath), value) 270 271 # Record the value and type for the constant. 272 273 self._reserve_constant(objpath, name_ref.value, name_ref.get_origin(), encoding) 274 return name_ref 275 276 def reserve_constant(self, objpath, value, origin, encoding=None): 277 278 """ 279 Reserve a constant within 'objpath' with the given 'value' and having a 280 type with the given 'origin', with the optional 'encoding' applying to 281 text values. 282 """ 283 284 constant_name = self.get_constant_name(value, origin) 285 objpath = self.get_object_path(constant_name) 286 self._reserve_constant(objpath, value, origin, encoding) 287 288 def _reserve_constant(self, objpath, value, origin, encoding): 289 290 """ 291 Store a constant for 'objpath' with the given 'value' and 'origin', with 292 the optional 'encoding' applying to text values. 293 """ 294 295 self.constant_values[objpath] = value, origin, encoding 296 297 def get_literal_reference(self, name, ref, items, cls): 298 299 """ 300 Return a literal reference for the given type 'name', literal 'ref', 301 node 'items' and employing the given 'cls' as the class of the returned 302 reference object. 303 """ 304 305 # Construct an invocation using the items as arguments. 306 307 typename = "$L%s" % name 308 309 invocation = compiler.ast.CallFunc( 310 compiler.ast.Name(typename), 311 items 312 ) 313 314 # Get a name for the actual literal. 315 316 instname = self.get_literal_name() 317 self.next_literal() 318 319 # Record the type for the literal. 320 321 objpath = self.get_object_path(instname) 322 self.literal_types[objpath] = ref.get_origin() 323 324 # Return a wrapper for the invocation exposing the items. 325 326 return cls( 327 instname, 328 ref.instance_of(), 329 self.process_structure_node(invocation), 330 invocation.args 331 ) 332 333 # Node handling. 334 335 def process_structure(self, node): 336 337 """ 338 Within the given 'node', process the program structure. 339 340 During inspection, this will process global declarations, adjusting the 341 module namespace, and import statements, building a module dependency 342 hierarchy. 343 344 During translation, this will consult deduced program information and 345 output translated code. 346 """ 347 348 l = [] 349 for n in node.getChildNodes(): 350 l.append(self.process_structure_node(n)) 351 return l 352 353 def process_augassign_node(self, n): 354 355 "Process the given augmented assignment node 'n'." 356 357 op = operator_functions[n.op] 358 359 if isinstance(n.node, compiler.ast.Getattr): 360 target = compiler.ast.AssAttr(n.node.expr, n.node.attrname, "OP_ASSIGN") 361 elif isinstance(n.node, compiler.ast.Name): 362 target = compiler.ast.AssName(n.node.name, "OP_ASSIGN") 363 else: 364 target = n.node 365 366 assignment = compiler.ast.Assign( 367 [target], 368 compiler.ast.CallFunc( 369 compiler.ast.Name("$op%s" % op), 370 [n.node, n.expr])) 371 372 return self.process_structure_node(assignment) 373 374 def process_assignment_for_object(self, original_name, source): 375 376 """ 377 Return an assignment operation making 'original_name' refer to the given 378 'source'. 379 """ 380 381 assignment = compiler.ast.Assign( 382 [compiler.ast.AssName(original_name, "OP_ASSIGN")], 383 source 384 ) 385 386 return self.process_structure_node(assignment) 387 388 def process_assignment_node_items(self, n, expr): 389 390 """ 391 Process the given assignment node 'n' whose children are to be assigned 392 items of 'expr'. 393 """ 394 395 name_ref = self.process_structure_node(expr) 396 397 # Either unpack the items and present them directly to each assignment 398 # node. 399 400 if isinstance(name_ref, LiteralSequenceRef): 401 self.process_literal_sequence_items(n, name_ref) 402 403 # Or have the assignment nodes access each item via the sequence API. 404 405 else: 406 self.process_assignment_node_items_by_position(n, expr, name_ref) 407 408 def process_assignment_node_items_by_position(self, n, expr, name_ref): 409 410 """ 411 Process the given sequence assignment node 'n', converting the node to 412 the separate assignment of each target using positional access on a 413 temporary variable representing the sequence. Use 'expr' as the assigned 414 value and 'name_ref' as the reference providing any existing temporary 415 variable. 416 """ 417 418 assignments = [] 419 420 if isinstance(name_ref, NameRef): 421 temp = name_ref.name 422 else: 423 temp = self.get_temporary_name() 424 self.next_temporary() 425 426 assignments.append( 427 compiler.ast.Assign([compiler.ast.AssName(temp, "OP_ASSIGN")], expr) 428 ) 429 430 for i, node in enumerate(n.nodes): 431 assignments.append( 432 compiler.ast.Assign([node], compiler.ast.Subscript( 433 compiler.ast.Name(temp), "OP_APPLY", [compiler.ast.Const(i, str(i))])) 434 ) 435 436 return self.process_structure_node(compiler.ast.Stmt(assignments)) 437 438 def process_literal_sequence_items(self, n, name_ref): 439 440 """ 441 Process the given assignment node 'n', obtaining from the given 442 'name_ref' the items to be assigned to the assignment targets. 443 """ 444 445 if len(n.nodes) == len(name_ref.items): 446 for node, item in zip(n.nodes, name_ref.items): 447 self.process_assignment_node(node, item) 448 else: 449 raise InspectError("In %s, item assignment needing %d items is given %d items." % ( 450 self.get_namespace_path(), len(n.nodes), len(name_ref.items))) 451 452 def process_compare_node(self, n): 453 454 """ 455 Process the given comparison node 'n', converting an operator sequence 456 from... 457 458 <expr1> <op1> <expr2> <op2> <expr3> 459 460 ...to... 461 462 <op1>(<expr1>, <expr2>) and <op2>(<expr2>, <expr3>) 463 """ 464 465 invocations = [] 466 last = n.expr 467 468 for op, op_node in n.ops: 469 op = operator_functions.get(op) 470 471 invocations.append(compiler.ast.CallFunc( 472 compiler.ast.Name("$op%s" % op), 473 [last, op_node])) 474 475 last = op_node 476 477 if len(invocations) > 1: 478 result = compiler.ast.And(invocations) 479 else: 480 result = invocations[0] 481 482 return self.process_structure_node(result) 483 484 def process_dict_node(self, node): 485 486 """ 487 Process the given dictionary 'node', returning a list of (key, value) 488 tuples. 489 """ 490 491 l = [] 492 for key, value in node.items: 493 l.append(( 494 self.process_structure_node(key), 495 self.process_structure_node(value))) 496 return l 497 498 def process_for_node(self, n): 499 500 """ 501 Generate attribute accesses for {n.list}.__iter__ and the next method on 502 the iterator, producing a replacement node for the original. 503 """ 504 505 node = compiler.ast.Stmt([ 506 507 # <iterator> = {n.list}.__iter__ 508 509 compiler.ast.Assign( 510 [compiler.ast.AssName(self.get_iterator_name(), "OP_ASSIGN")], 511 compiler.ast.CallFunc( 512 compiler.ast.Getattr(n.list, "__iter__"), 513 [] 514 )), 515 516 # try: 517 # while True: 518 # <var>... = <iterator>.next() 519 # ... 520 # except StopIteration: 521 # pass 522 523 compiler.ast.TryExcept( 524 compiler.ast.While( 525 compiler.ast.Name("True"), 526 compiler.ast.Stmt([ 527 compiler.ast.Assign( 528 [n.assign], 529 compiler.ast.CallFunc( 530 compiler.ast.Getattr(compiler.ast.Name(self.get_iterator_name()), "next"), 531 [] 532 )), 533 n.body]), 534 None), 535 [(compiler.ast.Name("StopIteration"), None, compiler.ast.Stmt([compiler.ast.Pass()]))], 536 None) 537 ]) 538 539 self.next_iterator() 540 self.process_structure_node(node) 541 542 def process_literal_sequence_node(self, n, name, ref, cls): 543 544 """ 545 Process the given literal sequence node 'n' as a function invocation, 546 with 'name' indicating the type of the sequence, and 'ref' being a 547 reference to the type. The 'cls' is used to instantiate a suitable name 548 reference. 549 """ 550 551 if name == "dict": 552 items = [] 553 for key, value in n.items: 554 items.append(compiler.ast.Tuple([key, value])) 555 else: # name in ("list", "tuple"): 556 items = n.nodes 557 558 return self.get_literal_reference(name, ref, items, cls) 559 560 def process_operator_node(self, n): 561 562 """ 563 Process the given operator node 'n' as an operator function invocation. 564 """ 565 566 op = operator_functions[n.__class__.__name__] 567 invocation = compiler.ast.CallFunc( 568 compiler.ast.Name("$op%s" % op), 569 list(n.getChildNodes()) 570 ) 571 return self.process_structure_node(invocation) 572 573 def process_print_node(self, n): 574 575 """ 576 Process the given print node 'n' as an invocation on a stream of the 577 form... 578 579 $print(dest, args, nl) 580 581 The special function name will be translated elsewhere. 582 """ 583 584 nl = isinstance(n, compiler.ast.Printnl) 585 invocation = compiler.ast.CallFunc( 586 compiler.ast.Name("$print"), 587 [n.dest or compiler.ast.Name("None"), 588 compiler.ast.List(list(n.nodes)), 589 nl and compiler.ast.Name("True") or compiler.ast.Name("False")] 590 ) 591 return self.process_structure_node(invocation) 592 593 def process_slice_node(self, n, expr=None): 594 595 """ 596 Process the given slice node 'n' as an operator function invocation. 597 """ 598 599 op = n.flags == "OP_ASSIGN" and "setslice" or "getslice" 600 invocation = compiler.ast.CallFunc( 601 compiler.ast.Name("$op%s" % op), 602 [n.expr, n.lower or compiler.ast.Name("None"), n.upper or compiler.ast.Name("None")] + 603 (expr and [expr] or []) 604 ) 605 return self.process_structure_node(invocation) 606 607 def process_sliceobj_node(self, n): 608 609 """ 610 Process the given slice object node 'n' as a slice constructor. 611 """ 612 613 op = "slice" 614 invocation = compiler.ast.CallFunc( 615 compiler.ast.Name("$op%s" % op), 616 n.nodes 617 ) 618 return self.process_structure_node(invocation) 619 620 def process_subscript_node(self, n, expr=None): 621 622 """ 623 Process the given subscript node 'n' as an operator function invocation. 624 """ 625 626 op = n.flags == "OP_ASSIGN" and "setitem" or "getitem" 627 invocation = compiler.ast.CallFunc( 628 compiler.ast.Name("$op%s" % op), 629 [n.expr] + list(n.subs) + (expr and [expr] or []) 630 ) 631 return self.process_structure_node(invocation) 632 633 def process_attribute_chain(self, n): 634 635 """ 636 Process the given attribute access node 'n'. Return a reference 637 describing the expression. 638 """ 639 640 # AssAttr/Getattr are nested with the outermost access being the last 641 # access in any chain. 642 643 self.attrs.insert(0, n.attrname) 644 attrs = self.attrs 645 646 # Break attribute chains where non-access nodes are found. 647 648 if not self.have_access_expression(n): 649 self.reset_attribute_chain() 650 651 # Descend into the expression, extending backwards any existing chain, 652 # or building another for the expression. 653 654 name_ref = self.process_structure_node(n.expr) 655 656 # Restore chain information applying to this node. 657 658 if not self.have_access_expression(n): 659 self.restore_attribute_chain(attrs) 660 661 # Return immediately if the expression was another access and thus a 662 # continuation backwards along the chain. The above processing will 663 # have followed the chain all the way to its conclusion. 664 665 if self.have_access_expression(n): 666 del self.attrs[0] 667 668 return name_ref 669 670 # Attribute chain handling. 671 672 def reset_attribute_chain(self): 673 674 "Reset the attribute chain for a subexpression of an attribute access." 675 676 self.attrs = [] 677 self.chain_assignment.append(self.in_assignment) 678 self.chain_invocation.append(self.in_invocation) 679 self.in_assignment = None 680 self.in_invocation = False 681 682 def restore_attribute_chain(self, attrs): 683 684 "Restore the attribute chain for an attribute access." 685 686 self.attrs = attrs 687 self.in_assignment = self.chain_assignment.pop() 688 self.in_invocation = self.chain_invocation.pop() 689 690 def have_access_expression(self, node): 691 692 "Return whether the expression associated with 'node' is Getattr." 693 694 return isinstance(node.expr, compiler.ast.Getattr) 695 696 def get_name_for_tracking(self, name, path=None): 697 698 """ 699 Return the name to be used for attribute usage observations involving 700 the given 'name' in the current namespace. If 'path' is indicated and 701 the name is being used outside a function, return the path value; 702 otherwise, return a path computed using the current namespace and the 703 given name. 704 705 The intention of this method is to provide a suitably-qualified name 706 that can be tracked across namespaces. Where globals are being 707 referenced in class namespaces, they should be referenced using their 708 path within the module, not using a path within each class. 709 710 It may not be possible to identify a global within a function at the 711 time of inspection (since a global may appear later in a file). 712 Consequently, globals are identified by their local name rather than 713 their module-qualified path. 714 """ 715 716 # For functions, use the appropriate local names. 717 718 if self.in_function: 719 return name 720 721 # For static namespaces, use the given qualified name. 722 723 elif path: 724 return path 725 726 # Otherwise, establish a name in the current namespace. 727 728 else: 729 return self.get_object_path(name) 730 731 def get_path_for_access(self): 732 733 "Outside functions, register accesses at the module level." 734 735 if not self.in_function: 736 return self.name 737 else: 738 return self.get_namespace_path() 739 740 def get_module_name(self, node): 741 742 """ 743 Using the given From 'node' in this module, calculate any relative import 744 information, returning a tuple containing a module to import along with any 745 names to import based on the node's name information. 746 747 Where the returned module is given as None, whole module imports should 748 be performed for the returned modules using the returned names. 749 """ 750 751 # Absolute import. 752 753 if node.level == 0: 754 return node.modname, node.names 755 756 # Relative to an ancestor of this module. 757 758 else: 759 path = self.name.split(".") 760 level = node.level 761 762 # Relative imports treat package roots as submodules. 763 764 if split(self.filename)[-1] == "__init__.py": 765 level -= 1 766 767 if level > len(path): 768 raise InspectError("Relative import %r involves too many levels up from module %r" % ( 769 ("%s%s" % ("." * node.level, node.modname or "")), self.name)) 770 771 basename = ".".join(path[:len(path)-level]) 772 773 # Name imports from a module. 774 775 if node.modname: 776 return "%s.%s" % (basename, node.modname), node.names 777 778 # Relative whole module imports. 779 780 else: 781 return basename, node.names 782 783 def get_argnames(args): 784 785 """ 786 Return a list of all names provided by 'args'. Since tuples may be 787 employed, the arguments are traversed depth-first. 788 """ 789 790 l = [] 791 for arg in args: 792 if isinstance(arg, tuple): 793 l += get_argnames(arg) 794 else: 795 l.append(arg) 796 return l 797 798 # Dictionary utilities. 799 800 def init_item(d, key, fn): 801 802 """ 803 Add to 'd' an entry for 'key' using the callable 'fn' to make an initial 804 value where no entry already exists. 805 """ 806 807 if not d.has_key(key): 808 d[key] = fn() 809 return d[key] 810 811 def dict_for_keys(d, keys): 812 813 "Return a new dictionary containing entries from 'd' for the given 'keys'." 814 815 nd = {} 816 for key in keys: 817 if d.has_key(key): 818 nd[key] = d[key] 819 return nd 820 821 def make_key(s): 822 823 "Make sequence 's' into a tuple-based key, first sorting its contents." 824 825 l = list(s) 826 l.sort() 827 return tuple(l) 828 829 def add_counter_item(d, key): 830 831 """ 832 Make a mapping in 'd' for 'key' to the number of keys added before it, thus 833 maintaining a mapping of keys to their order of insertion. 834 """ 835 836 if not d.has_key(key): 837 d[key] = len(d.keys()) 838 return d[key] 839 840 def remove_items(d1, d2): 841 842 "Remove from 'd1' all items from 'd2'." 843 844 for key in d2.keys(): 845 if d1.has_key(key): 846 del d1[key] 847 848 # Set utilities. 849 850 def first(s): 851 return list(s)[0] 852 853 def same(s1, s2): 854 return set(s1) == set(s2) 855 856 # General input/output. 857 858 def readfile(filename): 859 860 "Return the contents of 'filename'." 861 862 f = open(filename) 863 try: 864 return f.read() 865 finally: 866 f.close() 867 868 def writefile(filename, s): 869 870 "Write to 'filename' the string 's'." 871 872 f = open(filename, "w") 873 try: 874 f.write(s) 875 finally: 876 f.close() 877 878 # General encoding. 879 880 def sorted_output(x): 881 882 "Sort sequence 'x' and return a string with commas separating the values." 883 884 x = map(str, x) 885 x.sort() 886 return ", ".join(x) 887 888 # Attribute chain decoding. 889 890 def get_attrnames(attrnames): 891 892 """ 893 Split the qualified attribute chain 'attrnames' into its components, 894 handling special attributes starting with "#" that indicate type 895 conformance. 896 """ 897 898 if attrnames.startswith("#"): 899 return [attrnames] 900 else: 901 return attrnames.split(".") 902 903 def get_attrname_from_location(location): 904 905 """ 906 Extract the first attribute from the attribute names employed in a 907 'location'. 908 """ 909 910 path, name, attrnames, access = location 911 if not attrnames: 912 return attrnames 913 return get_attrnames(attrnames)[0] 914 915 def get_name_path(path, name): 916 917 "Return a suitable qualified name from the given 'path' and 'name'." 918 919 if "." in name: 920 return name 921 else: 922 return "%s.%s" % (path, name) 923 924 # Usage-related functions. 925 926 def get_types_for_usage(attrnames, objects): 927 928 """ 929 Identify the types that can support the given 'attrnames', using the 930 given 'objects' as the catalogue of type details. 931 """ 932 933 types = [] 934 for name, _attrnames in objects.items(): 935 if set(attrnames).issubset(_attrnames): 936 types.append(name) 937 return types 938 939 def get_invoked_attributes(usage): 940 941 "Obtain invoked attribute from the given 'usage'." 942 943 invoked = [] 944 if usage: 945 for attrname, invocation, assignment in usage: 946 if invocation: 947 invoked.append(attrname) 948 return invoked 949 950 def get_assigned_attributes(usage): 951 952 "Obtain assigned attribute from the given 'usage'." 953 954 assigned = [] 955 if usage: 956 for attrname, invocation, assignment in usage: 957 if assignment: 958 assigned.append(attrname) 959 return assigned 960 961 # Type and module functions. 962 963 def get_builtin_module(name): 964 965 "Return the module name containing the given type 'name'." 966 967 # NOTE: This makes assumptions about the __builtins__ structure. 968 969 if name == "string": 970 return "str" 971 elif name == "utf8string": 972 return "unicode" 973 elif name == "NoneType": 974 return "none" 975 else: 976 return name 977 978 def get_builtin_type(name): 979 980 "Return the type name provided by the given Python value 'name'." 981 982 if name == "str": 983 return "string" 984 elif name == "unicode": 985 return "utf8string" 986 else: 987 return name 988 989 # Useful data. 990 991 predefined_constants = "False", "None", "NotImplemented", "True" 992 993 operator_functions = { 994 995 # Fundamental operations. 996 997 "is" : "is_", 998 "is not" : "is_not", 999 1000 # Binary operations. 1001 1002 "in" : "in_", 1003 "not in" : "not_in", 1004 "Add" : "add", 1005 "Bitand" : "and_", 1006 "Bitor" : "or_", 1007 "Bitxor" : "xor", 1008 "Div" : "div", 1009 "FloorDiv" : "floordiv", 1010 "LeftShift" : "lshift", 1011 "Mod" : "mod", 1012 "Mul" : "mul", 1013 "Power" : "pow", 1014 "RightShift" : "rshift", 1015 "Sub" : "sub", 1016 1017 # Unary operations. 1018 1019 "Invert" : "invert", 1020 "UnaryAdd" : "pos", 1021 "UnarySub" : "neg", 1022 1023 # Augmented assignment. 1024 1025 "+=" : "iadd", 1026 "-=" : "isub", 1027 "*=" : "imul", 1028 "/=" : "idiv", 1029 "//=" : "ifloordiv", 1030 "%=" : "imod", 1031 "**=" : "ipow", 1032 "<<=" : "ilshift", 1033 ">>=" : "irshift", 1034 "&=" : "iand", 1035 "^=" : "ixor", 1036 "|=" : "ior", 1037 1038 # Comparisons. 1039 1040 "==" : "eq", 1041 "!=" : "ne", 1042 "<" : "lt", 1043 "<=" : "le", 1044 ">=" : "ge", 1045 ">" : "gt", 1046 } 1047 1048 # vim: tabstop=4 expandtab shiftwidth=4