# HG changeset patch # User Paul Boddie # Date 1481935870 -3600 # Node ID b59c5fc87a581b7f6a95bdcc96706a97768a3b38 # Parent 6d5de0f9144e48a541fde9bfc60ae711c7622179 Filter module-level dependencies, condense dependencies to exclude non-initialised functions, thus making the graph smaller. Test function defaults for being static as well as being constants. Prevent modules from being dependent on their own contents. Changed the usage graph to use paths instead of counters for diagnostic and validation purposes. diff -r 6d5de0f9144e -r b59c5fc87a58 importer.py --- a/importer.py Sat Dec 17 00:33:59 2016 +0100 +++ b/importer.py Sat Dec 17 01:51:10 2016 +0100 @@ -66,6 +66,7 @@ # Object relationships and dependencies. self.depends = {} + self.module_depends = {} # Basic program information. @@ -462,7 +463,7 @@ # Record a module ordering dependency. if not found.static(): - self.add_dependency(module.name, provider) + self.add_module_dependency(module.name, provider) # Restore the original references so that they may be read back in # and produce the same results. @@ -476,6 +477,7 @@ self.require_providers(module_name) self.add_special_dependencies() + self.add_module_dependencies() def require_providers(self, module_name): @@ -531,14 +533,63 @@ ref = Reference("", name) self.add_dependency(name, ref.parent()) + def add_module_dependencies(self): + + "Record module-based dependencies." + + for module_name, providers in self.module_depends.items(): + if self.modules.has_key(module_name): + for provider in providers: + if self.modules.has_key(provider): + self.add_dependency(module_name, provider) + def add_dependency(self, path, origin): "Add dependency details for 'path' involving 'origin'." - if origin: + if origin and not origin.startswith("%s." % path): init_item(self.depends, path, set) self.depends[path].add(origin) + def add_module_dependency(self, module_name, provider): + + "Add dependency details for 'module_name' involving 'provider'." + + if provider: + init_item(self.module_depends, module_name, set) + self.module_depends[module_name].add(provider) + + def condense_dependencies(self): + + """ + Condense the dependencies by removing all functions that do not need + initialisation. + """ + + d = {} + for path, depends in self.depends.items(): + d[path] = {} + d[path] = self.condense_dependency_entry(depends, d) + + self.depends = {} + + for path, depends in d.items(): + if depends: + self.depends[path] = depends + + def condense_dependency_entry(self, depends, d): + l = set() + for depend in depends: + if self.modules.has_key(depend) or self.classes.has_key(depend) or \ + self.is_dynamic_callable(depend): + + l.add(depend) + else: + deps = d.get(depend) + if deps: + l.update(self.condense_dependency_entry(deps, d)) + return l + # NOTE: Consolidate this information in a common location. special_attributes = ("__args__", "__file__", "__fn__", "__fname__", "__mname__", "__name__") @@ -579,7 +630,7 @@ # Identify non-constant defaults. for name, ref in defaults: - if not ref.is_constant_alias(): + if not ref.static() and not ref.is_constant_alias(): return True return False @@ -588,17 +639,21 @@ "Produce an object initialisation ordering." + self.condense_dependencies() + # Record the number of modules using or depending on each module. usage = {} + # Record path-based dependencies. + for path in self.depends.keys(): - usage[path] = 0 + usage[path] = set() for path, depends in self.depends.items(): for origin in depends: - init_item(usage, origin, lambda: 0) - usage[origin] += 1 + init_item(usage, origin, set) + usage[origin].add(path) # Produce an ordering by obtaining exposed modules (required by modules # already processed) and putting them at the start of the list. @@ -609,7 +664,7 @@ have_next = False for path, n in usage.items(): - if n == 0: + if not n: ordered.insert(0, path) depends = self.depends.get(path) @@ -617,7 +672,7 @@ if depends: for origin in depends: - usage[origin] -= 1 + usage[origin].remove(path) del usage[path] have_next = True