Lichen

modules.py

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