Lichen

inspector.py

84:3b3bb6a8a762
2016-10-07 Paul Boddie Produce an error when attempting to access an attribute on an unrecognised name.
     1 #!/usr/bin/env python     2      3 """     4 Inspect and obtain module structure.     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 branching import BranchTracker    24 from common import get_argnames, init_item, predefined_constants    25 from modules import BasicModule, CacheWritingModule, InspectionNaming    26 from errors import InspectError    27 from referencing import Reference    28 from resolving import NameResolving    29 from results import AccessRef, InstanceRef, InvocationRef, LiteralSequenceRef, \    30                     LocalNameRef, NameRef, ResolvedNameRef    31 import compiler    32 import sys    33     34 class InspectedModule(BasicModule, CacheWritingModule, NameResolving, InspectionNaming):    35     36     "A module inspector."    37     38     def __init__(self, name, importer):    39     40         "Initialise the module with basic details."    41     42         BasicModule.__init__(self, name, importer)    43     44         self.in_class = False    45         self.in_conditional = False    46         self.global_attr_accesses = {}    47     48         # Usage tracking.    49     50         self.trackers = []    51         self.attr_accessor_branches = {}    52     53     def __repr__(self):    54         return "InspectedModule(%r, %r)" % (self.name, self.importer)    55     56     # Principal methods.    57     58     def parse(self, filename):    59     60         "Parse the file having the given 'filename'."    61     62         self.parse_file(filename)    63     64         # Inspect the module.    65     66         self.start_tracking_in_module()    67     68         # Detect and record imports and globals declared in the module.    69     70         self.assign_general_local("__name__", self.get_constant("str", self.name))    71         self.assign_general_local("__file__", self.get_constant("str", filename))    72         self.process_structure(self.astnode)    73     74         # Set the class of the module after the definition has occurred.    75     76         ref = self.get_builtin("object")    77         self.set_name("__class__", ref)    78     79         # Get module-level attribute usage details.    80     81         self.stop_tracking_in_module()    82     83         # Collect external name references.    84     85         self.collect_names()    86     87     def complete(self):    88     89         "Complete the module inspection."    90     91         # Resolve names not definitively mapped to objects.    92     93         self.resolve()    94     95         # Define the invocation requirements in each namespace.    96     97         self.set_invocation_usage()    98     99         # Propagate to the importer information needed in subsequent activities.   100    101         self.propagate()   102    103     # Accessory methods.   104    105     def collect_names(self):   106    107         "Collect the names used by each scope."   108    109         for path in self.names_used.keys():   110             self.collect_names_for_path(path)   111    112     def collect_names_for_path(self, path):   113    114         """   115         Collect the names used by the given 'path'. These are propagated to the   116         importer in advance of any dependency resolution.   117         """   118    119         names = self.names_used.get(path)   120         if not names:   121             return   122    123         in_function = self.function_locals.has_key(path)   124    125         for name in names:   126             if name in predefined_constants or in_function and name in self.function_locals[path]:   127                 continue   128    129             # Find local definitions (within dynamic namespaces).   130    131             key = "%s.%s" % (path, name)   132             ref = self.get_resolved_object(key)   133             if ref:   134                 self.set_name_reference(key, ref)   135                 continue   136    137             # Find global or known built-in definitions.   138    139             ref = self.get_resolved_global_or_builtin(name)   140             if ref:   141                 self.set_name_reference(key, ref)   142                 continue   143    144             # Find presumed built-in definitions.   145    146             ref = self.get_builtin(name)   147             self.set_name_reference(key, ref)   148    149     def set_name_reference(self, path, ref):   150    151         "Map the given name 'path' to 'ref'."   152    153         self.importer.all_name_references[path] = self.name_references[path] = ref   154    155     def get_resolved_global_or_builtin(self, name):   156    157         "Return the resolved global or built-in object with the given 'name'."   158    159         # In some circumstances, the name is neither global nor recognised by   160         # the importer. It is then assumed to be a general built-in.   161    162         return self.get_global(name) or \   163                self.importer.get_object("__builtins__.%s" % name)   164    165     # Module structure traversal.   166    167     def process_structure_node(self, n):   168    169         "Process the individual node 'n'."   170    171         # Module global detection.   172    173         if isinstance(n, compiler.ast.Global):   174             self.process_global_node(n)   175    176         # Module import declarations.   177    178         elif isinstance(n, compiler.ast.From):   179             self.process_from_node(n)   180    181         elif isinstance(n, compiler.ast.Import):   182             self.process_import_node(n)   183    184         # Nodes using operator module functions.   185    186         elif isinstance(n, compiler.ast.Operator):   187             return self.process_operator_node(n)   188    189         elif isinstance(n, compiler.ast.AugAssign):   190             self.process_augassign_node(n)   191    192         elif isinstance(n, compiler.ast.Compare):   193             return self.process_compare_node(n)   194    195         elif isinstance(n, compiler.ast.Slice):   196             return self.process_slice_node(n)   197    198         elif isinstance(n, compiler.ast.Sliceobj):   199             return self.process_sliceobj_node(n)   200    201         elif isinstance(n, compiler.ast.Subscript):   202             return self.process_subscript_node(n)   203    204         # Namespaces within modules.   205    206         elif isinstance(n, compiler.ast.Class):   207             self.process_class_node(n)   208    209         elif isinstance(n, compiler.ast.Function):   210             self.process_function_node(n, n.name)   211    212         elif isinstance(n, compiler.ast.Lambda):   213             return self.process_lambda_node(n)   214    215         # Assignments.   216    217         elif isinstance(n, compiler.ast.Assign):   218    219             # Handle each assignment node.   220    221             for node in n.nodes:   222                 self.process_assignment_node(node, n.expr)   223    224         # Assignments within non-Assign nodes.   225    226         elif isinstance(n, compiler.ast.AssName):   227             self.process_assignment_node(n, None)   228    229         elif isinstance(n, compiler.ast.AssAttr):   230             self.process_attribute_access(n)   231    232         # Accesses.   233    234         elif isinstance(n, compiler.ast.Getattr):   235             return self.process_attribute_access(n)   236    237         # Name recording for later testing.   238    239         elif isinstance(n, compiler.ast.Name):   240             return self.process_name_node(n)   241    242         # Conditional statement tracking.   243    244         elif isinstance(n, compiler.ast.For):   245             self.process_for_node(n)   246    247         elif isinstance(n, compiler.ast.While):   248             self.process_while_node(n)   249    250         elif isinstance(n, compiler.ast.If):   251             self.process_if_node(n)   252    253         elif isinstance(n, (compiler.ast.And, compiler.ast.Or)):   254             return self.process_logical_node(n)   255    256         # Exception control-flow tracking.   257    258         elif isinstance(n, compiler.ast.TryExcept):   259             self.process_try_node(n)   260    261         elif isinstance(n, compiler.ast.TryFinally):   262             self.process_try_finally_node(n)   263    264         # Control-flow modification statements.   265    266         elif isinstance(n, compiler.ast.Break):   267             self.trackers[-1].suspend_broken_branch()   268    269         elif isinstance(n, compiler.ast.Continue):   270             self.trackers[-1].suspend_continuing_branch()   271    272         elif isinstance(n, compiler.ast.Raise):   273             self.process_structure(n)   274             self.trackers[-1].abandon_branch()   275    276         elif isinstance(n, compiler.ast.Return):   277             self.process_structure(n)   278             self.trackers[-1].abandon_returning_branch()   279    280         # Invocations.   281    282         elif isinstance(n, compiler.ast.CallFunc):   283             return self.process_invocation_node(n)   284    285         # Constant usage.   286    287         elif isinstance(n, compiler.ast.Const):   288             return self.get_literal_instance(n, n.value.__class__.__name__)   289    290         elif isinstance(n, compiler.ast.Dict):   291             return self.get_literal_instance(n, "dict")   292    293         elif isinstance(n, compiler.ast.List):   294             return self.get_literal_instance(n, "list")   295    296         elif isinstance(n, compiler.ast.Tuple):   297             return self.get_literal_instance(n, "tuple")   298    299         # Unsupported nodes.   300    301         elif isinstance(n, compiler.ast.GenExpr):   302             raise InspectError("Generator expressions are not supported.", self.get_namespace_path(), n)   303    304         elif isinstance(n, compiler.ast.IfExp):   305             raise InspectError("If-else expressions are not supported.", self.get_namespace_path(), n)   306    307         elif isinstance(n, compiler.ast.ListComp):   308             raise InspectError("List comprehensions are not supported.", self.get_namespace_path(), n)   309    310         # All other nodes are processed depth-first.   311    312         else:   313             self.process_structure(n)   314    315         # By default, no expression details are returned.   316    317         return None   318    319     # Specific node handling.   320    321     def process_assignment_node(self, n, expr):   322    323         "Process the individual node 'n' to be assigned the contents of 'expr'."   324    325         # Names and attributes are assigned the entire expression.   326    327         if isinstance(n, compiler.ast.AssName):   328             if n.name == "self":   329                 raise InspectError("Redefinition of self is not allowed.", self.get_namespace_path(), n)   330    331             name_ref = expr and self.process_structure_node(expr)   332    333             # Name assignments populate either function namespaces or the   334             # general namespace hierarchy.   335    336             self.assign_general_local(n.name, name_ref)   337    338             # Record usage of the name.   339    340             self.record_name(n.name)   341    342         elif isinstance(n, compiler.ast.AssAttr):   343             if expr: self.process_structure_node(expr)   344             self.process_attribute_access(n)   345    346         # Lists and tuples are matched against the expression and their   347         # items assigned to expression items.   348    349         elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)):   350             self.process_assignment_node_items(n, expr)   351    352         # Slices and subscripts are permitted within assignment nodes.   353    354         elif isinstance(n, compiler.ast.Slice):   355             self.process_slice_node(n, expr)   356    357         elif isinstance(n, compiler.ast.Subscript):   358             self.process_subscript_node(n, expr)   359    360     def process_attribute_access(self, n):   361    362         "Process the given attribute access node 'n'."   363    364         # Obtain any completed chain and return the reference to it.   365    366         name_ref = self.process_attribute_chain(n)   367         if self.have_access_expression(n):   368             return name_ref   369    370         # Where the start of the chain of attributes has been reached, determine   371         # the complete access.   372    373         # Given a non-access node, this chain can be handled in its entirety,   374         # either being name-based and thus an access rooted on a name, or being   375         # based on some other node and thus an anonymous access of some kind.   376    377         path = self.get_namespace_path()   378    379         # Start with the the full attribute chain.   380    381         remaining = self.attrs   382         attrnames = ".".join(remaining)   383    384         # If the accessor cannot be identified, or where attributes   385         # remain in an attribute chain, record the anonymous accesses.   386    387         if not isinstance(name_ref, NameRef): # includes ResolvedNameRef   388    389             assignment = isinstance(n, compiler.ast.AssAttr)   390    391             init_item(self.attr_accesses, path, set)   392             self.attr_accesses[path].add(attrnames)   393    394             self.record_access_details(None, attrnames, assignment)   395             del self.attrs[0]   396             return   397    398         # Name-based accesses will handle the first attribute in a   399         # chain.   400    401         else:   402             attrname = remaining[0]   403    404             # Attribute assignments are used to identify instance attributes.   405    406             if isinstance(n, compiler.ast.AssAttr) and \   407                 self.in_class and self.in_function and n.expr.name == "self":   408    409                 self.set_instance_attr(attrname)   410    411             # Record attribute usage using any name local to this namespace,   412             # if assigned in the namespace, or using an external name   413             # (presently just globals within classes).   414    415             name = self.get_name_for_tracking(name_ref.name, name_ref.final())   416             tracker = self.trackers[-1]   417    418             immediate_access = len(self.attrs) == 1   419             assignment = immediate_access and isinstance(n, compiler.ast.AssAttr)   420    421             del self.attrs[0]   422    423             # Record global-based chains for subsequent resolution.   424    425             is_global = self.in_function and not self.function_locals[path].has_key(name) or \   426                         not self.in_function   427    428             if is_global:   429                 self.record_global_access_details(name, attrnames)   430    431             # Make sure the name is being tracked: global names will not   432             # already be initialised in a branch and must be added   433             # explicitly.   434    435             if not tracker.have_name(name):   436                 tracker.assign_names([name])   437                 if self.in_function:   438                     self.scope_globals[path].add(name)   439    440             # Record attribute usage in the tracker, and record the branch   441             # information for the access.   442    443             branches = tracker.use_attribute(name, attrname)   444    445             if not branches:   446                 raise InspectError("Name %s is accessed using %s before an assignment." % (   447                     name, attrname), path, n)   448    449             self.record_branches_for_access(branches, name, attrnames)   450             access_number = self.record_access_details(name, attrnames, assignment)   451             return AccessRef(name, attrnames, access_number)   452    453     def process_class_node(self, n):   454    455         "Process the given class node 'n'."   456    457         path = self.get_namespace_path()   458    459         # To avoid notions of class "versions" where the same definition   460         # might be parameterised with different state and be referenced   461         # elsewhere (as base classes, for example), classes in functions or   462         # conditions are forbidden.   463    464         if self.in_function or self.in_conditional:   465             print >>sys.stderr, "In %s, class %s in function or conditional statement ignored." % (   466                 path, n.name)   467             return   468    469         # Resolve base classes.   470    471         bases = []   472    473         for base in n.bases:   474             base_class = self.get_class(base)   475    476             if not base_class:   477                 print >>sys.stderr, "In %s, class %s has unidentifiable base class: %s" % (   478                     path, n.name, base)   479                 return   480             else:   481                 bases.append(base_class)   482    483         # Record bases for the class and retain the class name.   484    485         class_name = self.get_object_path(n.name)   486    487         if not bases and class_name != "__builtins__.core.object":   488             ref = self.get_object("__builtins__.object")   489             bases.append(ref)   490    491         self.importer.classes[class_name] = self.classes[class_name] = bases   492         self.importer.subclasses[class_name] = set()   493         self.scope_globals[class_name] = set()   494    495         # Set the definition before entering the namespace rather than   496         # afterwards because methods may reference it. In normal Python,   497         # a class is not accessible until the definition is complete, but   498         # methods can generally reference it since upon being called the   499         # class will already exist.   500    501         self.set_definition(n.name, "<class>")   502    503         in_class = self.in_class   504         self.in_class = class_name   505         self.set_instance_attr("__class__", Reference("<class>", class_name))   506         self.enter_namespace(n.name)   507         self.set_name("__fn__") # special instantiator attribute   508         self.set_name("__args__") # special instantiator attribute   509         self.assign_general_local("__name__", self.get_constant("str", class_name))   510         self.process_structure_node(n.code)   511         self.exit_namespace()   512         self.in_class = in_class   513    514     def process_from_node(self, n):   515    516         "Process the given node 'n', importing from another module."   517    518         path = self.get_namespace_path()   519    520         module_name, names = self.get_module_name(n)   521         if module_name == self.name:   522             raise InspectError("Cannot import from the current module.", path, n)   523    524         self.queue_module(module_name)   525    526         # Attempt to obtain the referenced objects.   527    528         for name, alias in n.names:   529             if name == "*":   530                 raise InspectError("Only explicitly specified names can be imported from modules.", path, n)   531    532             # Explicit names.   533    534             ref = self.import_name_from_module(name, module_name)   535             value = ResolvedNameRef(alias or name, ref)   536             self.set_general_local(alias or name, value)   537    538     def process_function_node(self, n, name):   539    540         """   541         Process the given function or lambda node 'n' with the given 'name'.   542         """   543    544         is_lambda = isinstance(n, compiler.ast.Lambda)   545    546         # Where a function is declared conditionally, use a separate name for   547         # the definition, and assign the definition to the stated name.   548    549         if (self.in_conditional or self.in_function) and not is_lambda:   550             original_name = name   551             name = self.get_lambda_name()   552         else:   553             original_name = None   554    555         # Initialise argument and local records.   556    557         function_name = self.get_object_path(name)   558         argnames = get_argnames(n.argnames)   559         is_method = self.in_class and not self.in_function   560    561         # Remove explicit "self" from method parameters.   562    563         if is_method and argnames and argnames[0] == "self":   564             del argnames[0]   565    566         # Copy and propagate the parameters.   567    568         self.importer.function_parameters[function_name] = \   569         self.function_parameters[function_name] = argnames[:]   570    571         # Define all arguments/parameters in the local namespace.   572    573         locals = self.function_locals[function_name] = {}   574    575         # Insert "self" into method locals.   576    577         if is_method:   578             argnames.insert(0, "self")   579    580         # Define "self" in terms of the class if in a method.   581         # This does not diminish the need for type-narrowing in the deducer.   582    583         if argnames:   584             if self.in_class and not self.in_function and argnames[0] == "self":   585                 locals[argnames[0]] = Reference("<instance>", self.in_class)   586             else:   587                 locals[argnames[0]] = Reference("<var>")   588    589         for argname in argnames[1:]:   590             locals[argname] = Reference("<var>")   591    592         globals = self.scope_globals[function_name] = set()   593    594         # Process the defaults.   595    596         defaults = self.importer.function_defaults[function_name] = \   597                    self.function_defaults[function_name] = []   598    599         for argname, default in compiler.ast.get_defaults(n):   600             if default:   601    602                 # Obtain any reference for the default.   603    604                 name_ref = self.process_structure_node(default)   605                 defaults.append((argname, name_ref.is_name() and name_ref.reference() or Reference("<var>")))   606    607         # Reset conditional tracking to focus on the function contents.   608    609         in_conditional = self.in_conditional   610         self.in_conditional = False   611    612         in_function = self.in_function   613         self.in_function = function_name   614    615         self.enter_namespace(name)   616    617         # Track attribute usage within the namespace.   618    619         path = self.get_namespace_path()   620    621         self.start_tracking(locals)   622         self.process_structure_node(n.code)   623         self.stop_tracking()   624    625         # Exit to the parent.   626    627         self.exit_namespace()   628    629         # Update flags.   630    631         self.in_function = in_function   632         self.in_conditional = in_conditional   633    634         # Define the function using the appropriate name.   635    636         self.set_definition(name, "<function>")   637    638         # Where a function is set conditionally, assign the name.   639    640         if original_name:   641             self.process_assignment_for_function(original_name, name)   642    643     def process_global_node(self, n):   644    645         """   646         Process the given "global" node 'n'.   647         """   648    649         path = self.get_namespace_path()   650    651         if path != self.name:   652             self.scope_globals[path].update(n.names)   653    654     def process_if_node(self, n):   655    656         """   657         Process the given "if" node 'n'.   658         """   659    660         tracker = self.trackers[-1]   661         tracker.new_branchpoint()   662    663         for test, body in n.tests:   664             self.process_structure_node(test)   665    666             tracker.new_branch()   667    668             in_conditional = self.in_conditional   669             self.in_conditional = True   670             self.process_structure_node(body)   671             self.in_conditional = in_conditional   672    673             tracker.shelve_branch()   674    675         # Maintain a branch for the else clause.   676    677         tracker.new_branch()   678         if n.else_:   679             self.process_structure_node(n.else_)   680         tracker.shelve_branch()   681    682         tracker.merge_branches()   683    684     def process_import_node(self, n):   685    686         "Process the given import node 'n'."   687    688         path = self.get_namespace_path()   689    690         # Load the mentioned module.   691    692         for name, alias in n.names:   693             if name == self.name:   694                 raise InspectError("Cannot import the current module.", path, n)   695    696             self.set_module(alias or name.split(".")[-1], name)   697             self.queue_module(name, True)   698    699     def process_invocation_node(self, n):   700    701         "Process the given invocation node 'n'."   702    703         path = self.get_namespace_path()   704    705         self.allocate_arguments(path, n.args)   706    707         try:   708             # Process the expression, obtaining any identified reference.   709    710             name_ref = self.process_structure_node(n.node)   711    712             # Process the arguments.   713    714             for arg in n.args:   715                 self.process_structure_node(arg)   716    717             # Detect class invocations.   718    719             if isinstance(name_ref, ResolvedNameRef) and name_ref.has_kind("<class>"):   720                 return InstanceRef(name_ref.reference().instance_of())   721    722             elif isinstance(name_ref, NameRef):   723                 return InvocationRef(name_ref)   724    725             return None   726    727         finally:   728             self.deallocate_arguments(path, n.args)   729    730     def process_lambda_node(self, n):   731    732         "Process the given lambda node 'n'."   733    734         name = self.get_lambda_name()   735         self.process_function_node(n, name)   736    737         origin = self.get_object_path(name)   738         return ResolvedNameRef(name, Reference("<function>", origin))   739    740     def process_logical_node(self, n):   741    742         "Process the given operator node 'n'."   743    744         self.process_operator_chain(n.nodes, self.process_structure_node)   745    746     def process_name_node(self, n):   747    748         "Process the given name node 'n'."   749    750         path = self.get_namespace_path()   751    752         # Special names.   753    754         if n.name.startswith("$"):   755             value = self.get_special(n.name)   756             if value:   757                 return value   758    759         # Special case for operator functions introduced through code   760         # transformations.   761    762         if n.name.startswith("$op"):   763    764             # Obtain the location of the actual function defined in the operator   765             # package.   766    767             op = n.name[len("$op"):]   768    769             # Attempt to get a reference.   770    771             ref = self.import_name_from_module(op, "operator")   772             self.add_deferred(ref)   773    774             # Record the imported name and provide the resolved name reference.   775    776             value = ResolvedNameRef(n.name, ref)   777             self.set_special(n.name, value)   778             return value   779    780         # Test for self usage, which is only allowed in methods.   781    782         if n.name == "self" and not (self.in_function and self.in_class):   783             raise InspectError("Use of self is only allowed in methods.", path, n)   784    785         # Record usage of the name.   786    787         self.record_name(n.name)   788    789         # Search for unknown names in non-function scopes immediately.   790         # External names in functions are resolved later.   791    792         ref = self.find_name(n.name)   793         if ref:   794             return ResolvedNameRef(n.name, ref)   795    796         # Explicitly-declared global names.   797    798         elif self.in_function and n.name in self.scope_globals[path]:   799             return NameRef(n.name)   800    801         # Examine other names.   802    803         else:   804             tracker = self.trackers[-1]   805    806             # Check local names.   807    808             branches = tracker.tracking_name(n.name)   809    810             # Local name.   811    812             if branches:   813                 self.record_branches_for_access(branches, n.name, None)   814                 access_number = self.record_access_details(n.name, None, False)   815                 return LocalNameRef(n.name, access_number)   816    817             # Possible global or built-in name.   818    819             else:   820                 return NameRef(n.name)   821    822     def process_operator_chain(self, nodes, fn):   823    824         """   825         Process the given chain of 'nodes', applying 'fn' to each node or item.   826         Each node starts a new conditional region, effectively making a deeply-   827         nested collection of if-like statements.   828         """   829    830         tracker = self.trackers[-1]   831    832         for item in nodes:   833             tracker.new_branchpoint()   834             tracker.new_branch()   835             fn(item)   836    837         for item in nodes[:-1]:   838             tracker.shelve_branch()   839             tracker.new_branch()   840             tracker.shelve_branch()   841             tracker.merge_branches()   842    843         tracker.shelve_branch()   844         tracker.merge_branches()   845    846     def process_try_node(self, n):   847    848         """   849         Process the given "try...except" node 'n'.   850         """   851    852         tracker = self.trackers[-1]   853         tracker.new_branchpoint()   854    855         self.process_structure_node(n.body)   856    857         for name, var, handler in n.handlers:   858             if name is not None:   859                 self.process_structure_node(name)   860    861             # Any abandoned branches from the body can now be resumed in a new   862             # branch.   863    864             tracker.resume_abandoned_branches()   865    866             # Establish the local for the handler.   867    868             if var is not None:   869                 self.process_structure_node(var)   870             if handler is not None:   871                 self.process_structure_node(handler)   872    873             tracker.shelve_branch()   874    875         # The else clause maintains the usage from the body but without the   876         # abandoned branches since they would never lead to the else clause   877         # being executed.   878    879         if n.else_:   880             tracker.new_branch()   881             self.process_structure_node(n.else_)   882             tracker.shelve_branch()   883    884         # Without an else clause, a null branch propagates the successful   885         # outcome.   886    887         else:   888             tracker.new_branch()   889             tracker.shelve_branch()   890    891         tracker.merge_branches()   892    893     def process_try_finally_node(self, n):   894    895         """   896         Process the given "try...finally" node 'n'.   897         """   898    899         tracker = self.trackers[-1]   900         self.process_structure_node(n.body)   901    902         # Any abandoned branches from the body can now be resumed.   903    904         branches = tracker.resume_all_abandoned_branches()   905         self.process_structure_node(n.final)   906    907         # At the end of the finally clause, abandoned branches are discarded.   908    909         tracker.restore_active_branches(branches)   910    911     def process_while_node(self, n):   912    913         "Process the given while node 'n'."   914    915         tracker = self.trackers[-1]   916         tracker.new_branchpoint(loop_node=True)   917    918         # Evaluate any test or iterator outside the loop.   919    920         self.process_structure_node(n.test)   921    922         # Propagate attribute usage to branches.   923    924         tracker.new_branch(loop_node=True)   925    926         # Enter the loop.   927    928         in_conditional = self.in_conditional   929         self.in_conditional = True   930         self.process_structure_node(n.body)   931         self.in_conditional = in_conditional   932    933         # Continuing branches are resumed before any test.   934    935         tracker.resume_continuing_branches()   936    937         # Evaluate any continuation test within the body.   938    939         self.process_structure_node(n.test)   940    941         tracker.shelve_branch(loop_node=True)   942    943         # Support the non-looping condition.   944    945         tracker.new_branch()   946         tracker.shelve_branch()   947    948         tracker.merge_branches()   949    950         # Evaluate any else clause outside branches.   951    952         if n.else_:   953             self.process_structure_node(n.else_)   954    955         # Connect broken branches to the code after any loop.   956    957         tracker.resume_broken_branches()   958    959     # Branch tracking methods.   960    961     def start_tracking(self, names):   962    963         """   964         Start tracking attribute usage for names in the current namespace,   965         immediately registering the given 'names'.   966         """   967    968         path = self.get_namespace_path()   969         parent = self.trackers[-1]   970         tracker = BranchTracker()   971         self.trackers.append(tracker)   972    973         # Record the given names established as new branches.   974    975         tracker.assign_names(names)   976    977     def assign_name(self, name, name_ref):   978    979         "Assign to 'name' the given 'name_ref' in the current namespace."   980    981         name = self.get_name_for_tracking(name)   982         self.trackers[-1].assign_names([name], [name_ref])   983    984     def stop_tracking(self):   985    986         """   987         Stop tracking attribute usage, recording computed usage for the current   988         namespace.   989         """   990    991         path = self.get_namespace_path()   992         tracker = self.trackers.pop()   993         self.record_assignments_for_access(tracker)   994    995         self.attr_usage[path] = tracker.get_all_usage()   996         self.name_initialisers[path] = tracker.get_all_values()   997    998     def start_tracking_in_module(self):   999   1000         "Start tracking attribute usage in the module."  1001   1002         tracker = BranchTracker()  1003         self.trackers.append(tracker)  1004   1005     def stop_tracking_in_module(self):  1006   1007         "Stop tracking attribute usage in the module."  1008   1009         tracker = self.trackers[0]  1010         self.record_assignments_for_access(tracker)  1011         self.attr_usage[self.name] = tracker.get_all_usage()  1012         self.name_initialisers[self.name] = tracker.get_all_values()  1013   1014     def record_assignments_for_access(self, tracker):  1015   1016         """  1017         For the current path, use the given 'tracker' to record assignment  1018         version information for attribute accesses.  1019         """  1020   1021         path = self.get_path_for_access()  1022   1023         if not self.attr_accessor_branches.has_key(path):  1024             return  1025   1026         init_item(self.attr_accessors, path, dict)  1027         attr_accessors = self.attr_accessors[path]  1028   1029         # Obtain the branches applying during each access.  1030   1031         for access, all_branches in self.attr_accessor_branches[path].items():  1032             name, attrnames = access  1033             init_item(attr_accessors, access, list)  1034   1035             # Obtain the assignments applying to each branch.  1036   1037             for branches in all_branches:  1038                 positions = tracker.get_assignment_positions_for_branches(name, branches)  1039   1040                 # Detect missing name information.  1041   1042                 if None in positions:  1043                     globals = self.global_attr_accesses.get(path)  1044                     accesses = globals and globals.get(name)  1045                     if not accesses:  1046                         print >>sys.stderr, "In %s, %s may not be defined when used." % (  1047                             self.get_namespace_path(), name)  1048                     positions.remove(None)  1049   1050                 attr_accessors[access].append(positions)  1051   1052     def record_branches_for_access(self, branches, name, attrnames):  1053   1054         """  1055         Record the given 'branches' for an access involving the given 'name' and  1056         'attrnames'.  1057         """  1058   1059         access = name, attrnames  1060         path = self.get_path_for_access()  1061   1062         init_item(self.attr_accessor_branches, path, dict)  1063         attr_accessor_branches = self.attr_accessor_branches[path]  1064   1065         init_item(attr_accessor_branches, access, list)  1066         attr_accessor_branches[access].append(branches)  1067   1068     def record_access_details(self, name, attrnames, assignment):  1069   1070         """  1071         For the given 'name' and 'attrnames', record an access indicating  1072         whether 'assignment' is occurring.  1073   1074         These details correspond to accesses otherwise recorded by the attribute  1075         accessor and attribute access dictionaries.  1076         """  1077   1078         access = name, attrnames  1079         path = self.get_path_for_access()  1080   1081         init_item(self.attr_access_modifiers, path, dict)  1082         init_item(self.attr_access_modifiers[path], access, list)  1083   1084         access_number = len(self.attr_access_modifiers[path][access])  1085         self.attr_access_modifiers[path][access].append(assignment)  1086         return access_number  1087   1088     def record_global_access_details(self, name, attrnames):  1089   1090         """  1091         Record details of a global access via the given 'name' involving the  1092         indicated 'attrnames'.  1093         """  1094   1095         path = self.get_namespace_path()  1096   1097         init_item(self.global_attr_accesses, path, dict)  1098         init_item(self.global_attr_accesses[path], name, set)  1099         self.global_attr_accesses[path][name].add(attrnames)  1100   1101     # Namespace modification.  1102   1103     def record_name(self, name):  1104   1105         "Record the use of 'name' in a namespace."  1106   1107         path = self.get_namespace_path()  1108         init_item(self.names_used, path, set)  1109         self.names_used[path].add(name)  1110   1111     def set_module(self, name, module_name):  1112   1113         """  1114         Set a module in the current namespace using the given 'name' associated  1115         with the corresponding 'module_name'.  1116         """  1117   1118         if name:  1119             self.set_general_local(name, Reference("<module>", module_name))  1120   1121     def set_definition(self, name, kind):  1122   1123         """  1124         Set the definition having the given 'name' and 'kind'.  1125   1126         Definitions are set in the static namespace hierarchy, but they can also  1127         be recorded for function locals.  1128         """  1129   1130         if self.is_global(name):  1131             print >>sys.stderr, "In %s, %s is defined as being global." % (  1132                 self.get_namespace_path(), name)  1133   1134         path = self.get_object_path(name)  1135         self.set_object(path, kind)  1136   1137         ref = self.get_object(path)  1138         if ref.get_kind() == "<var>":  1139             print >>sys.stderr, "In %s, %s is defined more than once." % (  1140                 self.get_namespace_path(), name)  1141   1142         if not self.is_global(name) and self.in_function:  1143             self.set_function_local(name, ref)  1144   1145     def set_function_local(self, name, ref=None):  1146   1147         "Set the local with the given 'name' and optional 'ref'."  1148   1149         locals = self.function_locals[self.get_namespace_path()]  1150         multiple = not ref or locals.has_key(name) and locals[name] != ref  1151         locals[name] = multiple and Reference("<var>") or ref  1152   1153     def assign_general_local(self, name, name_ref):  1154   1155         """  1156         Set for 'name' the given 'name_ref', recording the name for attribute  1157         usage tracking.  1158         """  1159   1160         self.set_general_local(name, name_ref)  1161         self.assign_name(name, name_ref)  1162   1163     def set_general_local(self, name, value=None):  1164   1165         """  1166         Set the 'name' with optional 'value' in any kind of local namespace,  1167         where the 'value' should be a reference if specified.  1168         """  1169   1170         init_value = self.get_initialising_value(value)  1171   1172         # Module global names.  1173   1174         if self.is_global(name):  1175             path = self.get_global_path(name)  1176             self.set_object(path, init_value)  1177   1178         # Function local names.  1179   1180         elif self.in_function:  1181             path = self.get_object_path(name)  1182             self.set_function_local(name, init_value)  1183   1184         # Other namespaces (classes).  1185   1186         else:  1187             path = self.get_object_path(name)  1188             self.set_name(name, init_value)  1189   1190     def set_name(self, name, ref=None):  1191   1192         "Attach the 'name' with optional 'ref' to the current namespace."  1193   1194         self.set_object(self.get_object_path(name), ref)  1195   1196     def set_instance_attr(self, name, ref=None):  1197   1198         """  1199         Add an instance attribute of the given 'name' to the current class,  1200         using the optional 'ref'.  1201         """  1202   1203         init_item(self.instance_attrs, self.in_class, set)  1204         self.instance_attrs[self.in_class].add(name)  1205   1206         if ref:  1207             init_item(self.instance_attr_constants, self.in_class, dict)  1208             self.instance_attr_constants[self.in_class][name] = ref  1209   1210     def get_initialising_value(self, value):  1211   1212         "Return a suitable initialiser reference for 'value'."  1213   1214         # Includes LiteralSequenceRef, ResolvedNameRef...  1215   1216         if isinstance(value, (NameRef, AccessRef, InstanceRef)):  1217             return value.reference()  1218   1219         # In general, invocations do not produce known results. However, the  1220         # name initialisers are resolved once a module has been inspected.  1221   1222         elif isinstance(value, InvocationRef):  1223             return value.reference()  1224   1225         else:  1226             return value  1227   1228     # Static, program-relative naming.  1229   1230     def find_name(self, name):  1231   1232         """  1233         Return the qualified name for the given 'name' used in the current  1234         non-function namespace.  1235         """  1236   1237         path = self.get_namespace_path()  1238         ref = None  1239   1240         if not self.in_function and name not in predefined_constants:  1241             if self.in_class:  1242                 ref = self.get_object(self.get_object_path(name))  1243             if not ref:  1244                 ref = self.get_global_or_builtin(name)  1245   1246         return ref  1247   1248     def get_class(self, node):  1249   1250         """  1251         Use the given 'node' to obtain the identity of a class. Return a  1252         reference for the class. Unresolved dependencies are permitted and must  1253         be resolved later.  1254         """  1255   1256         ref = self._get_class(node)  1257         return ref.has_kind(["<class>", "<depends>"]) and ref or None  1258   1259     def _get_class(self, node):  1260   1261         """  1262         Use the given 'node' to find a class definition. Return a reference to  1263         the class.  1264         """  1265   1266         if isinstance(node, compiler.ast.Getattr):  1267   1268             # Obtain the identity of the access target.  1269   1270             ref = self._get_class(node.expr)  1271   1272             # Where the target is a class or module, obtain the identity of the  1273             # attribute.  1274   1275             if ref.has_kind(["<function>", "<var>"]):  1276                 return None  1277             else:  1278                 attrname = "%s.%s" % (ref.get_origin(), node.attrname)  1279                 return self.get_object(attrname)  1280   1281         # Names can be module-level or built-in.  1282   1283         elif isinstance(node, compiler.ast.Name):  1284   1285             # Record usage of the name and attempt to identify it.  1286   1287             self.record_name(node.name)  1288             return self.find_name(node.name)  1289         else:  1290             return None  1291   1292     def get_constant(self, name, value):  1293   1294         "Return a constant reference for the given type 'name' and 'value'."  1295   1296         ref = self.get_builtin_class(name)  1297         return self.get_constant_reference(ref, value)  1298   1299     def get_literal_instance(self, n, name):  1300   1301         "For node 'n', return a reference to an instance of 'name'."  1302   1303         # Get a reference to the built-in class.  1304   1305         ref = self.get_builtin_class(name)  1306   1307         # Obtain the details of the literal itself.  1308         # An alias to the type is generated for sequences.  1309   1310         if name in ("dict", "list", "tuple"):  1311             self.set_special_literal(name, ref)  1312             return self.process_literal_sequence_node(n, name, ref, LiteralSequenceRef)  1313   1314         # Constant values are independently recorded.  1315   1316         else:  1317             return self.get_constant_reference(ref, n.value)  1318   1319     # Special names.  1320   1321     def get_special(self, name):  1322   1323         "Return any stored value for the given special 'name'."  1324   1325         return self.special.get(name)  1326   1327     def set_special(self, name, value):  1328   1329         """  1330         Set a special 'name' that merely tracks the use of an implicit object  1331         'value'.  1332         """  1333   1334         self.special[name] = value  1335   1336     def set_special_literal(self, name, ref):  1337   1338         """  1339         Set a special name for the literal type 'name' having type 'ref'. Such  1340         special names provide a way of referring to literal object types.  1341         """  1342   1343         literal_name = "$L%s" % name  1344         value = ResolvedNameRef(literal_name, ref)  1345         self.set_special(literal_name, value)  1346   1347     # Functions and invocations.  1348   1349     def set_invocation_usage(self):  1350   1351         """  1352         Discard the current invocation storage figures, retaining the maximum  1353         values.  1354         """  1355   1356         for path, (current, maximum) in self.function_targets.items():  1357             self.importer.function_targets[path] = self.function_targets[path] = maximum  1358   1359         for path, (current, maximum) in self.function_arguments.items():  1360             self.importer.function_arguments[path] = self.function_arguments[path] = maximum  1361   1362     def allocate_arguments(self, path, args):  1363   1364         """  1365         Allocate temporary argument storage using current and maximum  1366         requirements for the given 'path' and 'args'.  1367         """  1368   1369         init_item(self.function_targets, path, lambda: [0, 0])  1370         t = self.function_targets[path]  1371         t[0] += 1  1372         t[1] = max(t[0], t[1])  1373   1374         init_item(self.function_arguments, path, lambda: [0, 0])  1375         t = self.function_arguments[path]  1376         t[0] += len(args) + 1  1377         t[1] = max(t[0], t[1])  1378   1379     def deallocate_arguments(self, path, args):  1380   1381         "Deallocate temporary argument storage for the given 'path' and 'args'."  1382   1383         self.function_targets[path][0] -= 1  1384         self.function_arguments[path][0] -= len(args) + 1  1385   1386 # vim: tabstop=4 expandtab shiftwidth=4