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