# HG changeset patch # User Paul Boddie # Date 1481929854 -3600 # Node ID e0c8ddcc9c6a257f263a20b8df5de9c25bc81a28 # Parent 4bd1e2112bb606ccf878053925342b6f3c8fb23d Introduced dependency tracking involving deduced attributes, special name references, inheritance relationships and initialisation relationships, recording the locations of special name usage. diff -r 4bd1e2112bb6 -r e0c8ddcc9c6a deducer.py --- a/deducer.py Fri Dec 16 18:45:33 2016 +0100 +++ b/deducer.py Sat Dec 17 00:10:54 2016 +0100 @@ -674,9 +674,7 @@ # Identify references providing dependencies. for attrtype, objtype, attr in referenced_attrs: - if not attr.unresolved(): - provider = self.importer.get_module_provider(attr) - self.importer.test_dependency(attr, path, provider) + self.importer.add_dependency(path, attr.get_origin()) def get_referenced_attrs(self, location): diff -r 4bd1e2112bb6 -r e0c8ddcc9c6a importer.py --- a/importer.py Fri Dec 16 18:45:33 2016 +0100 +++ b/importer.py Sat Dec 17 00:10:54 2016 +0100 @@ -381,6 +381,7 @@ """ self.finalise_classes() + self.add_init_dependencies() self.to_cache() if self.missing: @@ -400,7 +401,6 @@ self.waiting = {} self.depends = {} - self.depend_refs = {} for module in self.modules.values(): @@ -456,10 +456,6 @@ if self.verbose: print >>sys.stderr, "Requiring", provider, "for", ref, "from", module.name - # Record a module ordering dependency. - - self.test_dependency(found, module.name, provider) - module.deferred = original_deferred # Check modules again to see if they are now required and should now @@ -468,30 +464,7 @@ for module_name in self.waiting.keys(): self.require_providers(module_name) - def test_dependency(self, ref, module_name, provider): - - """ - Test 'ref' for establishing a dependency from 'module_name' to - 'provider'. - """ - - if not ref.static() or self.uses_dynamic_callable(ref): - self.add_provider(module_name, provider) - self.add_dependency(module_name, provider, ref) - - def add_provider(self, module_name, provider): - - "Add a dependency for 'module_name' of 'provider'." - - init_item(self.depends, module_name, set) - self.depends[module_name].add(provider) - - def add_dependency(self, module_name, provider, ref): - - "Add dependency details for 'module_name' and 'provider' involving 'ref'." - - init_item(self.depend_refs, (module_name, provider), set) - self.depend_refs[(module_name, provider)].add(ref) + self.add_special_dependencies() def require_providers(self, module_name): @@ -508,36 +481,85 @@ print >>sys.stderr, "Requiring", provider self.require_providers(provider) - def uses_callable(self, ref): + def add_special_dependencies(self): + + "Add dependencies due to the use of special names in namespaces." + + for module in self.modules.values(): + for ref, paths in module.special.values(): + for path in paths: + self.add_dependency(path, ref.get_origin()) + + def add_init_dependencies(self): + + "Add dependencies related to object initialisation." - "Return whether 'ref' refers to a callable." + for name in self.classes.keys(): + if self.is_dynamic_class(name): + + # Make subclasses depend on any class with non-static + # attributes, plus its module for the initialisation. - # Find the function or method associated with the reference. + for subclass in self.subclasses[name]: + ref = Reference("", subclass) + self.add_dependency(subclass, name) + self.add_dependency(subclass, self.get_module_provider(ref)) + + # Also make the class dependent on its module for + # initialisation. + + ref = Reference("", name) + self.add_dependency(name, self.get_module_provider(ref)) - if ref.has_kind(""): - return ref.get_origin() - elif ref.has_kind(""): - return "%s.__init__" % ref.get_origin() - else: + for name in self.function_defaults.keys(): + if self.is_dynamic_callable(name): + + # Make functions with defaults requiring initialisation depend + # on the parent scope. + + ref = Reference("", name) + self.add_dependency(name, ref.parent()) + + def add_dependency(self, path, origin): + + "Add dependency details for 'path' involving 'origin'." + + if origin: + init_item(self.depends, path, set) + self.depends[path].add(origin) + + special_attributes = ("__args__", "__file__", "__fn__", "__fname__", "__mname__", "__name__") + + def is_dynamic_class(self, name): + + """ + Return whether 'name' refers to a class with members that must be + initialised dynamically. + """ + + attrs = self.all_class_attrs.get(name) + + if not attrs: return False - def uses_dynamic_callable(self, ref): + for attrname, attr in attrs.items(): + if attrname in self.special_attributes: + continue + if not attr or not self.get_object(attr).static(): + return True + + return False + + def is_dynamic_callable(self, name): """ - Return whether 'ref' refers to a callable employing defaults that may + Return whether 'name' refers to a callable employing defaults that may need initialising before the callable can be used. """ - origin = self.uses_callable(ref) - if not origin: - return False - - if ref.has_kind(""): - return True - # Find any defaults for the function or method. - defaults = self.function_defaults.get(origin) + defaults = self.function_defaults.get(name) if not defaults: return False @@ -549,26 +571,21 @@ return False - def order_modules(self): - - "Produce a module initialisation ordering." + def order_objects(self): - self.check_ordering() - - module_names = self.modules.keys() + "Produce an object initialisation ordering." # Record the number of modules using or depending on each module. usage = {} - for module_name in module_names: - usage[module_name] = 0 + for path in self.depends.keys(): + usage[path] = 0 - for module_name, depends in self.depends.items(): - if module_name in module_names: - for provider in depends: - if provider in module_names: - usage[provider] += 1 + for path, depends in self.depends.items(): + for origin in depends: + init_item(usage, origin, lambda: 0) + usage[origin] += 1 # Produce an ordering by obtaining exposed modules (required by modules # already processed) and putting them at the start of the list. @@ -576,60 +593,45 @@ ordered = [] while usage: - for module_name, n in usage.items(): + have_next = False + + for path, n in usage.items(): if n == 0: - ordered.insert(0, module_name) - module_names = self.depends.get(module_name) + ordered.insert(0, path) + depends = self.depends.get(path) + + # Reduce usage of the referenced objects. - # Reduce usage of the referenced modules. + if depends: + for origin in depends: + usage[origin] -= 1 - if module_names: - for name in module_names: - usage[name] -= 1 + del usage[path] + have_next = True - del usage[module_name] + if not have_next: + raise ProgramError("Modules with unresolvable dependencies exist: %s" % ", ".join(usage.keys())) ordered.remove("__main__") ordered.append("__main__") return ordered - def check_ordering(self): - - "Check the ordering dependencies." - - mutual = set() - - # Get dependency relationships for each module. + def order_modules(self): - for module_name, modules in self.depends.items(): - - # Find the reverse relationship. + "Produce a module initialisation ordering." - for provider in modules: - if self.depends.has_key(provider) and module_name in self.depends[provider]: - - # Record the module names in order. + ordered = self.order_objects() + filtered = [] - mutual.add((module_name < provider and module_name or provider, - module_name > provider and module_name or provider)) - - if not mutual: - return - - # Format the dependencies. + for module_name in self.modules.keys(): + if module_name not in ordered: + filtered.append(module_name) - mutual = list(mutual) - mutual.sort() - l = [] + for path in ordered: + if self.modules.has_key(path): + filtered.append(path) - for module_name, provider in mutual: - refs = self.depend_refs.get((module_name, provider)) or set() - refs.update(self.depend_refs.get((provider, module_name)) or set()) - refs = list(refs) - refs.sort() - l.append("%s <-> %s\n %s" % (module_name, provider, "\n ".join(map(str, refs)))) - - raise ProgramError, "Modules may not depend on each other for non-static objects:\n%s" % "\n".join(l) + return filtered def find_dependency(self, ref): diff -r 4bd1e2112bb6 -r e0c8ddcc9c6a inspector.py --- a/inspector.py Fri Dec 16 18:45:33 2016 +0100 +++ b/inspector.py Sat Dec 17 00:10:54 2016 +0100 @@ -1421,7 +1421,12 @@ "Return any stored value for the given special 'name'." - return self.special.get(name) + value = self.special.get(name) + if value: + ref, paths = value + else: + ref = None + return ref def set_special(self, name, value): @@ -1430,7 +1435,13 @@ 'value'. """ - self.special[name] = value + if not self.special.has_key(name): + paths = set() + self.special[name] = value, paths + else: + _ref, paths = self.special[name] + + paths.add(self.get_namespace_path()) def set_special_literal(self, name, ref): diff -r 4bd1e2112bb6 -r e0c8ddcc9c6a modules.py --- a/modules.py Fri Dec 16 18:45:33 2016 +0100 +++ b/modules.py Sat Dec 17 00:10:54 2016 +0100 @@ -431,8 +431,8 @@ f.readline() # "special:" line = f.readline().rstrip() while line: - name, ref = line.split(" ", 1) - self.special[name] = decode_reference(ref) + name, ref, paths = self._get_fields(line, 3) + self.special[name] = decode_reference(ref), paths.split(", ") line = f.readline().rstrip() def _get_members(self, f): @@ -698,7 +698,7 @@ "deferred:" deferred references "special:" - zero or more: special name " " reference + zero or more: special name " " reference " " qualified names (empty line) "members:" zero or more: qualified name " " reference @@ -810,7 +810,8 @@ names = self.special.keys() names.sort() for name in names: - print >>f, name, self.special[name] + ref, paths = self.special[name] + print >>f, name, ref, ", ".join(paths) print >>f print >>f, "members:" diff -r 4bd1e2112bb6 -r e0c8ddcc9c6a resolving.py --- a/resolving.py Fri Dec 16 18:45:33 2016 +0100 +++ b/resolving.py Sat Dec 17 00:10:54 2016 +0100 @@ -71,7 +71,8 @@ "Check special names." for name, value in self.special.items(): - self.special[name] = self.importer.identify(value.get_origin()) + ref, paths = value + self.special[name] = self.importer.identify(ref.get_origin()), paths def check_names_used(self): diff -r 4bd1e2112bb6 -r e0c8ddcc9c6a translator.py --- a/translator.py Fri Dec 16 18:45:33 2016 +0100 +++ b/translator.py Sat Dec 17 00:10:54 2016 +0100 @@ -1356,7 +1356,8 @@ elif n.name.startswith("$L") or n.name.startswith("$op") or \ n.name.startswith("$print"): - ref = self.importer.get_module(self.name).special.get(n.name) + + ref, paths = self.importer.get_module(self.name).special[n.name] return TrResolvedNameRef(n.name, ref) # Get the appropriate name for the name reference, using the same method