Lichen

modules.py

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