Lichen

modules.py

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