Lichen

modules.py

702:2e22fd27e941
2017-03-11 Paul Boddie Merged slightly differing default branches.
     1 #!/usr/bin/env python     2      3 """     4 Module abstractions.     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 common import get_builtin_class, get_builtin_module, init_item, \    24                    remove_items, CommonModule    25 from encoders import decode_modifiers, decode_usage, encode_modifiers, encode_usage    26 from referencing import decode_reference, Reference    27 from results import ResolvedNameRef    28 import sys    29     30 class BasicModule(CommonModule):    31     32     "The basic module information."    33     34     def __init__(self, name, importer):    35         CommonModule.__init__(self, name, importer)    36     37         # Import details, primarily for cache output.    38     39         self.imports = set()    40         self.required = set()    41         self.deferred = []    42     43         # Global name information.    44     45         self.objects = {}    46         self.special = {}    47     48         # Class relationships.    49     50         self.classes = {}    51     52         # Attributes.    53     54         self.class_attrs = {}    55         self.instance_attrs = {}    56         self.instance_attr_constants = {}    57         self.module_attrs = set()    58     59         # Names used in each namespace.    60     61         self.names_used = {}    62     63         # Function details.    64     65         self.function_parameters = {}    66         self.function_defaults = {}    67         self.function_locals = {}    68         self.scope_globals = {}    69     70         # Exception handler details.    71     72         self.exception_namespaces = set()    73     74         # Attribute usage at module and function levels.    75     76         self.attr_usage = {}    77         self.name_initialisers = {}    78     79         # General attribute access expressions.    80     81         self.attr_accesses = {}    82         self.const_accesses = {}    83     84         # Attribute accessor definition details.    85     86         self.attr_accessors = {}    87     88         # Assignment details for accesses.    89     90         self.attr_access_modifiers = {}    91     92         # Name resolution details.    93     94         self.name_references = {} # references to globals    95     96         # Initialisation-related details.    97     98         self.initialised_names = {}    99         self.aliased_names = {}   100    101     def __repr__(self):   102         return "BasicModule(%r, %r)" % (self.name, self.importer)   103    104     # Derived information methods.   105    106     def propagate(self):   107    108         "Finalise and propagate module information."   109    110         self.propagate_attrs()   111         self.propagate_name_references()   112         self.propagate_attr_accesses()   113         self.propagate_constants()   114    115     def unpropagate(self):   116    117         """   118         Retract information from the importer including information about this   119         module derived by the importer.   120         """   121    122         del self.importer.all_module_attrs[self.name]   123    124         for name in self.classes.keys():   125             del self.importer.classes[name]   126             del self.importer.all_class_attrs[name]   127             del self.importer.all_instance_attrs[name]   128             del self.importer.all_instance_attr_constants[name]   129    130             for name, bases in self.classes.items():   131                 for base in bases:   132    133                     # Get the identity of the class from the reference.   134    135                     base = base.get_origin()   136    137                     try:   138                         self.importer.subclasses[base].remove(name)   139                     except (KeyError, ValueError):   140                         pass   141    142         remove_items(self.importer.all_name_references, self.name_references)   143         remove_items(self.importer.all_initialised_names, self.initialised_names)   144         remove_items(self.importer.all_aliased_names, self.aliased_names)   145         remove_items(self.importer.all_attr_accesses, self.attr_accesses)   146         remove_items(self.importer.all_const_accesses, self.const_accesses)   147         remove_items(self.importer.all_attr_access_modifiers, self.attr_access_modifiers)   148         remove_items(self.importer.all_constants, self.constants)   149         remove_items(self.importer.all_constant_values, self.constant_values)   150    151         # Remove this module's objects from the importer. Objects are   152         # automatically propagated when defined.   153    154         ref = self.importer.objects.get(self.name)   155         if ref and ref.has_kind("<module>"):   156             del self.importer.objects[self.name]   157    158         for name, ref in self.objects.items():   159             if not ref.has_kind("<module>"):   160                 del self.importer.objects[name]   161    162     def collect(self):   163    164         "Collect removed objects."   165    166         for name, ref in self.objects.items():   167             if not self.importer.objects.has_key(ref.get_origin()) and self.importer.objects.has_key(name):   168                 del self.importer.objects[name]   169    170     def propagate_attrs(self):   171    172         "Derive attributes from the class and module member details."   173    174         # Initialise class attribute records for all classes.   175    176         for name in self.classes.keys():   177             self.importer.all_class_attrs[name] = self.class_attrs[name] = {}   178    179         # Separate the objects into module and class attributes.   180    181         for name in self.objects.keys():   182             if "." in name:   183                 parent, attrname = name.rsplit(".", 1)   184                 if self.classes.has_key(parent):   185                     self.class_attrs[parent][attrname] = name   186                 elif parent == self.name:   187                     self.module_attrs.add(attrname)   188    189         # Propagate the module attributes.   190    191         self.importer.all_module_attrs[self.name] = self.module_attrs   192    193     def propagate_name_references(self):   194    195         "Propagate name references for the module."   196    197         self.importer.all_initialised_names.update(self.initialised_names)   198         self.importer.all_aliased_names.update(self.aliased_names)   199    200     def propagate_attr_accesses(self):   201    202         "Propagate attribute accesses for the module."   203    204         self.importer.all_attr_accesses.update(self.attr_accesses)   205         self.importer.all_const_accesses.update(self.const_accesses)   206         self.importer.all_attr_access_modifiers.update(self.attr_access_modifiers)   207    208     def propagate_constants(self):   209    210         "Propagate constant values and aliases for the module."   211    212         self.importer.all_constants.update(self.constants)   213         self.importer.all_constant_values.update(self.constant_values)   214    215         for name in self.classes.keys():   216             self.importer.all_instance_attrs[name] = self.instance_attrs.get(name) or {}   217             self.importer.all_instance_attr_constants[name] = self.instance_attr_constants.get(name) or {}   218    219     def set_object(self, name, value=None):   220    221         "Set an object with the given 'name' and the given 'value'."   222    223         # Decode any string value, with a new reference being returned even   224         # given a provided reference.   225    226         ref = decode_reference(value, name)   227         self.add_deferred(ref)   228         self._set_object(name, ref)   229    230     def _set_object(self, name, ref):   231    232         # Determine how the object properties will be defined.   233    234         multiple = self.objects.has_key(name) and self.objects[name] != ref   235         self.importer.objects[name] = self.objects[name] = multiple and ref.as_var() or ref   236    237     def queue_module(self, name, required=False):   238    239         """   240         Queue the module with the given 'name'. If 'required' is true (it is   241         false by default), indicate that the module is required in the final   242         program.   243         """   244    245         self.importer.queue_module(name, self, required)   246         if required:   247             self.required.add(name)   248         self.imports.add(name)   249    250 class InspectionNaming:   251    252     "Name operations related to inspection."   253    254     # Module-relative naming.   255    256     def is_global(self, name):   257    258         """   259         Return whether 'name' is registered as a global in the current   260         namespace.   261         """   262    263         path = self.get_namespace_path()   264         return name in self.scope_globals.get(path, [])   265    266     def get_global(self, name):   267    268         """   269         Get the global of the given 'name' from this module, returning a   270         reference incorporating the original definition details.   271         """   272    273         path = self.get_global_path(name)   274         return self.objects.get(path)   275    276     # Name definition discovery.   277    278     def get_global_or_builtin(self, name):   279    280         """   281         Return a reference for the given 'name' found in this module or in the   282         __builtins__.   283         """   284    285         return self.get_global(name) or self.get_builtin(name)   286    287     def get_builtin(self, name):   288    289         "Return a reference to the built-in with the given 'name'."   290    291         self.queue_module("__builtins__")   292         ref = Reference("<depends>", "__builtins__.%s" % name)   293         self.deferred.append(ref)   294         return ref   295    296     def get_builtin_class(self, name):   297    298         "Return a reference to the actual object providing 'name'."   299    300         objpath = get_builtin_class(name)   301         module_name = get_builtin_module(name)   302    303         if self.name != module_name:   304             self.queue_module(module_name, True)   305    306         return Reference("<class>", objpath)   307    308     def get_object(self, path, defer=True):   309    310         """   311         Get the details of an object with the given 'path'. Where the object   312         cannot be resolved, an unresolved reference is returned if 'defer' is   313         set to a true value (the default). Otherwise, None is returned.   314         """   315    316         if self.objects.has_key(path):   317             return self.objects[path]   318         elif defer:   319             ref = Reference("<depends>", path)   320             self.deferred.append(ref)   321             return ref   322         else:   323             return None   324    325     def import_name_from_module(self, name, module_name):   326    327         "Import 'name' from the module having the given 'module_name'."   328    329         if module_name != self.name:   330             self.queue_module(module_name)   331         ref = Reference("<depends>", "%s.%s" % (module_name, name))   332         self.deferred.append(ref)   333         return ref   334    335     def add_deferred(self, ref):   336    337         "Record 'ref' as a deferred reference."   338    339         if ref.has_kind("<depends>"):   340             self.deferred.append(ref)   341    342 class CachedModule(BasicModule):   343    344     "A cached module."   345    346     def __repr__(self):   347         return "CachedModule(%r, %r)" % (self.name, self.importer)   348    349     def set_object(self, name, value=None):   350    351         "Set an object with the given 'name' and the given 'value'."   352    353         # Decode any string value, with a new reference being returned even   354         # given a provided reference.   355    356         ref = decode_reference(value, name)   357         self._set_object(name, ref)   358    359     def to_cache(self, filename):   360    361         "Not actually writing the module back to 'filename'."   362    363         pass   364    365     def from_cache(self, filename):   366    367         """   368         Read a module's details from the file with the given 'filename' as   369         described in the to_cache method of InspectedModule.   370         """   371    372         f = open(filename)   373         try:   374             self.filename = f.readline().rstrip()   375    376             f.readline() # (empty line)   377    378             self._get_imports(f)   379             self._get_deferred(f)   380             self._get_special(f)   381             self._get_members(f)   382             self._get_class_relationships(f)   383             self._get_instance_attrs(f)   384             self._get_instance_attr_constants(f)   385             self.from_lines(f, self.names_used)     # "names used:"   386             self._get_name_references(f)   387             self._get_initialised_names(f)   388             self._get_aliased_names(f)   389             self._get_function_parameters(f)   390             self._get_function_defaults(f)   391             self._get_function_locals(f)   392             self.from_lines(f, self.scope_globals)  # "scope globals:"   393             self._get_attribute_usage(f)   394             self._get_attr_accesses(f)   395             self._get_const_accesses(f)   396             self._get_attr_accessors(f)   397             self._get_attr_access_modifiers(f)   398             self._get_constant_literals(f)   399             self._get_constant_values(f)   400             self._get_exception_namespaces(f)   401    402         finally:   403             f.close()   404    405     def complete(self):   406         self.propagate()   407    408     def _get_imports(self, f):   409         f.readline() # "imports:"   410         line = f.readline().strip()   411         self.required = line != "{}" and set(line.split(", ")) or set()   412         line = f.readline().strip()   413         self.imports = line != "{}" and set(line.split(", ")) or set()   414         f.readline()   415    416         for name in self.required:   417             self.queue_module(name, True)   418         for name in self.imports:   419             self.queue_module(name)   420    421     def _get_deferred(self, f):   422         f.readline() # "deferred:"   423         line = f.readline().rstrip()   424         self.deferred = map(decode_reference, line.split(", "))   425         f.readline()   426    427     def _get_special(self, f):   428         f.readline() # "special:"   429         line = f.readline().rstrip()   430         while line:   431             name, ref, paths = self._get_fields(line, 3)   432             self.special[name] = decode_reference(ref), paths.split(", ")   433             line = f.readline().rstrip()   434    435     def _get_members(self, f):   436         f.readline() # "members:"   437         line = f.readline().rstrip()   438         while line:   439             name, ref = line.split(" ", 1)   440             self.set_object(name, ref)   441             line = f.readline().rstrip()   442    443     def _get_class_relationships(self, f):   444         f.readline() # "class relationships:"   445         line = f.readline().rstrip()   446         while line:   447             name, value = self._get_fields(line)   448             values = value and value.split(", ") or []   449             self.importer.classes[name] = self.classes[name] = map(decode_reference, values)   450             self.importer.subclasses[name] = set()   451             line = f.readline().rstrip()   452    453     def _get_instance_attrs(self, f):   454         f.readline() # "instance attributes:"   455         line = f.readline().rstrip()   456         while line:   457             name, value = self._get_fields(line)   458             self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or [])   459             line = f.readline().rstrip()   460    461     def _get_instance_attr_constants(self, f):   462         f.readline() # "instance attribute constants:"   463         line = f.readline().rstrip()   464         while line:   465             name, attrname, ref = self._get_fields(line, 3)   466             init_item(self.instance_attr_constants, name, dict)   467             self.instance_attr_constants[name][attrname] = decode_reference(ref)   468             line = f.readline().rstrip()   469    470     def _get_name_references(self, f):   471         f.readline() # "name references:"   472         line = f.readline().rstrip()   473         while line:   474             name, ref = self._get_fields(line)   475             self.importer.all_name_references[name] = self.name_references[name] = decode_reference(ref)   476             line = f.readline().rstrip()   477    478     def _get_initialised_names(self, f):   479         f.readline() # "initialised names:"   480         line = f.readline().rstrip()   481         while line:   482             path, name, version, value = self._get_fields(line, 4)   483             init_item(self.initialised_names, (path, name), dict)   484             self.initialised_names[(path, name)][int(version)] = decode_reference(value)   485             line = f.readline().rstrip()   486    487     def _get_aliased_names(self, f):   488         f.readline() # "aliased names:"   489         line = f.readline().rstrip()   490         while line:   491             path, name, version, original_path, original_name, attrnames, number = self._get_fields(line, 7)   492             init_item(self.aliased_names, (path, name), dict)   493             if number == "{}": number = None   494             else: number = int(number)   495             self.aliased_names[(path, name)][int(version)] = (original_path, original_name, attrnames != "{}" and attrnames or None, number)   496             line = f.readline().rstrip()   497    498     def _get_function_parameters(self, f):   499         f.readline() # "function parameters:"   500         line = f.readline().rstrip()   501         while line:   502             function, names = self._get_fields(line)   503             self.importer.function_parameters[function] = \   504                 self.function_parameters[function] = names != "{}" and names.split(", ") or []   505             line = f.readline().rstrip()   506    507     def _get_function_defaults(self, f):   508         f.readline() # "function default parameters:"   509         line = f.readline().rstrip()   510         while line:   511             function, defaults = self._get_fields(line)   512             self.importer.function_defaults[function] = \   513                 self.function_defaults[function] = l = []   514             if defaults != "{}":   515                 for value in defaults.split(", "):   516                     name, default = value.split("=")   517                     default = decode_reference(default)   518                     l.append((name, default))   519             line = f.readline().rstrip()   520    521     def _get_function_locals(self, f):   522         f.readline() # "function locals:"   523         line = f.readline().rstrip()   524         while line:   525             function, name, value = self._get_fields(line, 3)   526             init_item(self.function_locals, function, dict)   527             init_item(self.importer.function_locals, function, dict)   528             if name != "{}":   529                 self.importer.function_locals[function][name] = \   530                     self.function_locals[function][name] = decode_reference(value)   531             line = f.readline().rstrip()   532    533     def _get_attribute_usage(self, f):   534         f.readline() # "attribute usage:"   535         line = f.readline().rstrip()   536         while line:   537             unit, value = self._get_fields(line)   538             init_item(self.attr_usage, unit, dict)   539             self.usage_from_cache(value, self.attr_usage[unit])   540             line = f.readline().rstrip()   541    542     def _get_attr_accesses(self, f):   543         f.readline() # "attribute accesses:"   544         line = f.readline().rstrip()   545         while line:   546             name, value = self._get_fields(line)   547             self.attr_accesses[name] = set(value.split(", "))   548             line = f.readline().rstrip()   549    550     def _get_const_accesses(self, f):   551         f.readline() # "constant accesses:"   552         line = f.readline().rstrip()   553         while line:   554             name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6)   555             if attrnames == "{}": attrnames = None   556             init_item(self.const_accesses, name, dict)   557             self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "")   558             line = f.readline().rstrip()   559    560     def _get_attr_accessors(self, f):   561         f.readline() # "attribute access usage:"   562         line = f.readline().rstrip()   563         while line:   564             objpath, name, attrname, value = self._get_fields(line, 4)   565             if attrname == "{}": attrname = None   566             access = name, attrname   567             init_item(self.attr_accessors, objpath, dict)   568             init_item(self.attr_accessors[objpath], access, list)   569             positions = map(int, value.split(", "))   570             self.attr_accessors[objpath][access].append(positions)   571             line = f.readline().rstrip()   572    573     def _get_attr_access_modifiers(self, f):   574         f.readline() # "attribute access modifiers:"   575         line = f.readline().rstrip()   576         while line:   577             objpath, name, attrnames, value = self._get_fields(line, 4)   578             if name == "{}": name = None   579             if attrnames == "{}": attrnames = None   580             access = name, attrnames   581             init_item(self.attr_access_modifiers, objpath, dict)   582             init_item(self.attr_access_modifiers[objpath], access, list)   583             modifiers = decode_modifiers(value)   584             self.attr_access_modifiers[objpath][access] = modifiers   585             line = f.readline().rstrip()   586    587     def _get_constant_literals(self, f):   588         f.readline() # "constant literals:"   589         line = f.readline().rstrip()   590         last_path = None   591         n = None   592         while line:   593             path, value_type, encoding, value = self._get_fields(line, 4)   594             if path != last_path:   595                 n = 0   596                 last_path = path   597             else:   598                 n += 1   599             init_item(self.constants, path, dict)   600             value = eval(value)   601             encoding = encoding != "{}" and encoding or None   602             self.constants[path][(value, value_type, encoding)] = n   603             line = f.readline().rstrip()   604    605     def _get_constant_values(self, f):   606         f.readline() # "constant values:"   607         line = f.readline().rstrip()   608         while line:   609             name, value_type, encoding, value = self._get_fields(line, 4)   610             value = eval(value)   611             encoding = encoding != "{}" and encoding or None   612             self.constant_values[name] = value, value_type, encoding   613             line = f.readline().rstrip()   614    615     def _get_exception_namespaces(self, f):   616         f.readline() # "exception namespaces:"   617         value = f.readline().rstrip()   618         self.exception_namespaces = value and set(value.split(", ")) or set()   619         f.readline()   620    621     # Generic parsing methods.   622    623     def from_lines(self, f, d):   624    625         "Read lines from 'f', populating 'd'."   626    627         f.readline() # section heading   628         line = f.readline().rstrip()   629         while line:   630             name, value = self._get_fields(line)   631             d[name] = set(value and value.split(", ") or [])   632             line = f.readline().rstrip()   633    634     def usage_from_cache(self, value, mapping):   635    636         """   637         Interpret the given 'value' containing name and usage information,   638         storing the information in the given 'mapping'.   639         """   640    641         local, usage = self._get_fields(value)   642         init_item(mapping, local, list)   643         self._usage_from_cache(mapping[local], usage)   644    645     def _usage_from_cache(self, d, usage):   646    647         # Interpret descriptions of each version of the name.   648    649         all_usages = set()   650         for attrnames in usage.split("; "):   651             if attrnames == "{}":   652                 all_attrnames = ()   653             else:   654                 all_attrnames = decode_usage(attrnames)   655             all_usages.add(all_attrnames)   656    657         d.append(all_usages)   658    659     def _get_fields(self, s, n=2):   660         result = s.split(" ", n-1)   661         if len(result) == n:   662             return result   663         else:   664             return tuple(result) + tuple([""] * (n - len(result)))   665    666 class CacheWritingModule:   667    668     """   669     A mix-in providing cache-writing support, to be combined with BasicModule.   670     """   671    672     def to_cache(self, filename):   673    674         """   675         Write a cached representation of the inspected module to the file having   676         the given 'filename'.   677         """   678    679         f = open(filename, "w")   680         try:   681             print >>f, self.filename   682    683             print >>f   684             print >>f, "imports:"   685             required = list(self.required)   686             required.sort()   687             print >>f, required and ", ".join(required) or "{}"   688             imports = list(self.imports)   689             imports.sort()   690             print >>f, imports and ", ".join(imports) or "{}"   691    692             print >>f   693             print >>f, "deferred:"   694             deferred = map(str, set(self.deferred))   695             deferred.sort()   696             print >>f, ", ".join(deferred)   697    698             print >>f   699             print >>f, "special:"   700             names = self.special.keys()   701             names.sort()   702             for name in names:   703                 ref, paths = self.special[name]   704                 print >>f, name, ref, ", ".join(paths)   705    706             print >>f   707             print >>f, "members:"   708             objects = self.objects.keys()   709             objects.sort()   710             for name in objects:   711                 print >>f, name, self.objects[name]   712    713             print >>f   714             print >>f, "class relationships:"   715             classes = self.classes.keys()   716             classes.sort()   717             for class_ in classes:   718                 bases = self.classes[class_]   719                 if bases:   720                     print >>f, class_, ", ".join(map(str, bases))   721                 else:   722                     print >>f, class_   723    724             self.to_lines(f, "instance attributes:", self.instance_attrs)   725    726             print >>f   727             print >>f, "instance attribute constants:"   728             classes = self.instance_attr_constants.items()   729             classes.sort()   730             for name, attrs in classes:   731                 attrs = attrs.items()   732                 attrs.sort()   733                 for attrname, ref in attrs:   734                     print >>f, name, attrname, ref   735    736             self.to_lines(f, "names used:", self.names_used)   737    738             print >>f   739             print >>f, "name references:"   740             refs = self.name_references.items()   741             refs.sort()   742             for name, ref in refs:   743                 print >>f, name, ref   744    745             print >>f   746             print >>f, "initialised names:"   747             assignments = self.initialised_names.items()   748             assignments.sort()   749             for (path, name), refs in assignments:   750                 versions = refs.items()   751                 versions.sort()   752                 for version, ref in versions:   753                     print >>f, path, name, version, ref   754    755             print >>f   756             print >>f, "aliased names:"   757             assignments = self.aliased_names.items()   758             assignments.sort()   759             for (path, name), aliases in assignments:   760                 versions = aliases.items()   761                 versions.sort()   762                 for version, alias in versions:   763                     original_path, original_name, attrnames, number = alias   764                     print >>f, path, name, version, original_path, original_name, attrnames or "{}", number is None and "{}" or number   765    766             print >>f   767             print >>f, "function parameters:"   768             functions = self.function_parameters.keys()   769             functions.sort()   770             for function in functions:   771                 parameters = self.function_parameters[function]   772                 if parameters:   773                     print >>f, function, ", ".join(parameters)   774                 else:   775                     print >>f, function, "{}"   776    777             print >>f   778             print >>f, "function default parameters:"   779             functions = self.function_defaults.keys()   780             functions.sort()   781             for function in functions:   782                 parameters = self.function_defaults[function]   783                 if parameters:   784                     print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters])   785                 else:   786                     print >>f, function, "{}"   787    788             print >>f   789             print >>f, "function locals:"   790             functions = self.function_locals.keys()   791             functions.sort()   792             for function in functions:   793                 names = self.function_locals[function].items()   794                 if names:   795                     names.sort()   796                     for name, value in names:   797                         print >>f, function, name, value   798                 else:   799                     print >>f, function, "{}"   800    801             self.to_lines(f, "scope globals:", self.scope_globals)   802    803             print >>f   804             print >>f, "attribute usage:"   805             units = self.attr_usage.keys()   806             units.sort()   807             for unit in units:   808                 d = self.attr_usage[unit]   809                 self.usage_to_cache(d, f, unit)   810    811             print >>f   812             print >>f, "attribute accesses:"   813             paths = self.attr_accesses.keys()   814             paths.sort()   815             for path in paths:   816                 accesses = list(self.attr_accesses[path])   817                 accesses.sort()   818                 print >>f, path, ", ".join(accesses)   819    820             print >>f   821             print >>f, "constant accesses:"   822             paths = self.const_accesses.keys()   823             paths.sort()   824             for path in paths:   825                 accesses = self.const_accesses[path].items()   826                 accesses.sort()   827                 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses:   828                     print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}"   829    830             print >>f   831             print >>f, "attribute access usage:"   832             paths = self.attr_accessors.keys()   833             paths.sort()   834             for path in paths:   835                 all_accesses = self.attr_accessors[path].items()   836                 all_accesses.sort()   837                 for (name, attrname), accesses in all_accesses:   838                     for positions in accesses:   839                         positions = map(str, positions)   840                         print >>f, path, name, attrname or "{}", ", ".join(positions)   841    842             print >>f   843             print >>f, "attribute access modifiers:"   844             paths = self.attr_access_modifiers.keys()   845             paths.sort()   846             for path in paths:   847                 all_accesses = self.attr_access_modifiers[path].items()   848                 all_accesses.sort()   849                 for (name, attrnames), modifiers in all_accesses:   850                     print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers)   851    852             print >>f   853             print >>f, "constant literals:"   854             paths = self.constants.keys()   855             paths.sort()   856             for path in paths:   857                 constants = []   858                 for (value, value_type, encoding), n in self.constants[path].items():   859                     constants.append((n, value_type, encoding, value))   860                 constants.sort()   861                 for n, value_type, encoding, value in constants:   862                     print >>f, path, value_type, encoding or "{}", repr(value)   863    864             print >>f   865             print >>f, "constant values:"   866             names = self.constant_values.keys()   867             names.sort()   868             for name in names:   869                 value, value_type, encoding = self.constant_values[name]   870                 print >>f, name, value_type, encoding or "{}", repr(value)   871    872             print >>f   873             print >>f, "exception namespaces:"   874             paths = list(self.exception_namespaces)   875             paths.sort()   876             print >>f, ", ".join(paths)   877    878         finally:   879             f.close()   880    881     def to_lines(self, f, heading, d):   882    883         "Write lines to 'f' with the given 'heading', using 'd'."   884    885         print >>f   886         print >>f, heading   887         keys = d.keys()   888         keys.sort()   889         for key in keys:   890             attrs = list(d[key])   891             if attrs:   892                 attrs.sort()   893                 print >>f, key, ", ".join(attrs)   894    895     def usage_to_cache(self, details, f, prefix):   896    897         "Write the given namespace usage details to the cache."   898    899         names = list(details.keys())   900         if names:   901             names.sort()   902             for name in names:   903                 if details[name]:   904    905                     # Produce descriptions for each version of the name.   906    907                     for version in details[name]:   908                         all_usages = []   909                         for usage in version:   910                             all_usages.append(encode_usage(usage))   911    912                         print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages))   913    914 # vim: tabstop=4 expandtab shiftwidth=4