# HG changeset patch # User Paul Boddie # Date 1473097821 -7200 # Node ID 671c2c298d335744533d5787986ca7984fc509bd # Parent f47c63967c5965e95ad988cea5c10b7d56eeb802 Separated external name reference definition from subsequent operations involving name references so that deferred references can be identified in the importer's resolution activity and be replaced with resolved names, with such resolved names then becoming available to each module's resolution activity. Introduced a special "" reference kind that can be handled in the initialised name processing (which uses inspection-specific name reference objects) and also when processing affected module data. diff -r f47c63967c59 -r 671c2c298d33 importer.py --- a/importer.py Mon Sep 05 00:12:56 2016 +0200 +++ b/importer.py Mon Sep 05 19:50:21 2016 +0200 @@ -169,6 +169,14 @@ self.objects[name] = ref + # Identification of both stored object names and name references. + + def identify(self, name): + + "Identify 'name' using stored object and external name records." + + return self.objects.get(name) or self.all_name_references.get(name) + # Indirect object retrieval. def get_attributes(self, ref, attrname): @@ -328,24 +336,25 @@ "Resolve dependencies between modules." - resolved = {} + for d in [self.objects, self.all_name_references]: + resolved = {} - for name, ref in self.objects.items(): - if ref.has_kind(""): - found = self.find_dependency(ref) - if found: - resolved[name] = found - else: - print >>sys.stderr, "Name %s references an unknown object: %s" % (name, ref.get_origin()) + for name, ref in d.items(): + if ref.has_kind(""): + found = self.find_dependency(ref) + if found: + resolved[name] = found + else: + print >>sys.stderr, "Name %s references an unknown object: %s" % (name, ref.get_origin()) - # Record the resolved names and identify required modules. + # Record the resolved names and identify required modules. - for name, ref in resolved.items(): - self.objects[name] = ref + for name, ref in resolved.items(): + d[name] = ref - module_name = self.get_module_provider(ref) - if module_name: - self.required.add(module_name) + module_name = self.get_module_provider(ref) + if module_name: + self.required.add(module_name) def find_dependency(self, ref): diff -r f47c63967c59 -r 671c2c298d33 inspector.py --- a/inspector.py Mon Sep 05 00:12:56 2016 +0200 +++ b/inspector.py Mon Sep 05 19:50:21 2016 +0200 @@ -57,6 +57,8 @@ def __repr__(self): return "InspectedModule(%r, %r)" % (self.name, self.importer) + # Principal methods. + def parse(self, filename): "Parse the file having the given 'filename'." @@ -82,6 +84,10 @@ self.stop_tracking_in_module() + # Collect external name references. + + self.collect_names() + def complete(self): "Complete the module inspection." @@ -98,6 +104,54 @@ self.propagate() + # Accessory methods. + + def collect_names(self): + + "Collect the names used by each scope." + + for path in self.names_used.keys(): + self.collect_names_for_path(path) + + def collect_names_for_path(self, path): + + "Collect the names used by the given 'path'." + + names = self.names_used.get(path) + if not names: + return + + in_function = self.function_locals.has_key(path) + + for name in names: + if name in predefined_constants or in_function and name in self.function_locals[path]: + continue + + # Find local definitions (within static namespaces). + + key = "%s.%s" % (path, name) + ref = self.get_resolved_object(key) + if ref: + self.importer.all_name_references[key] = self.name_references[key] = ref.alias(key) + continue + + # Find global or built-in definitions. + + ref = self.get_resolved_global_or_builtin(name) + if ref: + self.importer.all_name_references[key] = self.name_references[key] = ref + continue + + print >>sys.stderr, "Name not recognised: %s in %s" % (name, path) + init_item(self.names_missing, path, set) + self.names_missing[path].add(name) + + def get_resolved_global_or_builtin(self, name): + + "Return the resolved global or built-in object with the given 'name'." + + return self.get_global(name) or self.importer.get_object("__builtins__.%s" % name) + def set_invocation_usage(self): """ @@ -734,7 +788,6 @@ ref = self.import_name_from_module(op, "operator") # Record the imported name and provide the resolved name reference. - # NOTE: Maybe use a different class. value = ResolvedNameRef(n.name, ref) self.set_special(n.name, value) @@ -1198,7 +1251,7 @@ # name initialisers are resolved once a module has been inspected. elif isinstance(value, InvocationRef): - return None + return value.reference() else: return value diff -r f47c63967c59 -r 671c2c298d33 modules.py --- a/modules.py Mon Sep 05 00:12:56 2016 +0200 +++ b/modules.py Mon Sep 05 19:50:21 2016 +0200 @@ -181,7 +181,6 @@ "Propagate name references for the module." - self.importer.all_name_references.update(self.name_references) self.importer.all_initialised_names.update(self.initialised_names) self.importer.all_aliased_names.update(self.aliased_names) diff -r f47c63967c59 -r 671c2c298d33 resolving.py --- a/resolving.py Mon Sep 05 00:12:56 2016 +0200 +++ b/resolving.py Mon Sep 05 19:50:21 2016 +0200 @@ -31,18 +31,6 @@ # Object resolution. - def resolve_object(self, ref): - - """ - Return the given 'ref' in resolved form, given knowledge of the entire - program. - """ - - if ref.has_kind(""): - return self.importer.get_object(ref.get_origin()) - else: - return ref - def get_resolved_object(self, path): """ @@ -61,12 +49,6 @@ else: return None - def get_resolved_global_or_builtin(self, name): - - "Return the resolved global or built-in object with the given 'name'." - - return self.get_global(name) or self.importer.get_object("__builtins__.%s" % name) - # Post-inspection resolution activities. def resolve(self): @@ -77,22 +59,31 @@ self.resolve_class_bases() self.check_special() self.check_names_used() + self.check_invocations() self.resolve_initialisers() self.resolve_literals() self.remove_redundant_accessors() def resolve_members(self): - "Resolve any members referring to deferred references." + """ + Resolve any members referring to deferred references, using information + stored in the importer. This updates stored object and external name + records in this module. + """ - for name, ref in self.objects.items(): - if ref.has_kind(""): - ref = self.importer.get_object(name) + for impd, d in [ + (self.importer.objects, self.objects), + (self.importer.all_name_references, self.name_references) + ]: - # Alias the member and write back to the importer. + for name, ref in d.items(): + + # Obtain resolved names from the importer. - ref = ref.alias(name) - self.importer.objects[name] = self.objects[name] = ref + if ref.has_kind(""): + ref = self.importer.identify(name) + d[name] = ref def resolve_class_bases(self): @@ -103,7 +94,7 @@ bad = [] for base in bases: - ref = self.resolve_object(base) + ref = self.importer.identify(base.get_origin()) # Obtain the origin of the base class reference. @@ -127,46 +118,44 @@ def check_names_used(self): - "Check the names used by each function." + "Check the external names used by each scope." - for path in self.names_used.keys(): - self.check_names_used_for_path(path) + for key, ref in self.name_references.items(): + path, name = key.rsplit(".", 1) + self.resolve_accesses(path, name, ref) - def check_names_used_for_path(self, path): - - "Check the names used by the given 'path'." + def check_invocations(self): - names = self.names_used.get(path) - if not names: - return + "Find invocations amongst module data and replace their results." - in_function = self.function_locals.has_key(path) + # Find members and replace invocation results with values. This is + # effectively the same as is done for initialised names, but refers to + # any unchanging value after initialisation. - for name in names: - if name in predefined_constants or in_function and name in self.function_locals[path]: - continue + for key, ref in self.objects.items(): + if ref.has_kind(""): + ref = self.convert_invocation(ref) + self.importer.objects[key] = self.objects[key] = ref - # Find local definitions (within static namespaces). + # Rewrite function defaults, which are effectively extra members of the + # module. - key = "%s.%s" % (path, name) - ref = self.get_resolved_object(key) - if ref: - self.name_references[key] = ref.final() or key - self.resolve_accesses(path, name, ref) - continue - - # Find global or built-in definitions. + defaults = self.function_defaults.items() - ref = self.get_resolved_global_or_builtin(name) - objpath = ref and (ref.final() or ref.get_name()) - if objpath: - self.name_references[key] = objpath - self.resolve_accesses(path, name, ref) - continue + for fname, parameters in defaults: + l = [] + for pname, ref in parameters: + if ref.has_kind(""): + ref = self.convert_invocation(ref) + l.append((pname, ref)) + self.function_defaults[fname] = l - print >>sys.stderr, "Name not recognised: %s in %s" % (name, path) - init_item(self.names_missing, path, set) - self.names_missing[path].add(name) + def convert_invocation(self, ref): + + "Convert the given invocation 'ref', handling instantiation." + + ref = self.importer.identify(ref.get_origin()) + return ref and ref.has_kind("") and ref.instance_of() or Reference("") def resolve_accesses(self, path, name, ref): @@ -237,6 +226,9 @@ objpath = ".".join(attrs) if objpath != path: + if last_ref.has_kind(""): + last_ref = self.convert_invocation(last_ref) + # Establish a constant access. init_item(self.const_accesses, path, dict) @@ -362,15 +354,14 @@ # or unresolved names referring to globals or built-ins. if ref.has_kind(""): - ref = self.importer.get_object(ref.get_origin()) or \ - self.importer.get_object(self.name_references.get(ref.get_origin())) + ref = self.importer.identify(ref.get_origin()) # Convert class invocations to instances. if invocation: - ref = ref.has_kind("") and ref.instance_of() or None + ref = self.convert_invocation(ref) - if ref: + if ref and not ref.has_kind(""): initialised_names[i] = ref if initialised_names: diff -r f47c63967c59 -r 671c2c298d33 results.py --- a/results.py Mon Sep 05 00:12:56 2016 +0200 +++ b/results.py Mon Sep 05 19:50:21 2016 +0200 @@ -19,6 +19,8 @@ this program. If not, see . """ +from referencing import Reference + # Classes representing inspection and translation observations. class Result: @@ -55,6 +57,13 @@ def __init__(self, name_ref): self.name_ref = name_ref + def reference(self): + origin = self.name_ref.get_origin() + if origin: + return Reference("", origin) + else: + return Reference("") + def __repr__(self): return "InvocationRef(%r)" % self.name_ref