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 # Result classes. 799 800 class InstructionSequence: 801 802 "A generic sequence of instructions." 803 804 def __init__(self, instructions): 805 self.instructions = instructions 806 807 def get_value_instruction(self): 808 return self.instructions[-1] 809 810 def get_init_instructions(self): 811 return self.instructions[:-1] 812 813 # Dictionary utilities. 814 815 def init_item(d, key, fn): 816 817 """ 818 Add to 'd' an entry for 'key' using the callable 'fn' to make an initial 819 value where no entry already exists. 820 """ 821 822 if not d.has_key(key): 823 d[key] = fn() 824 return d[key] 825 826 def dict_for_keys(d, keys): 827 828 "Return a new dictionary containing entries from 'd' for the given 'keys'." 829 830 nd = {} 831 for key in keys: 832 if d.has_key(key): 833 nd[key] = d[key] 834 return nd 835 836 def make_key(s): 837 838 "Make sequence 's' into a tuple-based key, first sorting its contents." 839 840 l = list(s) 841 l.sort() 842 return tuple(l) 843 844 def add_counter_item(d, key): 845 846 """ 847 Make a mapping in 'd' for 'key' to the number of keys added before it, thus 848 maintaining a mapping of keys to their order of insertion. 849 """ 850 851 if not d.has_key(key): 852 d[key] = len(d.keys()) 853 return d[key] 854 855 def remove_items(d1, d2): 856 857 "Remove from 'd1' all items from 'd2'." 858 859 for key in d2.keys(): 860 if d1.has_key(key): 861 del d1[key] 862 863 # Set utilities. 864 865 def first(s): 866 return list(s)[0] 867 868 def same(s1, s2): 869 return set(s1) == set(s2) 870 871 # General input/output. 872 873 def readfile(filename): 874 875 "Return the contents of 'filename'." 876 877 f = open(filename) 878 try: 879 return f.read() 880 finally: 881 f.close() 882 883 def writefile(filename, s): 884 885 "Write to 'filename' the string 's'." 886 887 f = open(filename, "w") 888 try: 889 f.write(s) 890 finally: 891 f.close() 892 893 # General encoding. 894 895 def sorted_output(x): 896 897 "Sort sequence 'x' and return a string with commas separating the values." 898 899 x = map(str, x) 900 x.sort() 901 return ", ".join(x) 902 903 # Attribute chain decoding. 904 905 def get_attrnames(attrnames): 906 907 """ 908 Split the qualified attribute chain 'attrnames' into its components, 909 handling special attributes starting with "#" that indicate type 910 conformance. 911 """ 912 913 if attrnames.startswith("#"): 914 return [attrnames] 915 else: 916 return attrnames.split(".") 917 918 def get_attrname_from_location(location): 919 920 """ 921 Extract the first attribute from the attribute names employed in a 922 'location'. 923 """ 924 925 path, name, attrnames, access = location 926 if not attrnames: 927 return attrnames 928 return get_attrnames(attrnames)[0] 929 930 def get_name_path(path, name): 931 932 "Return a suitable qualified name from the given 'path' and 'name'." 933 934 if "." in name: 935 return name 936 else: 937 return "%s.%s" % (path, name) 938 939 # Usage-related functions. 940 941 def get_types_for_usage(attrnames, objects): 942 943 """ 944 Identify the types that can support the given 'attrnames', using the 945 given 'objects' as the catalogue of type details. 946 """ 947 948 types = [] 949 for name, _attrnames in objects.items(): 950 if set(attrnames).issubset(_attrnames): 951 types.append(name) 952 return types 953 954 def get_invoked_attributes(usage): 955 956 "Obtain invoked attribute from the given 'usage'." 957 958 invoked = [] 959 if usage: 960 for attrname, invocation, assignment in usage: 961 if invocation: 962 invoked.append(attrname) 963 return invoked 964 965 def get_assigned_attributes(usage): 966 967 "Obtain assigned attribute from the given 'usage'." 968 969 assigned = [] 970 if usage: 971 for attrname, invocation, assignment in usage: 972 if assignment: 973 assigned.append(attrname) 974 return assigned 975 976 # Type and module functions. 977 978 def get_builtin_module(name): 979 980 "Return the module name containing the given type 'name'." 981 982 # NOTE: This makes assumptions about the __builtins__ structure. 983 984 if name == "string": 985 return "str" 986 elif name == "utf8string": 987 return "unicode" 988 elif name == "NoneType": 989 return "none" 990 else: 991 return name 992 993 def get_builtin_type(name): 994 995 "Return the type name provided by the given Python value 'name'." 996 997 if name == "str": 998 return "string" 999 elif name == "unicode": 1000 return "utf8string" 1001 else: 1002 return name 1003 1004 # Useful data. 1005 1006 predefined_constants = "False", "None", "NotImplemented", "True" 1007 1008 operator_functions = { 1009 1010 # Fundamental operations. 1011 1012 "is" : "is_", 1013 "is not" : "is_not", 1014 1015 # Binary operations. 1016 1017 "in" : "in_", 1018 "not in" : "not_in", 1019 "Add" : "add", 1020 "Bitand" : "and_", 1021 "Bitor" : "or_", 1022 "Bitxor" : "xor", 1023 "Div" : "div", 1024 "FloorDiv" : "floordiv", 1025 "LeftShift" : "lshift", 1026 "Mod" : "mod", 1027 "Mul" : "mul", 1028 "Power" : "pow", 1029 "RightShift" : "rshift", 1030 "Sub" : "sub", 1031 1032 # Unary operations. 1033 1034 "Invert" : "invert", 1035 "UnaryAdd" : "pos", 1036 "UnarySub" : "neg", 1037 1038 # Augmented assignment. 1039 1040 "+=" : "iadd", 1041 "-=" : "isub", 1042 "*=" : "imul", 1043 "/=" : "idiv", 1044 "//=" : "ifloordiv", 1045 "%=" : "imod", 1046 "**=" : "ipow", 1047 "<<=" : "ilshift", 1048 ">>=" : "irshift", 1049 "&=" : "iand", 1050 "^=" : "ixor", 1051 "|=" : "ior", 1052 1053 # Comparisons. 1054 1055 "==" : "eq", 1056 "!=" : "ne", 1057 "<" : "lt", 1058 "<=" : "le", 1059 ">=" : "ge", 1060 ">" : "gt", 1061 } 1062 1063 # vim: tabstop=4 expandtab shiftwidth=4