# HG changeset patch # User Paul Boddie # Date 1487547308 -3600 # Node ID be30371d7a373ed833285f8c7f7fbf3008be9c90 # Parent a1912ae5f941407ab29bdf3608a67950316b5a74 Employ global names for usage tracking involving globals or other external, non-static names. diff -r a1912ae5f941 -r be30371d7a37 common.py --- a/common.py Sun Feb 19 17:56:06 2017 +0100 +++ b/common.py Mon Feb 20 00:35:08 2017 +0100 @@ -741,14 +741,17 @@ return isinstance(node.expr, compiler.ast.Getattr) - def get_name_for_tracking(self, name, ref=None): + def get_name_for_tracking(self, name, name_ref=None): """ Return the name to be used for attribute usage observations involving - the given 'name' in the current namespace. If 'ref' is indicated and - the name is being used outside a function, return the origin information - from 'ref'; otherwise, return a path computed using the current - namespace and the given name. + the given 'name' in the current namespace. + + If the name is being used outside a function, and if 'name_ref' is + given, a path featuring the name in the global namespace is returned + where 'name_ref' indicates a global, or a static reference is used if + 'name_ref' provides such a reference. Otherwise, a path computed using + the current namespace and the given name is returned. The intention of this method is to provide a suitably-qualified name that can be tracked across namespaces. Where globals are being @@ -766,15 +769,15 @@ if self.in_function: return name - # For static namespaces, use the given qualified name. + # For static references, use the reference origin. - elif ref and ref.static(): - return ref.get_origin() + elif name_ref and name_ref.final(): + return name_ref.final() - # For non-static objects in static namespaces, use any alias. + # For global names outside functions, use a global name. - elif ref and ref.get_name(): - return ref.get_name() + elif name_ref and name_ref.is_global_name(): + return self.get_global_path(name) # Otherwise, establish a name in the current namespace. diff -r a1912ae5f941 -r be30371d7a37 inspector.py --- a/inspector.py Sun Feb 19 17:56:06 2017 +0100 +++ b/inspector.py Mon Feb 20 00:35:08 2017 +0100 @@ -412,7 +412,7 @@ # if assigned in the namespace, or using an external name # (presently just globals within classes). - name = self.get_name_for_tracking(name_ref.name, name_ref.reference()) + name = self.get_name_for_tracking(name_ref.name, name_ref) tracker = self.trackers[-1] immediate_access = len(self.attrs) == 1 @@ -420,10 +420,7 @@ # Record global-based chains for subsequent resolution. - is_global = self.in_function and not self.function_locals[path].has_key(name) or \ - not self.in_function - - if is_global: + if name_ref.is_global_name(): self.record_global_access_details(name, attrnames) # Make sure the name is being tracked: global names will not @@ -873,12 +870,12 @@ ref = self.find_name(n.name) if ref: - return ResolvedNameRef(n.name, ref) + return ResolvedNameRef(n.name, ref, is_global=True) # Explicitly-declared global names. elif self.in_function and n.name in self.scope_globals[path]: - return NameRef(n.name) + return NameRef(n.name, is_global=True) # Examine other names. @@ -899,7 +896,7 @@ # Possible global or built-in name. else: - return NameRef(n.name) + return NameRef(n.name, is_global=True) def process_operator_chain(self, nodes, fn): diff -r a1912ae5f941 -r be30371d7a37 results.py --- a/results.py Sun Feb 19 17:56:06 2017 +0100 +++ b/results.py Mon Feb 20 00:35:08 2017 +0100 @@ -30,6 +30,9 @@ def is_name(self): return False + def is_global_name(self): + return False + def reference(self): return None @@ -90,25 +93,29 @@ "A reference to a name." - def __init__(self, name, expr=None): + def __init__(self, name, expr=None, is_global=False): self.name = name self.expr = expr + self.is_global = is_global def is_name(self): return True + def is_global_name(self): + return self.is_global + def final(self): return None def __repr__(self): - return "NameRef(%r, %r)" % (self.name, self.expr) + return "NameRef(%r, %r, %r)" % (self.name, self.expr, self.is_global) class LocalNameRef(NameRef): "A reference to a local name." def __init__(self, name, number): - NameRef.__init__(self, name) + NameRef.__init__(self, name, is_global=False) self.number = number def __repr__(self): @@ -149,12 +156,12 @@ "A resolved name-based reference." - def __init__(self, name, ref, expr=None): - NameRef.__init__(self, name, expr) + def __init__(self, name, ref, expr=None, is_global=False): + NameRef.__init__(self, name, expr, is_global) ResolvedRef.__init__(self, ref) def __repr__(self): - return "ResolvedNameRef(%r, %r, %r)" % (self.name, self.ref, self.expr) + return "ResolvedNameRef(%r, %r, %r, %r)" % (self.name, self.ref, self.expr, self.is_global) class ConstantValueRef(ResolvedNameRef): diff -r a1912ae5f941 -r be30371d7a37 translator.py --- a/translator.py Sun Feb 19 17:56:06 2017 +0100 +++ b/translator.py Mon Feb 20 00:35:08 2017 +0100 @@ -91,8 +91,8 @@ "A reference to a name in the translation." - def __init__(self, name, ref, expr=None, parameter=None, location=None): - results.ResolvedNameRef.__init__(self, name, ref, expr) + def __init__(self, name, ref, expr=None, is_global=False, parameter=None, location=None): + results.ResolvedNameRef.__init__(self, name, ref, expr, is_global) self.parameter = parameter self.location = location @@ -726,7 +726,7 @@ # the complete access. name_ref = attr_expr and attr_expr.is_name() and attr_expr - name = name_ref and self.get_name_for_tracking(name_ref.name, name_ref and name_ref.reference()) or None + name = name_ref and self.get_name_for_tracking(name_ref.name, name_ref) or None location = self.get_access_location(name, self.attrs) refs = self.get_referenced_attributes(location) @@ -1490,8 +1490,23 @@ # Determine any assigned globals. globals = self.importer.get_module(self.name).scope_globals.get(path) + + # Explicitly declared globals. + if globals and n.name in globals: objpath = self.get_global_path(n.name) + is_global = True + + # Implicitly referenced globals in functions. + + elif self.in_function: + is_global = n.name not in self.importer.function_locals[path] + + # Implicitly referenced globals elsewhere. + + else: + namespace = self.importer.identify(path) + is_global = not self.importer.get_attributes(namespace, n.name) # Get the static identity of the name. @@ -1520,7 +1535,7 @@ # static namespace members. The reference should be configured to return # such names. - return TrResolvedNameRef(n.name, ref, expr=expr, parameter=parameter, location=location) + return TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global, parameter=parameter, location=location) def process_not_node(self, n):