Lichen

modules.py

643:a52196e59be8
2017-03-02 Paul Boddie Detect changes in structures or signatures that necessitate recompilation of all translated source files.
     1 #!/usr/bin/env python     2      3 """     4 Module abstractions.     5      6 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,     7               2014, 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>     8      9 This program is free software; you can redistribute it and/or modify it under    10 the terms of the GNU General Public License as published by the Free Software    11 Foundation; either version 3 of the License, or (at your option) any later    12 version.    13     14 This program is distributed in the hope that it will be useful, but WITHOUT    15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    16 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    17 details.    18     19 You should have received a copy of the GNU General Public License along with    20 this program.  If not, see <http://www.gnu.org/licenses/>.    21 """    22     23 from common import get_builtin_class, get_builtin_module, init_item, \    24                    remove_items, CommonModule    25 from encoders import decode_modifiers, decode_usage, encode_modifiers, encode_usage    26 from referencing import decode_reference, Reference    27 from results import ResolvedNameRef    28 import sys    29     30 class BasicModule(CommonModule):    31     32     "The basic module information."    33     34     def __init__(self, name, importer):    35         CommonModule.__init__(self, name, importer)    36     37         # Import details, primarily for cache output.    38     39         self.imports = set()    40         self.required = set()    41         self.deferred = []    42     43         # Global name information.    44     45         self.objects = {}    46         self.special = {}    47     48         # Class relationships.    49     50         self.classes = {}    51     52         # Attributes.    53     54         self.class_attrs = {}    55         self.instance_attrs = {}    56         self.instance_attr_constants = {}    57         self.module_attrs = set()    58     59         # Names used in each namespace.    60     61         self.names_used = {}    62     63         # Function details.    64     65         self.function_parameters = {}    66         self.function_defaults = {}    67         self.function_locals = {}    68         self.scope_globals = {}    69     70         # Invocation details.    71     72         self.function_targets = {}    73         self.function_arguments = {}    74     75         # Exception handler details.    76     77         self.exception_namespaces = set()    78     79         # Attribute usage at module and function levels.    80     81         self.attr_usage = {}    82         self.name_initialisers = {}    83     84         # General attribute access expressions.    85     86         self.attr_accesses = {}    87         self.const_accesses = {}    88     89         # Attribute accessor definition details.    90     91         self.attr_accessors = {}    92     93         # Assignment details for accesses.    94     95         self.attr_access_modifiers = {}    96     97         # Name resolution details.    98     99         self.name_references = {} # references to globals   100    101         # Initialisation-related details.   102    103         self.initialised_names = {}   104         self.aliased_names = {}   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_defaults(f)   396             self._get_function_locals(f)   397             self.from_lines(f, self.scope_globals)  # "scope globals:"   398             self._get_function_targets(f)   399             self._get_function_arguments(f)   400             self._get_attribute_usage(f)   401             self._get_attr_accesses(f)   402             self._get_const_accesses(f)   403             self._get_attr_accessors(f)   404             self._get_attr_access_modifiers(f)   405             self._get_constant_literals(f)   406             self._get_constant_values(f)   407             self._get_exception_namespaces(f)   408    409         finally:   410             f.close()   411    412     def complete(self):   413         self.propagate()   414    415     def _get_imports(self, f):   416         f.readline() # "imports:"   417         line = f.readline().strip()   418         self.required = line != "{}" and set(line.split(", ")) or set()   419         line = f.readline().strip()   420         self.imports = line != "{}" and set(line.split(", ")) or set()   421         f.readline()   422    423         for name in self.required:   424             self.queue_module(name, True)   425         for name in self.imports:   426             self.queue_module(name)   427    428     def _get_deferred(self, f):   429         f.readline() # "deferred:"   430         line = f.readline().rstrip()   431         self.deferred = map(decode_reference, line.split(", "))   432         f.readline()   433    434     def _get_special(self, f):   435         f.readline() # "special:"   436         line = f.readline().rstrip()   437         while line:   438             name, ref, paths = self._get_fields(line, 3)   439             self.special[name] = decode_reference(ref), paths.split(", ")   440             line = f.readline().rstrip()   441    442     def _get_members(self, f):   443         f.readline() # "members:"   444         line = f.readline().rstrip()   445         while line:   446             name, ref = line.split(" ", 1)   447             self.set_object(name, ref)   448             line = f.readline().rstrip()   449    450     def _get_class_relationships(self, f):   451         f.readline() # "class relationships:"   452         line = f.readline().rstrip()   453         while line:   454             name, value = self._get_fields(line)   455             values = value and value.split(", ") or []   456             self.importer.classes[name] = self.classes[name] = map(decode_reference, values)   457             self.importer.subclasses[name] = set()   458             line = f.readline().rstrip()   459    460     def _get_instance_attrs(self, f):   461         f.readline() # "instance attributes:"   462         line = f.readline().rstrip()   463         while line:   464             name, value = self._get_fields(line)   465             self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or [])   466             line = f.readline().rstrip()   467    468     def _get_instance_attr_constants(self, f):   469         f.readline() # "instance attribute constants:"   470         line = f.readline().rstrip()   471         while line:   472             name, attrname, ref = self._get_fields(line, 3)   473             init_item(self.instance_attr_constants, name, dict)   474             self.instance_attr_constants[name][attrname] = decode_reference(ref)   475             line = f.readline().rstrip()   476    477     def _get_name_references(self, f):   478         f.readline() # "name references:"   479         line = f.readline().rstrip()   480         while line:   481             name, ref = self._get_fields(line)   482             self.importer.all_name_references[name] = self.name_references[name] = decode_reference(ref)   483             line = f.readline().rstrip()   484    485     def _get_initialised_names(self, f):   486         f.readline() # "initialised names:"   487         line = f.readline().rstrip()   488         while line:   489             name, version, value = self._get_fields(line, 3)   490             init_item(self.initialised_names, name, dict)   491             self.initialised_names[name][int(version)] = decode_reference(value)   492             line = f.readline().rstrip()   493    494     def _get_aliased_names(self, f):   495         f.readline() # "aliased names:"   496         line = f.readline().rstrip()   497         while line:   498             name, version, path, original_name, attrnames, number = self._get_fields(line, 6)   499             init_item(self.aliased_names, name, dict)   500             if number == "{}": number = None   501             else: number = int(number)   502             self.aliased_names[name][int(version)] = (path, original_name, attrnames != "{}" and attrnames or None, number)   503             line = f.readline().rstrip()   504    505     def _get_function_parameters(self, f):   506         f.readline() # "function parameters:"   507         line = f.readline().rstrip()   508         while line:   509             function, names = self._get_fields(line)   510             self.importer.function_parameters[function] = \   511                 self.function_parameters[function] = names != "{}" and names.split(", ") or []   512             line = f.readline().rstrip()   513    514     def _get_function_defaults(self, f):   515         f.readline() # "function default parameters:"   516         line = f.readline().rstrip()   517         while line:   518             function, defaults = self._get_fields(line)   519             self.importer.function_defaults[function] = \   520                 self.function_defaults[function] = l = []   521             if defaults != "{}":   522                 for value in defaults.split(", "):   523                     name, default = value.split("=")   524                     default = decode_reference(default)   525                     l.append((name, default))   526             line = f.readline().rstrip()   527    528     def _get_function_locals(self, f):   529         f.readline() # "function locals:"   530         line = f.readline().rstrip()   531         while line:   532             function, name, value = self._get_fields(line, 3)   533             init_item(self.function_locals, function, dict)   534             init_item(self.importer.function_locals, function, dict)   535             if name != "{}":   536                 self.importer.function_locals[function][name] = \   537                     self.function_locals[function][name] = decode_reference(value)   538             line = f.readline().rstrip()   539    540     def _get_function_targets(self, f):   541         f.readline() # "function targets:"   542         line = f.readline().rstrip()   543         while line:   544             function, n = self._get_fields(line)   545             self.importer.function_targets[function] = \   546                 self.function_targets[function] = int(n)   547             line = f.readline().rstrip()   548    549     def _get_function_arguments(self, f):   550         f.readline() # "function arguments:"   551         line = f.readline().rstrip()   552         while line:   553             function, n = self._get_fields(line)   554             self.importer.function_arguments[function] = \   555                 self.function_arguments[function] = int(n)   556             line = f.readline().rstrip()   557    558     def _get_attribute_usage(self, f):   559         f.readline() # "attribute usage:"   560         line = f.readline().rstrip()   561         while line:   562             unit, value = self._get_fields(line)   563             init_item(self.attr_usage, unit, dict)   564             self.usage_from_cache(value, self.attr_usage[unit])   565             line = f.readline().rstrip()   566    567     def _get_attr_accesses(self, f):   568         f.readline() # "attribute accesses:"   569         line = f.readline().rstrip()   570         while line:   571             name, value = self._get_fields(line)   572             self.attr_accesses[name] = set(value.split(", "))   573             line = f.readline().rstrip()   574    575     def _get_const_accesses(self, f):   576         f.readline() # "constant accesses:"   577         line = f.readline().rstrip()   578         while line:   579             name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6)   580             if attrnames == "{}": attrnames = None   581             init_item(self.const_accesses, name, dict)   582             self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "")   583             line = f.readline().rstrip()   584    585     def _get_attr_accessors(self, f):   586         f.readline() # "attribute access usage:"   587         line = f.readline().rstrip()   588         while line:   589             objpath, name, attrname, value = self._get_fields(line, 4)   590             if attrname == "{}": attrname = None   591             access = name, attrname   592             init_item(self.attr_accessors, objpath, dict)   593             init_item(self.attr_accessors[objpath], access, list)   594             positions = map(int, value.split(", "))   595             self.attr_accessors[objpath][access].append(positions)   596             line = f.readline().rstrip()   597    598     def _get_attr_access_modifiers(self, f):   599         f.readline() # "attribute access modifiers:"   600         line = f.readline().rstrip()   601         while line:   602             objpath, name, attrnames, value = self._get_fields(line, 4)   603             if name == "{}": name = None   604             if attrnames == "{}": attrnames = None   605             access = name, attrnames   606             init_item(self.attr_access_modifiers, objpath, dict)   607             init_item(self.attr_access_modifiers[objpath], access, list)   608             modifiers = decode_modifiers(value)   609             self.attr_access_modifiers[objpath][access] = modifiers   610             line = f.readline().rstrip()   611    612     def _get_constant_literals(self, f):   613         f.readline() # "constant literals:"   614         line = f.readline().rstrip()   615         last_path = None   616         n = None   617         while line:   618             path, value_type, encoding, value = self._get_fields(line, 4)   619             if path != last_path:   620                 n = 0   621                 last_path = path   622             else:   623                 n += 1   624             init_item(self.constants, path, dict)   625             value = eval(value)   626             encoding = encoding != "{}" and encoding or None   627             self.constants[path][(value, value_type, encoding)] = n   628             line = f.readline().rstrip()   629    630     def _get_constant_values(self, f):   631         f.readline() # "constant values:"   632         line = f.readline().rstrip()   633         while line:   634             name, value_type, encoding, value = self._get_fields(line, 4)   635             value = eval(value)   636             encoding = encoding != "{}" and encoding or None   637             self.constant_values[name] = value, value_type, encoding   638             line = f.readline().rstrip()   639    640     def _get_exception_namespaces(self, f):   641         f.readline() # "exception namespaces:"   642         value = f.readline().rstrip()   643         self.exception_namespaces = value and set(value.split(", ")) or set()   644         f.readline()   645    646     # Generic parsing methods.   647    648     def from_lines(self, f, d):   649    650         "Read lines from 'f', populating 'd'."   651    652         f.readline() # section heading   653         line = f.readline().rstrip()   654         while line:   655             name, value = self._get_fields(line)   656             d[name] = set(value and value.split(", ") or [])   657             line = f.readline().rstrip()   658    659     def usage_from_cache(self, value, mapping):   660    661         """   662         Interpret the given 'value' containing name and usage information,   663         storing the information in the given 'mapping'.   664         """   665    666         local, usage = self._get_fields(value)   667         init_item(mapping, local, list)   668         self._usage_from_cache(mapping[local], usage)   669    670     def _usage_from_cache(self, d, usage):   671    672         # Interpret descriptions of each version of the name.   673    674         all_usages = set()   675         for attrnames in usage.split("; "):   676             if attrnames == "{}":   677                 all_attrnames = ()   678             else:   679                 all_attrnames = decode_usage(attrnames)   680             all_usages.add(all_attrnames)   681    682         d.append(all_usages)   683    684     def _get_fields(self, s, n=2):   685         result = s.split(" ", n-1)   686         if len(result) == n:   687             return result   688         else:   689             return tuple(result) + tuple([""] * (n - len(result)))   690    691 class CacheWritingModule:   692    693     """   694     A mix-in providing cache-writing support, to be combined with BasicModule.   695     """   696    697     def to_cache(self, filename):   698    699         """   700         Write a cached representation of the inspected module to the file having   701         the given 'filename'.   702         """   703    704         f = open(filename, "w")   705         try:   706             print >>f, self.filename   707    708             print >>f   709             print >>f, "imports:"   710             required = list(self.required)   711             required.sort()   712             print >>f, required and ", ".join(required) or "{}"   713             imports = list(self.imports)   714             imports.sort()   715             print >>f, imports and ", ".join(imports) or "{}"   716    717             print >>f   718             print >>f, "deferred:"   719             deferred = map(str, set(self.deferred))   720             deferred.sort()   721             print >>f, ", ".join(deferred)   722    723             print >>f   724             print >>f, "special:"   725             names = self.special.keys()   726             names.sort()   727             for name in names:   728                 ref, paths = self.special[name]   729                 print >>f, name, ref, ", ".join(paths)   730    731             print >>f   732             print >>f, "members:"   733             objects = self.objects.keys()   734             objects.sort()   735             for name in objects:   736                 print >>f, name, self.objects[name]   737    738             print >>f   739             print >>f, "class relationships:"   740             classes = self.classes.keys()   741             classes.sort()   742             for class_ in classes:   743                 bases = self.classes[class_]   744                 if bases:   745                     print >>f, class_, ", ".join(map(str, bases))   746                 else:   747                     print >>f, class_   748    749             self.to_lines(f, "instance attributes:", self.instance_attrs)   750    751             print >>f   752             print >>f, "instance attribute constants:"   753             classes = self.instance_attr_constants.items()   754             classes.sort()   755             for name, attrs in classes:   756                 attrs = attrs.items()   757                 attrs.sort()   758                 for attrname, ref in attrs:   759                     print >>f, name, attrname, ref   760    761             self.to_lines(f, "names used:", self.names_used)   762    763             print >>f   764             print >>f, "name references:"   765             refs = self.name_references.items()   766             refs.sort()   767             for name, ref in refs:   768                 print >>f, name, ref   769    770             print >>f   771             print >>f, "initialised names:"   772             assignments = self.initialised_names.items()   773             assignments.sort()   774             for name, refs in assignments:   775                 versions = refs.items()   776                 versions.sort()   777                 for version, ref in versions:   778                     print >>f, name, version, ref   779    780             print >>f   781             print >>f, "aliased names:"   782             assignments = self.aliased_names.items()   783             assignments.sort()   784             for name, aliases in assignments:   785                 versions = aliases.items()   786                 versions.sort()   787                 for version, alias in versions:   788                     path, original_name, attrnames, number = alias   789                     print >>f, name, version, path, original_name, attrnames or "{}", number is None and "{}" or number   790    791             print >>f   792             print >>f, "function parameters:"   793             functions = self.function_parameters.keys()   794             functions.sort()   795             for function in functions:   796                 parameters = self.function_parameters[function]   797                 if parameters:   798                     print >>f, function, ", ".join(parameters)   799                 else:   800                     print >>f, function, "{}"   801    802             print >>f   803             print >>f, "function default parameters:"   804             functions = self.function_defaults.keys()   805             functions.sort()   806             for function in functions:   807                 parameters = self.function_defaults[function]   808                 if parameters:   809                     print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters])   810                 else:   811                     print >>f, function, "{}"   812    813             print >>f   814             print >>f, "function locals:"   815             functions = self.function_locals.keys()   816             functions.sort()   817             for function in functions:   818                 names = self.function_locals[function].items()   819                 if names:   820                     names.sort()   821                     for name, value in names:   822                         print >>f, function, name, value   823                 else:   824                     print >>f, function, "{}"   825    826             self.to_lines(f, "scope globals:", self.scope_globals)   827    828             print >>f   829             print >>f, "function targets:"   830             functions = self.function_targets.keys()   831             functions.sort()   832             for function in functions:   833                 print >>f, function, self.function_targets[function]   834    835             print >>f   836             print >>f, "function arguments:"   837             functions = self.function_arguments.keys()   838             functions.sort()   839             for function in functions:   840                 print >>f, function, self.function_arguments[function]   841    842             print >>f   843             print >>f, "attribute usage:"   844             units = self.attr_usage.keys()   845             units.sort()   846             for unit in units:   847                 d = self.attr_usage[unit]   848                 self.usage_to_cache(d, f, unit)   849    850             print >>f   851             print >>f, "attribute accesses:"   852             paths = self.attr_accesses.keys()   853             paths.sort()   854             for path in paths:   855                 accesses = list(self.attr_accesses[path])   856                 accesses.sort()   857                 print >>f, path, ", ".join(accesses)   858    859             print >>f   860             print >>f, "constant accesses:"   861             paths = self.const_accesses.keys()   862             paths.sort()   863             for path in paths:   864                 accesses = self.const_accesses[path].items()   865                 accesses.sort()   866                 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses:   867                     print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}"   868    869             print >>f   870             print >>f, "attribute access usage:"   871             paths = self.attr_accessors.keys()   872             paths.sort()   873             for path in paths:   874                 all_accesses = self.attr_accessors[path].items()   875                 all_accesses.sort()   876                 for (name, attrname), accesses in all_accesses:   877                     for positions in accesses:   878                         positions = map(str, positions)   879                         print >>f, path, name, attrname or "{}", ", ".join(positions)   880    881             print >>f   882             print >>f, "attribute access modifiers:"   883             paths = self.attr_access_modifiers.keys()   884             paths.sort()   885             for path in paths:   886                 all_accesses = self.attr_access_modifiers[path].items()   887                 all_accesses.sort()   888                 for (name, attrnames), modifiers in all_accesses:   889                     print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers)   890    891             print >>f   892             print >>f, "constant literals:"   893             paths = self.constants.keys()   894             paths.sort()   895             for path in paths:   896                 constants = []   897                 for (value, value_type, encoding), n in self.constants[path].items():   898                     constants.append((n, value_type, encoding, value))   899                 constants.sort()   900                 for n, value_type, encoding, value in constants:   901                     print >>f, path, value_type, encoding or "{}", repr(value)   902    903             print >>f   904             print >>f, "constant values:"   905             names = self.constant_values.keys()   906             names.sort()   907             for name in names:   908                 value, value_type, encoding = self.constant_values[name]   909                 print >>f, name, value_type, encoding or "{}", repr(value)   910    911             print >>f   912             print >>f, "exception namespaces:"   913             paths = list(self.exception_namespaces)   914             paths.sort()   915             print >>f, ", ".join(paths)   916    917         finally:   918             f.close()   919    920     def to_lines(self, f, heading, d):   921    922         "Write lines to 'f' with the given 'heading', using 'd'."   923    924         print >>f   925         print >>f, heading   926         keys = d.keys()   927         keys.sort()   928         for key in keys:   929             attrs = list(d[key])   930             if attrs:   931                 attrs.sort()   932                 print >>f, key, ", ".join(attrs)   933    934     def usage_to_cache(self, details, f, prefix):   935    936         "Write the given namespace usage details to the cache."   937    938         names = list(details.keys())   939         if names:   940             names.sort()   941             for name in names:   942                 if details[name]:   943    944                     # Produce descriptions for each version of the name.   945    946                     for version in details[name]:   947                         all_usages = []   948                         for usage in version:   949                             all_usages.append(encode_usage(usage))   950    951                         print >>f, "%s %s %s" % (prefix, name, "; ".join(all_usages))   952    953 # vim: tabstop=4 expandtab shiftwidth=4