Lichen

modules.py

25:fa9a7f9773dc
2016-09-04 Paul Boddie Minor comment reformatting.
     1 #!/usr/bin/env python     2      3 """     4 Module abstractions.     5      6 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,     7               2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>     8      9 This program is free software; you can redistribute it and/or modify it under    10 the terms of the GNU General Public License as published by the Free Software    11 Foundation; either version 3 of the License, or (at your option) any later    12 version.    13     14 This program is distributed in the hope that it will be useful, but WITHOUT    15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    16 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    17 details.    18     19 You should have received a copy of the GNU General Public License along with    20 this program.  If not, see <http://www.gnu.org/licenses/>.    21 """    22     23 from common import init_item, remove_items, CommonModule    24 from encoders import decode_modifier_term, encode_modifiers, encode_usage    25 from referencing import decode_reference, Reference    26 from results import ResolvedNameRef    27 import sys    28     29 class BasicModule(CommonModule):    30     31     "The basic module information."    32     33     def __init__(self, name, importer):    34         CommonModule.__init__(self, name, importer)    35     36         # Import details, primarily for cache output.    37     38         self.imports = set()    39         self.required = set()    40     41         # Global name information.    42     43         self.objects = {}    44         self.special = {}    45     46         # Class relationships.    47     48         self.classes = {}    49     50         # Attributes.    51     52         self.class_attrs = {}    53         self.instance_attrs = {}    54         self.instance_attr_constants = {}    55         self.module_attrs = set()    56     57         # Names used and missing.    58     59         self.names_used = {}    60         self.names_missing = {}    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.all_class_attrs[name]   126             del self.importer.all_instance_attrs[name]   127             del self.importer.all_instance_attr_constants[name]   128    129             for name, bases in self.classes.items():   130                 for base in bases:   131    132                     # Get the identity of the class from the reference.   133    134                     base = base.get_origin()   135    136                     try:   137                         self.importer.subclasses[base].remove(name)   138                     except (KeyError, ValueError):   139                         pass   140    141         remove_items(self.importer.all_name_references, self.name_references)   142         remove_items(self.importer.all_initialised_names, self.initialised_names)   143         remove_items(self.importer.all_aliased_names, self.aliased_names)   144         remove_items(self.importer.all_attr_accesses, self.attr_accesses)   145         remove_items(self.importer.all_const_accesses, self.const_accesses)   146         remove_items(self.importer.all_attr_access_modifiers, self.attr_access_modifiers)   147         remove_items(self.importer.all_constants, self.constants)   148         remove_items(self.importer.all_constant_values, self.constant_values)   149    150         # Remove this module's objects from the importer. Objects are   151         # automatically propagated when defined.   152    153         for name, ref in self.objects.items():   154             if not ref.has_kind("<module>"):   155                 del self.importer.objects[name]   156    157     def propagate_attrs(self):   158    159         "Derive attributes from the class and module member details."   160    161         # Initialise class attribute records for all classes.   162    163         for name in self.classes.keys():   164             self.importer.all_class_attrs[name] = self.class_attrs[name] = {}   165    166         # Separate the objects into module and class attributes.   167    168         for name in self.objects.keys():   169             if "." in name:   170                 parent, attrname = name.rsplit(".", 1)   171                 if self.classes.has_key(parent):   172                     self.class_attrs[parent][attrname] = name   173                 elif parent == self.name:   174                     self.module_attrs.add(attrname)   175    176         # Propagate the module attributes.   177    178         self.importer.all_module_attrs[self.name] = self.module_attrs   179    180     def propagate_name_references(self):   181    182         "Propagate name references for the module."   183    184         self.importer.all_name_references.update(self.name_references)   185         self.importer.all_initialised_names.update(self.initialised_names)   186         self.importer.all_aliased_names.update(self.aliased_names)   187    188     def propagate_attr_accesses(self):   189    190         "Propagate attribute accesses for the module."   191    192         self.importer.all_attr_accesses.update(self.attr_accesses)   193         self.importer.all_const_accesses.update(self.const_accesses)   194         self.importer.all_attr_access_modifiers.update(self.attr_access_modifiers)   195    196     def propagate_constants(self):   197    198         "Propagate constant values and aliases for the module."   199    200         self.importer.all_constants.update(self.constants)   201         self.importer.all_constant_values.update(self.constant_values)   202    203         for name in self.classes.keys():   204             self.importer.all_instance_attrs[name] = self.instance_attrs.get(name) or {}   205             self.importer.all_instance_attr_constants[name] = self.instance_attr_constants.get(name) or {}   206    207     # Module-relative naming.   208    209     def is_global(self, name):   210    211         """   212         Return whether 'name' is registered as a global in the current   213         namespace.   214         """   215    216         path = self.get_namespace_path()   217         return name in self.scope_globals.get(path, [])   218    219     def get_global(self, name):   220    221         """   222         Get the global of the given 'name' from this module, returning a   223         reference incorporating the original definition details.   224         """   225    226         path = self.get_global_path(name)   227         return self.objects.get(path)   228    229     # Name definition discovery.   230    231     def get_global_or_builtin(self, name):   232    233         """   234         Return a reference for the given 'name' found in this module or in the   235         __builtins__.   236         """   237    238         return self.get_global(name) or self.get_builtin(name)   239    240     def get_builtin(self, name):   241    242         "Return a reference to the built-in with the given 'name'."   243    244         self.queue_module("__builtins__")   245         return Reference("<depends>", "__builtins__.%s" % name)   246    247     def get_builtin_class(self, name):   248    249         "Return a reference to the actual object providing 'name'."   250    251         # NOTE: This makes assumptions about the __builtins__ structure.   252    253         self.queue_module("__builtins__.%s" % name, True)   254         return Reference("<class>", "__builtins__.%s.%s" % (name, name))   255    256     def get_object(self, path):   257    258         """   259         Get the details of an object with the given 'path'. Where the object   260         cannot be resolved, an unresolved reference is returned.   261         """   262    263         if self.objects.has_key(path):   264             return self.objects[path]   265         else:   266             return Reference("<depends>", path)   267    268     def set_object(self, name, value=None):   269    270         "Set an object with the given 'name' and the given 'value'."   271    272         ref = decode_reference(value, name)   273         multiple = self.objects.has_key(name) and self.objects[name].get_kind() != ref.get_kind()   274         self.importer.objects[name] = self.objects[name] = multiple and ref.as_var() or ref   275    276     def import_name_from_module(self, name, module_name):   277    278         "Import 'name' from the module having the given 'module_name'."   279    280         if module_name != self.name:   281             self.queue_module(module_name)   282         return Reference("<depends>", "%s.%s" % (module_name, name))   283    284     def queue_module(self, name, required=False):   285    286         """   287         Queue the module with the given 'name'. If 'required' is true (it is   288         false by default), indicate that the module is required in the final   289         program.   290         """   291    292         self.importer.queue_module(name, self, required)   293         if required:   294             self.required.add(name)   295         self.imports.add(name)   296    297 class CachedModule(BasicModule):   298    299     "A cached module."   300    301     def __repr__(self):   302         return "CachedModule(%r, %r)" % (self.name, self.importer)   303    304     def to_cache(self, filename):   305    306         "Not actually writing the module back to 'filename'."   307    308         pass   309    310     def from_cache(self, filename):   311    312         """   313         Read a module's details from the file with the given 'filename' as   314         described in the to_cache method of InspectedModule.   315         """   316    317         f = open(filename)   318         try:   319             self.filename = f.readline().rstrip()   320    321             f.readline() # (empty line)   322    323             self._get_imports(f)   324             self._get_members(f)   325             self._get_class_relationships(f)   326             self._get_instance_attrs(f)   327             self._get_instance_attr_constants(f)   328             self.from_lines(f, self.names_used)     # "names used:"   329             self.from_lines(f, self.names_missing)  # "names missing:"   330             self._get_name_references(f)   331             self._get_initialised_names(f)   332             self._get_aliased_names(f)   333             self._get_function_parameters(f)   334             self._get_function_defaults(f)   335             self._get_function_locals(f)   336             self.from_lines(f, self.scope_globals)  # "scope globals:"   337             self._get_function_targets(f)   338             self._get_function_arguments(f)   339             self._get_attribute_usage(f)   340             self._get_attr_accesses(f)   341             self._get_const_accesses(f)   342             self._get_attr_accessors(f)   343             self._get_attr_access_modifiers(f)   344             self._get_constant_literals(f)   345             self._get_constant_values(f)   346    347         finally:   348             f.close()   349    350     def complete(self):   351         self.propagate()   352    353     def _get_imports(self, f):   354         f.readline() # "imports:"   355         line = f.readline().strip()   356         self.required = line != "{}" and set(line.split(", ")) or set()   357         line = f.readline().strip()   358         self.imports = line != "{}" and set(line.split(", ")) or set()   359         f.readline()   360    361         for name in self.required:   362             self.queue_module(name, True)   363         for name in self.imports:   364             self.queue_module(name)   365    366     def _get_members(self, f):   367         f.readline() # "members:"   368         line = f.readline().rstrip()   369         while line:   370             name, ref = line.split(" ", 1)   371             self.set_object(name, ref)   372             line = f.readline().rstrip()   373    374     def _get_class_relationships(self, f):   375         f.readline() # "class relationships:"   376         line = f.readline().rstrip()   377         while line:   378             name, value = self._get_fields(line)   379             values = value and value.split(", ") or []   380             self.importer.classes[name] = self.classes[name] = map(decode_reference, values)   381             self.importer.subclasses[name] = set()   382             line = f.readline().rstrip()   383    384     def _get_instance_attrs(self, f):   385         f.readline() # "instance attributes:"   386         line = f.readline().rstrip()   387         while line:   388             name, value = self._get_fields(line)   389             self.importer.all_instance_attrs[name] = self.instance_attrs[name] = set(value and value.split(", ") or [])   390             line = f.readline().rstrip()   391    392     def _get_instance_attr_constants(self, f):   393         f.readline() # "instance attribute constants:"   394         line = f.readline().rstrip()   395         while line:   396             name, attrname, ref = self._get_fields(line, 3)   397             init_item(self.instance_attr_constants, name, dict)   398             self.instance_attr_constants[name][attrname] = decode_reference(ref)   399             line = f.readline().rstrip()   400    401     def _get_name_references(self, f):   402         f.readline() # "name references:"   403         line = f.readline().rstrip()   404         while line:   405             name, value = self._get_fields(line)   406             self.name_references[name] = value   407             line = f.readline().rstrip()   408    409     def _get_initialised_names(self, f):   410         f.readline() # "initialised names:"   411         line = f.readline().rstrip()   412         while line:   413             name, version, value = self._get_fields(line, 3)   414             init_item(self.initialised_names, name, dict)   415             self.initialised_names[name][int(version)] = decode_reference(value)   416             line = f.readline().rstrip()   417    418     def _get_aliased_names(self, f):   419         f.readline() # "aliased names:"   420         line = f.readline().rstrip()   421         while line:   422             name, version, original_name, attrnames, number = self._get_fields(line, 5)   423             init_item(self.aliased_names, name, dict)   424             if number == "{}": number = None   425             else: number = int(number)   426             self.aliased_names[name][int(version)] = (original_name, attrnames != "{}" and attrnames or None, number)   427             line = f.readline().rstrip()   428    429     def _get_function_parameters(self, f):   430         f.readline() # "function parameters:"   431         line = f.readline().rstrip()   432         while line:   433             function, names = self._get_fields(line)   434             self.importer.function_parameters[function] = \   435                 self.function_parameters[function] = names and names.split(", ") or []   436             line = f.readline().rstrip()   437    438     def _get_function_defaults(self, f):   439         f.readline() # "function default parameters:"   440         line = f.readline().rstrip()   441         while line:   442             function, defaults = self._get_fields(line)   443             self.importer.function_defaults[function] = \   444                 self.function_defaults[function] = l = []   445             if defaults != "{}":   446                 for value in defaults.split(", "):   447                     name, default = value.split("=")   448                     default = decode_reference(default)   449                     l.append((name, default))   450             line = f.readline().rstrip()   451    452     def _get_function_locals(self, f):   453         f.readline() # "function locals:"   454         line = f.readline().rstrip()   455         while line:   456             function, name, value = self._get_fields(line, 3)   457             init_item(self.function_locals, function, dict)   458             if name != "{}":   459                 self.function_locals[function][name] = decode_reference(value)   460             line = f.readline().rstrip()   461    462     def _get_function_targets(self, f):   463         f.readline() # "function targets:"   464         line = f.readline().rstrip()   465         while line:   466             function, n = self._get_fields(line)   467             self.importer.function_targets[function] = \   468                 self.function_targets[function] = int(n)   469             line = f.readline().rstrip()   470    471     def _get_function_arguments(self, f):   472         f.readline() # "function arguments:"   473         line = f.readline().rstrip()   474         while line:   475             function, n = self._get_fields(line)   476             self.importer.function_arguments[function] = \   477                 self.function_arguments[function] = int(n)   478             line = f.readline().rstrip()   479    480     def _get_attribute_usage(self, f):   481         f.readline() # "attribute usage:"   482         line = f.readline().rstrip()   483         while line:   484             unit, value = self._get_fields(line)   485             init_item(self.attr_usage, unit, dict)   486             self.usage_from_cache(value, self.attr_usage[unit])   487             line = f.readline().rstrip()   488    489     def _get_attr_accesses(self, f):   490         f.readline() # "attribute accesses:"   491         line = f.readline().rstrip()   492         while line:   493             name, value = self._get_fields(line)   494             self.attr_accesses[name] = set(value.split(", "))   495             line = f.readline().rstrip()   496    497     def _get_const_accesses(self, f):   498         f.readline() # "constant accesses:"   499         line = f.readline().rstrip()   500         while line:   501             name, original_name, attrnames, objpath, ref, remaining = self._get_fields(line, 6)   502             if attrnames == "{}": attrnames = None   503             init_item(self.const_accesses, name, dict)   504             self.const_accesses[name][(original_name, attrnames)] = (objpath, decode_reference(ref), remaining != "{}" and remaining or "")   505             line = f.readline().rstrip()   506    507     def _get_attr_accessors(self, f):   508         f.readline() # "attribute access usage:"   509         line = f.readline().rstrip()   510         while line:   511             objpath, name, attrname, value = self._get_fields(line, 4)   512             if attrname == "{}": attrname = None   513             access = name, attrname   514             init_item(self.attr_accessors, objpath, dict)   515             init_item(self.attr_accessors[objpath], access, list)   516             positions = map(int, value.split(", "))   517             self.attr_accessors[objpath][access].append(positions)   518             line = f.readline().rstrip()   519    520     def _get_attr_access_modifiers(self, f):   521         f.readline() # "attribute access modifiers:"   522         line = f.readline().rstrip()   523         while line:   524             objpath, name, attrnames, value = self._get_fields(line, 4)   525             if name == "{}": name = None   526             if attrnames == "{}": attrnames = None   527             access = name, attrnames   528             init_item(self.attr_access_modifiers, objpath, dict)   529             init_item(self.attr_access_modifiers[objpath], access, list)   530             modifiers = [decode_modifier_term(s) for s in value]   531             self.attr_access_modifiers[objpath][access] = modifiers   532             line = f.readline().rstrip()   533    534     def _get_constant_literals(self, f):   535         f.readline() # "constant literals:"   536         line = f.readline().rstrip()   537         last_path = None   538         n = None   539         while line:   540             path, constant = self._get_fields(line)   541             if path != last_path:   542                 n = 0   543                 last_path = path   544             else:   545                 n += 1   546             init_item(self.constants, path, dict)   547             self.constants[path][eval(constant)] = n   548             line = f.readline().rstrip()   549    550     def _get_constant_values(self, f):   551         f.readline() # "constant values:"   552         line = f.readline().rstrip()   553         while line:   554             name, value_type, value = self._get_fields(line, 3)   555             self.constant_values[name] = eval(value), value_type   556             line = f.readline().rstrip()   557    558     # Generic parsing methods.   559    560     def from_lines(self, f, d):   561    562         "Read lines from 'f', populating 'd'."   563    564         f.readline() # section heading   565         line = f.readline().rstrip()   566         while line:   567             name, value = self._get_fields(line)   568             d[name] = set(value and value.split(", ") or [])   569             line = f.readline().rstrip()   570    571     def usage_from_cache(self, value, mapping):   572    573         """   574         Interpret the given 'value' containing name and usage information,   575         storing the information in the given 'mapping'.   576         """   577    578         local, usage = self._get_fields(value)   579         init_item(mapping, local, list)   580         self._usage_from_cache(mapping[local], usage)   581    582     def _usage_from_cache(self, d, usage):   583    584         # Interpret descriptions of each version of the name.   585    586         all_usages = set()   587         for attrnames in usage.split("; "):   588             if attrnames == "{}":   589                 all_attrnames = ()   590             else:   591                 # Decode attribute details for each usage description.   592    593                 all_attrnames = set()   594                 for attrname_str in attrnames.split(", "):   595                     all_attrnames.add(attrname_str)   596    597                 all_attrnames = list(all_attrnames)   598                 all_attrnames.sort()   599    600             all_usages.add(tuple(all_attrnames))   601    602         d.append(all_usages)   603    604     def _get_fields(self, s, n=2):   605         result = s.split(" ", n-1)   606         if len(result) == n:   607             return result   608         else:   609             return tuple(result) + tuple([""] * (n - len(result)))   610    611 class CacheWritingModule:   612    613     """   614     A mix-in providing cache-writing support, to be combined with BasicModule.   615     """   616    617     def to_cache(self, filename):   618    619         """   620         Write a cached representation of the inspected module with the following   621         format to the file having the given 'filename':   622    623         filename   624         (empty line)   625         "imports:"   626         required module names   627         possibly required module names   628         "members:"   629         zero or more: qualified name " " reference   630         (empty line)   631         "class relationships:"   632         zero or more: qualified class name " " base class references   633         (empty line)   634         "instance attributes:"   635         zero or more: qualified class name " " instance attribute names   636         (empty line)   637         "instance attribute constants:"   638         zero or more: qualified class name " " attribute name " " reference   639         (empty line)   640         "names used:"   641         zero or more: qualified class/function/module name " " names   642         (empty line)   643         "names missing:"   644         zero or more: qualified class/function/module name " " names   645         (empty line)   646         "name references:"   647         zero or more: qualified name " " reference   648         (empty line)   649         "initialised names:"   650         zero or more: qualified name " " definition version " " reference   651         (empty line)   652         "aliased names:"   653         zero or more: qualified name " " definition version " " original name " " attribute names " " access number   654         (empty line)   655         "function parameters:"   656         zero or more: qualified function name " " parameter names   657         (empty line)   658         "function default parameters:"   659         zero or more: qualified function name " " parameter names with defaults   660         (empty line)   661         "function locals:"   662         zero or more: qualified function name " " local variable name " " reference   663         (empty line)   664         "scope globals:"   665         zero or more: qualified function name " " global variable names   666         (empty line)   667         "function targets:"   668         zero or more: qualified function name " " maximum number of targets allocated   669         (empty line)   670         "function arguments:"   671         zero or more: qualified function name " " maximum number of arguments allocated   672         (empty line)   673         "attribute usage:"   674         zero or more: qualified scope name " " local/global/qualified variable name " " usages   675         (empty line)   676         "attribute accesses:"   677         zero or more: qualified scope name " " attribute-chains   678         (empty line)   679         "constant accesses:"   680         zero or more: qualified function name " " attribute-chain " " reference " " remaining attribute-chain   681         (empty line)   682         "attribute access usage:"   683         zero or more: qualified function name " " local/global variable name " " attribute name " " definition versions   684         (empty line)   685         "attribute access modifiers:"   686         zero or more: qualified function name " " local/global variable name " " attribute name " " access modifiers   687         "constant literals:"   688         zero or more: qualified scope name " " constant literal   689         "constant values:"   690         zero or more: qualified name " " value type " " constant literal   691    692         All collections of names are separated by ", " characters.   693    694         References can be "<var>", a module name, or one of "<class>" or   695         "<function>" followed optionally by a ":" character and a qualified   696         name.   697    698         Parameter names with defaults are separated by ", " characters, with   699         each name followed by "=" and then followed by a reference. If "{}" is   700         indicated, no defaults are defined for the function. Similarly, function   701         locals may be indicated as "{}" meaning that there are no locals.   702    703         All usages (attribute usage sets) are separated by "; " characters, with   704         the special string "{}" representing an empty set.   705    706         Each usage is a collection of names separated by ", " characters, with   707         assigned attribute names suffixed with a "*" character.   708    709         Each attribute-chain expression is a dot-separated chain of attribute   710         names, with assignments suffixed with a "*" character.   711    712         Definition versions are separated by ", " characters and indicate the   713         name definition version associated with the access.   714    715         Access modifiers are separated by ", " characters and indicate features   716         of each access, with multiple accesses described on a single line.   717         """   718    719         f = open(filename, "w")   720         try:   721             print >>f, self.filename   722    723             print >>f   724             print >>f, "imports:"   725             required = list(self.required)   726             required.sort()   727             print >>f, required and ", ".join(required) or "{}"   728             imports = list(self.imports)   729             imports.sort()   730             print >>f, imports and ", ".join(imports) or "{}"   731    732             print >>f   733             print >>f, "members:"   734             objects = self.objects.keys()   735             objects.sort()   736             for name in objects:   737                 print >>f, name, self.objects[name]   738    739             print >>f   740             print >>f, "class relationships:"   741             classes = self.classes.keys()   742             classes.sort()   743             for class_ in classes:   744                 bases = self.classes[class_]   745                 if bases:   746                     print >>f, class_, ", ".join(map(str, bases))   747                 else:   748                     print >>f, class_   749    750             self.to_lines(f, "instance attributes:", self.instance_attrs)   751    752             print >>f   753             print >>f, "instance attribute constants:"   754             classes = self.instance_attr_constants.items()   755             classes.sort()   756             for name, attrs in classes:   757                 attrs = attrs.items()   758                 attrs.sort()   759                 for attrname, ref in attrs:   760                     print >>f, name, attrname, ref   761    762             self.to_lines(f, "names used:", self.names_used)   763             self.to_lines(f, "names missing:", self.names_missing)   764    765             print >>f   766             print >>f, "name references:"   767             refs = self.name_references.items()   768             refs.sort()   769             for name, ref in refs:   770                 print >>f, name, ref   771    772             print >>f   773             print >>f, "initialised names:"   774             assignments = self.initialised_names.items()   775             assignments.sort()   776             for name, refs in assignments:   777                 versions = refs.items()   778                 versions.sort()   779                 for version, ref in versions:   780                     print >>f, name, version, ref   781    782             print >>f   783             print >>f, "aliased names:"   784             assignments = self.aliased_names.items()   785             assignments.sort()   786             for name, aliases in assignments:   787                 versions = aliases.items()   788                 versions.sort()   789                 for version, alias in versions:   790                     original_name, attrnames, number = alias   791                     print >>f, name, version, original_name, attrnames or "{}", number is None and "{}" or number   792    793             print >>f   794             print >>f, "function parameters:"   795             functions = self.function_parameters.keys()   796             functions.sort()   797             for function in functions:   798                 print >>f, function, ", ".join(self.function_parameters[function])   799    800             print >>f   801             print >>f, "function default parameters:"   802             functions = self.function_defaults.keys()   803             functions.sort()   804             for function in functions:   805                 parameters = self.function_defaults[function]   806                 if parameters:   807                     print >>f, function, ", ".join([("%s=%s" % (name, default)) for (name, default) in parameters])   808                 else:   809                     print >>f, function, "{}"   810    811             print >>f   812             print >>f, "function locals:"   813             functions = self.function_locals.keys()   814             functions.sort()   815             for function in functions:   816                 names = self.function_locals[function].items()   817                 if names:   818                     names.sort()   819                     for name, value in names:   820                         print >>f, function, name, value   821                 else:   822                     print >>f, function, "{}"   823    824             self.to_lines(f, "scope globals:", self.scope_globals)   825    826             print >>f   827             print >>f, "function targets:"   828             functions = self.function_targets.keys()   829             functions.sort()   830             for function in functions:   831                 print >>f, function, self.function_targets[function]   832    833             print >>f   834             print >>f, "function arguments:"   835             functions = self.function_arguments.keys()   836             functions.sort()   837             for function in functions:   838                 print >>f, function, self.function_arguments[function]   839    840             print >>f   841             print >>f, "attribute usage:"   842             units = self.attr_usage.keys()   843             units.sort()   844             for unit in units:   845                 d = self.attr_usage[unit]   846                 self.usage_to_cache(d, f, unit)   847    848             print >>f   849             print >>f, "attribute accesses:"   850             paths = self.attr_accesses.keys()   851             paths.sort()   852             for path in paths:   853                 accesses = list(self.attr_accesses[path])   854                 accesses.sort()   855                 print >>f, path, ", ".join(accesses)   856    857             print >>f   858             print >>f, "constant accesses:"   859             paths = self.const_accesses.keys()   860             paths.sort()   861             for path in paths:   862                 accesses = self.const_accesses[path].items()   863                 accesses.sort()   864                 for (original_name, attrnames), (objpath, ref, remaining_attrnames) in accesses:   865                     print >>f, path, original_name, attrnames, objpath, ref, remaining_attrnames or "{}"   866    867             print >>f   868             print >>f, "attribute access usage:"   869             paths = self.attr_accessors.keys()   870             paths.sort()   871             for path in paths:   872                 all_accesses = self.attr_accessors[path].items()   873                 all_accesses.sort()   874                 for (name, attrname), accesses in all_accesses:   875                     for positions in accesses:   876                         positions = map(str, positions)   877                         print >>f, path, name, attrname or "{}", ", ".join(positions)   878    879             print >>f   880             print >>f, "attribute access modifiers:"   881             paths = self.attr_access_modifiers.keys()   882             paths.sort()   883             for path in paths:   884                 all_accesses = self.attr_access_modifiers[path].items()   885                 all_accesses.sort()   886                 for (name, attrnames), modifiers in all_accesses:   887                     print >>f, path, name or "{}", attrnames or "{}", encode_modifiers(modifiers)   888    889             print >>f   890             print >>f, "constant literals:"   891             paths = self.constants.keys()   892             paths.sort()   893             for path in paths:   894                 constants = [(v, k) for (k, v) in self.constants[path].items()]   895                 constants.sort()   896                 for n, constant in constants:   897                     print >>f, path, repr(constant)   898    899             print >>f   900             print >>f, "constant values:"   901             names = self.constant_values.keys()   902             names.sort()   903             for name in names:   904                 value, value_type = self.constant_values[name]   905                 print >>f, name, value_type, repr(value)   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