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