Lichen

modules.py

88:674502205b57
2016-10-08 Paul Boddie Combined attribute invocation information with attribute usage.
     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, decode_usage, 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 in each namespace.    59     60         self.names_used = {}    61     62         # Function details.    63     64         self.function_parameters = {}    65         self.function_defaults = {}    66         self.function_locals = {}    67         self.scope_globals = {}    68     69         # Invocation details.    70     71         self.function_targets = {}    72         self.function_arguments = {}    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].get_kind() != ref.get_kind()   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         # NOTE: This makes assumptions about the __builtins__ structure.   301    302         module_name = "__builtins__.%s" % name   303         if self.name != module_name:   304             self.queue_module(module_name, True)   305         return Reference("<class>", "__builtins__.%s.%s" % (name, name))   306    307     def get_object(self, path):   308    309         """   310         Get the details of an object with the given 'path'. Where the object   311         cannot be resolved, an unresolved reference is returned.   312         """   313    314         if self.objects.has_key(path):   315             return self.objects[path]   316         else:   317             ref = Reference("<depends>", path)   318             self.deferred.append(ref)   319             return ref   320    321     def import_name_from_module(self, name, module_name):   322    323         "Import 'name' from the module having the given 'module_name'."   324    325         if module_name != self.name:   326             self.queue_module(module_name)   327         return Reference("<depends>", "%s.%s" % (module_name, name))   328    329     def add_deferred(self, ref):   330    331         "Record 'ref' as a deferred reference."   332    333         if ref.has_kind("<depends>"):   334             self.deferred.append(ref)   335    336 class CachedModule(BasicModule):   337    338     "A cached module."   339    340     def __repr__(self):   341         return "CachedModule(%r, %r)" % (self.name, self.importer)   342    343     def set_object(self, name, value=None):   344    345         "Set an object with the given 'name' and the given 'value'."   346    347         # Decode any string value, with a new reference being returned even   348         # given a provided reference.   349    350         ref = decode_reference(value, name)   351         self._set_object(name, ref)   352    353     def to_cache(self, filename):   354    355         "Not actually writing the module back to 'filename'."   356    357         pass   358    359     def from_cache(self, filename):   360    361         """   362         Read a module's details from the file with the given 'filename' as   363         described in the to_cache method of InspectedModule.   364         """   365    366         f = open(filename)   367         try:   368             self.filename = f.readline().rstrip()   369    370             f.readline() # (empty line)   371    372             self._get_imports(f)   373             self._get_members(f)   374             self._get_class_relationships(f)   375             self._get_instance_attrs(f)   376             self._get_instance_attr_constants(f)   377             self.from_lines(f, self.names_used)     # "names used:"   378             self._get_name_references(f)   379             self._get_initialised_names(f)   380             self._get_aliased_names(f)   381             self._get_function_parameters(f)   382             self._get_function_defaults(f)   383             self._get_function_locals(f)   384             self.from_lines(f, self.scope_globals)  # "scope globals:"   385             self._get_function_targets(f)   386             self._get_function_arguments(f)   387             self._get_attribute_usage(f)   388             self._get_attr_accesses(f)   389             self._get_const_accesses(f)   390             self._get_attr_accessors(f)   391             self._get_attr_access_modifiers(f)   392             self._get_constant_literals(f)   393             self._get_constant_values(f)   394    395         finally:   396             f.close()   397    398     def complete(self):   399         self.propagate()   400    401     def _get_imports(self, f):   402         f.readline() # "imports:"   403         line = f.readline().strip()   404         self.required = line != "{}" and set(line.split(", ")) or set()   405         line = f.readline().strip()   406         self.imports = line != "{}" and set(line.split(", ")) or set()   407         f.readline()   408    409         for name in self.required:   410             self.queue_module(name, True)   411         for name in self.imports:   412             self.queue_module(name)   413    414     def _get_members(self, f):   415         f.readline() # "members:"   416         line = f.readline().rstrip()   417         while line:   418             name, ref = line.split(" ", 1)   419             self.set_object(name, ref)   420             line = f.readline().rstrip()   421    422     def _get_class_relationships(self, f):   423         f.readline() # "class relationships:"   424         line = f.readline().rstrip()   425         while line:   426             name, value = self._get_fields(line)   427             values = value and value.split(", ") or []   428             self.importer.classes[name] = self.classes[name] = map(decode_reference, values)   429             self.importer.subclasses[name] = set()   430             line = f.readline().rstrip()   431    432     def _get_instance_attrs(self, f):   433         f.readline() # "instance attributes:"   434         line = f.readline().rstrip()   435         while line:   436             name, value = self._get_fields(line)   437             self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or [])   438             line = f.readline().rstrip()   439    440     def _get_instance_attr_constants(self, f):   441         f.readline() # "instance attribute constants:"   442         line = f.readline().rstrip()   443         while line:   444             name, attrname, ref = self._get_fields(line, 3)   445             init_item(self.instance_attr_constants, name, dict)   446             self.instance_attr_constants[name][attrname] = decode_reference(ref)   447             line = f.readline().rstrip()   448    449     def _get_name_references(self, f):   450         f.readline() # "name references:"   451         line = f.readline().rstrip()   452         while line:   453             name, ref = self._get_fields(line)   454             self.importer.all_name_references[name] = self.name_references[name] = decode_reference(ref)   455             line = f.readline().rstrip()   456    457     def _get_initialised_names(self, f):   458         f.readline() # "initialised names:"   459         line = f.readline().rstrip()   460         while line:   461             name, version, value = self._get_fields(line, 3)   462             init_item(self.initialised_names, name, dict)   463             self.initialised_names[name][int(version)] = decode_reference(value)   464             line = f.readline().rstrip()   465    466     def _get_aliased_names(self, f):   467         f.readline() # "aliased names:"   468         line = f.readline().rstrip()   469         while line:   470             name, version, original_name, attrnames, number = self._get_fields(line, 5)   471             init_item(self.aliased_names, name, dict)   472             if number == "{}": number = None   473             else: number = int(number)   474             self.aliased_names[name][int(version)] = (original_name, attrnames != "{}" and attrnames or None, number)   475             line = f.readline().rstrip()   476    477     def _get_function_parameters(self, f):   478         f.readline() # "function parameters:"   479         line = f.readline().rstrip()   480         while line:   481             function, names = self._get_fields(line)   482             self.importer.function_parameters[function] = \   483                 self.function_parameters[function] = names != "{}" and names.split(", ") or []   484             line = f.readline().rstrip()   485    486     def _get_function_defaults(self, f):   487         f.readline() # "function default parameters:"   488         line = f.readline().rstrip()   489         while line:   490             function, defaults = self._get_fields(line)   491             self.importer.function_defaults[function] = \   492                 self.function_defaults[function] = l = []   493             if defaults != "{}":   494                 for value in defaults.split(", "):   495                     name, default = value.split("=")   496                     default = decode_reference(default)   497                     l.append((name, default))   498             line = f.readline().rstrip()   499    500     def _get_function_locals(self, f):   501         f.readline() # "function locals:"   502         line = f.readline().rstrip()   503         while line:   504             function, name, value = self._get_fields(line, 3)   505             init_item(self.function_locals, function, dict)   506             if name != "{}":   507                 self.function_locals[function][name] = decode_reference(value)   508             line = f.readline().rstrip()   509    510     def _get_function_targets(self, f):   511         f.readline() # "function targets:"   512         line = f.readline().rstrip()   513         while line:   514             function, n = self._get_fields(line)   515             self.importer.function_targets[function] = \   516                 self.function_targets[function] = int(n)   517             line = f.readline().rstrip()   518    519     def _get_function_arguments(self, f):   520         f.readline() # "function arguments:"   521         line = f.readline().rstrip()   522         while line:   523             function, n = self._get_fields(line)   524             self.importer.function_arguments[function] = \   525                 self.function_arguments[function] = int(n)   526             line = f.readline().rstrip()   527    528     def _get_attribute_usage(self, f):   529         f.readline() # "attribute usage:"   530         line = f.readline().rstrip()   531         while line:   532             unit, value = self._get_fields(line)   533             init_item(self.attr_usage, unit, dict)   534             self.usage_from_cache(value, self.attr_usage[unit])   535             line = f.readline().rstrip()   536    537     def _get_attr_accesses(self, f):   538         f.readline() # "attribute accesses:"   539         line = f.readline().rstrip()   540         while line:   541             name, value = self._get_fields(line)   542             self.attr_accesses[name] = set(value.split(", "))   543             line = f.readline().rstrip()   544    545     def _get_const_accesses(self, f):   546         f.readline() # "constant accesses:"   547         line = f.readline().rstrip()   548         while line:   549             name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6)   550             if attrnames == "{}": attrnames = None   551             init_item(self.const_accesses, name, dict)   552             self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "")   553             line = f.readline().rstrip()   554    555     def _get_attr_accessors(self, f):   556         f.readline() # "attribute access usage:"   557         line = f.readline().rstrip()   558         while line:   559             objpath, name, attrname, value = self._get_fields(line, 4)   560             if attrname == "{}": attrname = None   561             access = name, attrname   562             init_item(self.attr_accessors, objpath, dict)   563             init_item(self.attr_accessors[objpath], access, list)   564             positions = map(int, value.split(", "))   565             self.attr_accessors[objpath][access].append(positions)   566             line = f.readline().rstrip()   567    568     def _get_attr_access_modifiers(self, f):   569         f.readline() # "attribute access modifiers:"   570         line = f.readline().rstrip()   571         while line:   572             objpath, name, attrnames, value = self._get_fields(line, 4)   573             if name == "{}": name = None   574             if attrnames == "{}": attrnames = None   575             access = name, attrnames   576             init_item(self.attr_access_modifiers, objpath, dict)   577             init_item(self.attr_access_modifiers[objpath], access, list)   578             modifiers = [decode_modifier_term(s) for s in value]   579             self.attr_access_modifiers[objpath][access] = modifiers   580             line = f.readline().rstrip()   581    582     def _get_constant_literals(self, f):   583         f.readline() # "constant literals:"   584         line = f.readline().rstrip()   585         last_path = None   586         n = None   587         while line:   588             path, constant = self._get_fields(line)   589             if path != last_path:   590                 n = 0   591                 last_path = path   592             else:   593                 n += 1   594             init_item(self.constants, path, dict)   595             self.constants[path][eval(constant)] = n   596             line = f.readline().rstrip()   597    598     def _get_constant_values(self, f):   599         f.readline() # "constant values:"   600         line = f.readline().rstrip()   601         while line:   602             name, value_type, value = self._get_fields(line, 3)   603             self.constant_values[name] = eval(value), value_type   604             line = f.readline().rstrip()   605    606     # Generic parsing methods.   607    608     def from_lines(self, f, d):   609    610         "Read lines from 'f', populating 'd'."   611    612         f.readline() # section heading   613         line = f.readline().rstrip()   614         while line:   615             name, value = self._get_fields(line)   616             d[name] = set(value and value.split(", ") or [])   617             line = f.readline().rstrip()   618    619     def usage_from_cache(self, value, mapping):   620    621         """   622         Interpret the given 'value' containing name and usage information,   623         storing the information in the given 'mapping'.   624         """   625    626         local, usage = self._get_fields(value)   627         init_item(mapping, local, list)   628         self._usage_from_cache(mapping[local], usage)   629    630     def _usage_from_cache(self, d, usage):   631    632         # Interpret descriptions of each version of the name.   633    634         all_usages = set()   635         for attrnames in usage.split("; "):   636             if attrnames == "{}":   637                 all_attrnames = ()   638             else:   639                 all_attrnames = decode_usage(attrnames)   640             all_usages.add(all_attrnames)   641    642         d.append(all_usages)   643    644     def _get_fields(self, s, n=2):   645         result = s.split(" ", n-1)   646         if len(result) == n:   647             return result   648         else:   649             return tuple(result) + tuple([""] * (n - len(result)))   650    651 class CacheWritingModule:   652    653     """   654     A mix-in providing cache-writing support, to be combined with BasicModule.   655     """   656    657     def to_cache(self, filename):   658    659         """   660         Write a cached representation of the inspected module with the following   661         format to the file having the given 'filename':   662    663         filename   664         (empty line)   665         "imports:"   666         required module names   667         possibly required module names   668         "members:"   669         zero or more: qualified name " " reference   670         (empty line)   671         "class relationships:"   672         zero or more: qualified class name " " base class references   673         (empty line)   674         "instance attributes:"   675         zero or more: qualified class name " " instance attribute names   676         (empty line)   677         "instance attribute constants:"   678         zero or more: qualified class name " " attribute name " " reference   679         (empty line)   680         "names used:"   681         zero or more: qualified class/function/module name " " names   682         (empty line)   683         "names missing:"   684         zero or more: qualified class/function/module name " " names   685         (empty line)   686         "name references:"   687         zero or more: qualified name " " reference   688         (empty line)   689         "initialised names:"   690         zero or more: qualified name " " definition version " " reference   691         (empty line)   692         "aliased names:"   693         zero or more: qualified name " " definition version " " original name " " attribute names " " access number   694         (empty line)   695         "function parameters:"   696         zero or more: qualified function name " " parameter names   697         (empty line)   698         "function default parameters:"   699         zero or more: qualified function name " " parameter names with defaults   700         (empty line)   701         "function locals:"   702         zero or more: qualified function name " " local variable name " " reference   703         (empty line)   704         "scope globals:"   705         zero or more: qualified function name " " global variable names   706         (empty line)   707         "function targets:"   708         zero or more: qualified function name " " maximum number of targets allocated   709         (empty line)   710         "function arguments:"   711         zero or more: qualified function name " " maximum number of arguments allocated   712         (empty line)   713         "attribute usage:"   714         zero or more: qualified scope name " " local/global/qualified variable name " " usages   715         (empty line)   716         "attribute accesses:"   717         zero or more: qualified scope name " " attribute-chains   718         (empty line)   719         "constant accesses:"   720         zero or more: qualified function name " " attribute-chain " " reference " " remaining attribute-chain   721         (empty line)   722         "attribute access usage:"   723         zero or more: qualified function name " " local/global variable name " " attribute name " " definition versions   724         (empty line)   725         "attribute access modifiers:"   726         zero or more: qualified function name " " local/global variable name " " attribute name " " access modifiers   727         "constant literals:"   728         zero or more: qualified scope name " " constant literal   729         "constant values:"   730         zero or more: qualified name " " value type " " constant literal   731    732         All collections of names are separated by ", " characters.   733    734         References can be "<var>", a module name, or one of "<class>" or   735         "<function>" followed optionally by a ":" character and a qualified   736         name.   737    738         Parameter names with defaults are separated by ", " characters, with   739         each name followed by "=" and then followed by a reference. If "{}" is   740         indicated, no defaults are defined for the function. Similarly, function   741         locals may be indicated as "{}" meaning that there are no locals.   742    743         All usages (attribute usage sets) are separated by "; " characters, with   744         the special string "{}" representing an empty set.   745    746         Each usage is a collection of names separated by ", " characters, with   747         assigned attribute names suffixed with a "*" character.   748    749         Each attribute-chain expression is a dot-separated chain of attribute   750         names, with assignments suffixed with a "*" character.   751    752         Definition versions are separated by ", " characters and indicate the   753         name definition version associated with the access.   754    755         Access modifiers are separated by ", " characters and indicate features   756         of each access, with multiple accesses described on a single line.   757         """   758    759         f = open(filename, "w")   760         try:   761             print >>f, self.filename   762    763             print >>f   764             print >>f, "imports:"   765             required = list(self.required)   766             required.sort()   767             print >>f, required and ", ".join(required) or "{}"   768             imports = list(self.imports)   769             imports.sort()   770             print >>f, imports and ", ".join(imports) or "{}"   771    772             print >>f   773             print >>f, "members:"   774             objects = self.objects.keys()   775             objects.sort()   776             for name in objects:   777                 print >>f, name, self.objects[name]   778    779             print >>f   780             print >>f, "class relationships:"   781             classes = self.classes.keys()   782             classes.sort()   783             for class_ in classes:   784                 bases = self.classes[class_]   785                 if bases:   786                     print >>f, class_, ", ".join(map(str, bases))   787                 else:   788                     print >>f, class_   789    790             self.to_lines(f, "instance attributes:", self.instance_attrs)   791    792             print >>f   793             print >>f, "instance attribute constants:"   794             classes = self.instance_attr_constants.items()   795             classes.sort()   796             for name, attrs in classes:   797                 attrs = attrs.items()   798                 attrs.sort()   799                 for attrname, ref in attrs:   800                     print >>f, name, attrname, ref   801    802             self.to_lines(f, "names used:", self.names_used)   803    804             print >>f   805             print >>f, "name references:"   806             refs = self.name_references.items()   807             refs.sort()   808             for name, ref in refs:   809                 print >>f, name, ref   810    811             print >>f   812             print >>f, "initialised names:"   813             assignments = self.initialised_names.items()   814             assignments.sort()   815             for name, refs in assignments:   816                 versions = refs.items()   817                 versions.sort()   818                 for version, ref in versions:   819                     print >>f, name, version, ref   820    821             print >>f   822             print >>f, "aliased names:"   823             assignments = self.aliased_names.items()   824             assignments.sort()   825             for name, aliases in assignments:   826                 versions = aliases.items()   827                 versions.sort()   828                 for version, alias in versions:   829                     original_name, attrnames, number = alias   830                     print >>f, name, version, original_name, attrnames or "{}", number is None and "{}" or number   831    832             print >>f   833             print >>f, "function parameters:"   834             functions = self.function_parameters.keys()   835             functions.sort()   836             for function in functions:   837                 parameters = self.function_parameters[function]   838                 if parameters:   839                     print >>f, function, ", ".join(parameters)   840                 else:   841                     print >>f, function, "{}"   842    843             print >>f   844             print >>f, "function default parameters:"   845             functions = self.function_defaults.keys()   846             functions.sort()   847             for function in functions:   848                 parameters = self.function_defaults[function]   849                 if parameters:   850                     print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters])   851                 else:   852                     print >>f, function, "{}"   853    854             print >>f   855             print >>f, "function locals:"   856             functions = self.function_locals.keys()   857             functions.sort()   858             for function in functions:   859                 names = self.function_locals[function].items()   860                 if names:   861                     names.sort()   862                     for name, value in names:   863                         print >>f, function, name, value   864                 else:   865                     print >>f, function, "{}"   866    867             self.to_lines(f, "scope globals:", self.scope_globals)   868    869             print >>f   870             print >>f, "function targets:"   871             functions = self.function_targets.keys()   872             functions.sort()   873             for function in functions:   874                 print >>f, function, self.function_targets[function]   875    876             print >>f   877             print >>f, "function arguments:"   878             functions = self.function_arguments.keys()   879             functions.sort()   880             for function in functions:   881                 print >>f, function, self.function_arguments[function]   882    883             print >>f   884             print >>f, "attribute usage:"   885             units = self.attr_usage.keys()   886             units.sort()   887             for unit in units:   888                 d = self.attr_usage[unit]   889                 self.usage_to_cache(d, f, unit)   890    891             print >>f   892             print >>f, "attribute accesses:"   893             paths = self.attr_accesses.keys()   894             paths.sort()   895             for path in paths:   896                 accesses = list(self.attr_accesses[path])   897                 accesses.sort()   898                 print >>f, path, ", ".join(accesses)   899    900             print >>f   901             print >>f, "constant accesses:"   902             paths = self.const_accesses.keys()   903             paths.sort()   904             for path in paths:   905                 accesses = self.const_accesses[path].items()   906                 accesses.sort()   907                 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses:   908                     print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}"   909    910             print >>f   911             print >>f, "attribute access usage:"   912             paths = self.attr_accessors.keys()   913             paths.sort()   914             for path in paths:   915                 all_accesses = self.attr_accessors[path].items()   916                 all_accesses.sort()   917                 for (name, attrname), accesses in all_accesses:   918                     for positions in accesses:   919                         positions = map(str, positions)   920                         print >>f, path, name, attrname or "{}", ", ".join(positions)   921    922             print >>f   923             print >>f, "attribute access modifiers:"   924             paths = self.attr_access_modifiers.keys()   925             paths.sort()   926             for path in paths:   927                 all_accesses = self.attr_access_modifiers[path].items()   928                 all_accesses.sort()   929                 for (name, attrnames), modifiers in all_accesses:   930                     print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers)   931    932             print >>f   933             print >>f, "constant literals:"   934             paths = self.constants.keys()   935             paths.sort()   936             for path in paths:   937                 constants = [(v, k) for (k, v) in self.constants[path].items()]   938                 constants.sort()   939                 for n, constant in constants:   940                     print >>f, path, repr(constant)   941    942             print >>f   943             print >>f, "constant values:"   944             names = self.constant_values.keys()   945             names.sort()   946             for name in names:   947                 value, value_type = self.constant_values[name]   948                 print >>f, name, value_type, repr(value)   949    950         finally:   951             f.close()   952    953     def to_lines(self, f, heading, d):   954    955         "Write lines to 'f' with the given 'heading', using 'd'."   956    957         print >>f   958         print >>f, heading   959         keys = d.keys()   960         keys.sort()   961         for key in keys:   962             attrs = list(d[key])   963             if attrs:   964                 attrs.sort()   965                 print >>f, key, ", ".join(attrs)   966    967     def usage_to_cache(self, details, f, prefix):   968    969         "Write the given namespace usage details to the cache."   970    971         names = list(details.keys())   972         if names:   973             names.sort()   974             for name in names:   975                 if details[name]:   976    977                     # Produce descriptions for each version of the name.   978    979                     for version in details[name]:   980                         all_usages = []   981                         for usage in version:   982                             all_usages.append(encode_usage(usage))   983    984                         print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages))   985    986 # vim: tabstop=4 expandtab shiftwidth=4