Lichen

common.py

26:f47c63967c59
2016-09-05 Paul Boddie Separated inspection-related naming methods from common module methods.
     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