1.1 --- a/deducer.py Fri Dec 16 18:45:33 2016 +0100
1.2 +++ b/deducer.py Sat Dec 17 00:10:54 2016 +0100
1.3 @@ -674,9 +674,7 @@
1.4 # Identify references providing dependencies.
1.5
1.6 for attrtype, objtype, attr in referenced_attrs:
1.7 - if not attr.unresolved():
1.8 - provider = self.importer.get_module_provider(attr)
1.9 - self.importer.test_dependency(attr, path, provider)
1.10 + self.importer.add_dependency(path, attr.get_origin())
1.11
1.12 def get_referenced_attrs(self, location):
1.13
2.1 --- a/importer.py Fri Dec 16 18:45:33 2016 +0100
2.2 +++ b/importer.py Sat Dec 17 00:10:54 2016 +0100
2.3 @@ -381,6 +381,7 @@
2.4 """
2.5
2.6 self.finalise_classes()
2.7 + self.add_init_dependencies()
2.8 self.to_cache()
2.9
2.10 if self.missing:
2.11 @@ -400,7 +401,6 @@
2.12
2.13 self.waiting = {}
2.14 self.depends = {}
2.15 - self.depend_refs = {}
2.16
2.17 for module in self.modules.values():
2.18
2.19 @@ -456,10 +456,6 @@
2.20 if self.verbose:
2.21 print >>sys.stderr, "Requiring", provider, "for", ref, "from", module.name
2.22
2.23 - # Record a module ordering dependency.
2.24 -
2.25 - self.test_dependency(found, module.name, provider)
2.26 -
2.27 module.deferred = original_deferred
2.28
2.29 # Check modules again to see if they are now required and should now
2.30 @@ -468,30 +464,7 @@
2.31 for module_name in self.waiting.keys():
2.32 self.require_providers(module_name)
2.33
2.34 - def test_dependency(self, ref, module_name, provider):
2.35 -
2.36 - """
2.37 - Test 'ref' for establishing a dependency from 'module_name' to
2.38 - 'provider'.
2.39 - """
2.40 -
2.41 - if not ref.static() or self.uses_dynamic_callable(ref):
2.42 - self.add_provider(module_name, provider)
2.43 - self.add_dependency(module_name, provider, ref)
2.44 -
2.45 - def add_provider(self, module_name, provider):
2.46 -
2.47 - "Add a dependency for 'module_name' of 'provider'."
2.48 -
2.49 - init_item(self.depends, module_name, set)
2.50 - self.depends[module_name].add(provider)
2.51 -
2.52 - def add_dependency(self, module_name, provider, ref):
2.53 -
2.54 - "Add dependency details for 'module_name' and 'provider' involving 'ref'."
2.55 -
2.56 - init_item(self.depend_refs, (module_name, provider), set)
2.57 - self.depend_refs[(module_name, provider)].add(ref)
2.58 + self.add_special_dependencies()
2.59
2.60 def require_providers(self, module_name):
2.61
2.62 @@ -508,36 +481,85 @@
2.63 print >>sys.stderr, "Requiring", provider
2.64 self.require_providers(provider)
2.65
2.66 - def uses_callable(self, ref):
2.67 + def add_special_dependencies(self):
2.68 +
2.69 + "Add dependencies due to the use of special names in namespaces."
2.70 +
2.71 + for module in self.modules.values():
2.72 + for ref, paths in module.special.values():
2.73 + for path in paths:
2.74 + self.add_dependency(path, ref.get_origin())
2.75 +
2.76 + def add_init_dependencies(self):
2.77 +
2.78 + "Add dependencies related to object initialisation."
2.79
2.80 - "Return whether 'ref' refers to a callable."
2.81 + for name in self.classes.keys():
2.82 + if self.is_dynamic_class(name):
2.83 +
2.84 + # Make subclasses depend on any class with non-static
2.85 + # attributes, plus its module for the initialisation.
2.86
2.87 - # Find the function or method associated with the reference.
2.88 + for subclass in self.subclasses[name]:
2.89 + ref = Reference("<class>", subclass)
2.90 + self.add_dependency(subclass, name)
2.91 + self.add_dependency(subclass, self.get_module_provider(ref))
2.92 +
2.93 + # Also make the class dependent on its module for
2.94 + # initialisation.
2.95 +
2.96 + ref = Reference("<class>", name)
2.97 + self.add_dependency(name, self.get_module_provider(ref))
2.98
2.99 - if ref.has_kind("<function>"):
2.100 - return ref.get_origin()
2.101 - elif ref.has_kind("<class>"):
2.102 - return "%s.__init__" % ref.get_origin()
2.103 - else:
2.104 + for name in self.function_defaults.keys():
2.105 + if self.is_dynamic_callable(name):
2.106 +
2.107 + # Make functions with defaults requiring initialisation depend
2.108 + # on the parent scope.
2.109 +
2.110 + ref = Reference("<function>", name)
2.111 + self.add_dependency(name, ref.parent())
2.112 +
2.113 + def add_dependency(self, path, origin):
2.114 +
2.115 + "Add dependency details for 'path' involving 'origin'."
2.116 +
2.117 + if origin:
2.118 + init_item(self.depends, path, set)
2.119 + self.depends[path].add(origin)
2.120 +
2.121 + special_attributes = ("__args__", "__file__", "__fn__", "__fname__", "__mname__", "__name__")
2.122 +
2.123 + def is_dynamic_class(self, name):
2.124 +
2.125 + """
2.126 + Return whether 'name' refers to a class with members that must be
2.127 + initialised dynamically.
2.128 + """
2.129 +
2.130 + attrs = self.all_class_attrs.get(name)
2.131 +
2.132 + if not attrs:
2.133 return False
2.134
2.135 - def uses_dynamic_callable(self, ref):
2.136 + for attrname, attr in attrs.items():
2.137 + if attrname in self.special_attributes:
2.138 + continue
2.139 + if not attr or not self.get_object(attr).static():
2.140 + return True
2.141 +
2.142 + return False
2.143 +
2.144 + def is_dynamic_callable(self, name):
2.145
2.146 """
2.147 - Return whether 'ref' refers to a callable employing defaults that may
2.148 + Return whether 'name' refers to a callable employing defaults that may
2.149 need initialising before the callable can be used.
2.150 """
2.151
2.152 - origin = self.uses_callable(ref)
2.153 - if not origin:
2.154 - return False
2.155 -
2.156 - if ref.has_kind("<class>"):
2.157 - return True
2.158 -
2.159 # Find any defaults for the function or method.
2.160
2.161 - defaults = self.function_defaults.get(origin)
2.162 + defaults = self.function_defaults.get(name)
2.163 if not defaults:
2.164 return False
2.165
2.166 @@ -549,26 +571,21 @@
2.167
2.168 return False
2.169
2.170 - def order_modules(self):
2.171 -
2.172 - "Produce a module initialisation ordering."
2.173 + def order_objects(self):
2.174
2.175 - self.check_ordering()
2.176 -
2.177 - module_names = self.modules.keys()
2.178 + "Produce an object initialisation ordering."
2.179
2.180 # Record the number of modules using or depending on each module.
2.181
2.182 usage = {}
2.183
2.184 - for module_name in module_names:
2.185 - usage[module_name] = 0
2.186 + for path in self.depends.keys():
2.187 + usage[path] = 0
2.188
2.189 - for module_name, depends in self.depends.items():
2.190 - if module_name in module_names:
2.191 - for provider in depends:
2.192 - if provider in module_names:
2.193 - usage[provider] += 1
2.194 + for path, depends in self.depends.items():
2.195 + for origin in depends:
2.196 + init_item(usage, origin, lambda: 0)
2.197 + usage[origin] += 1
2.198
2.199 # Produce an ordering by obtaining exposed modules (required by modules
2.200 # already processed) and putting them at the start of the list.
2.201 @@ -576,60 +593,45 @@
2.202 ordered = []
2.203
2.204 while usage:
2.205 - for module_name, n in usage.items():
2.206 + have_next = False
2.207 +
2.208 + for path, n in usage.items():
2.209 if n == 0:
2.210 - ordered.insert(0, module_name)
2.211 - module_names = self.depends.get(module_name)
2.212 + ordered.insert(0, path)
2.213 + depends = self.depends.get(path)
2.214 +
2.215 + # Reduce usage of the referenced objects.
2.216
2.217 - # Reduce usage of the referenced modules.
2.218 + if depends:
2.219 + for origin in depends:
2.220 + usage[origin] -= 1
2.221
2.222 - if module_names:
2.223 - for name in module_names:
2.224 - usage[name] -= 1
2.225 + del usage[path]
2.226 + have_next = True
2.227
2.228 - del usage[module_name]
2.229 + if not have_next:
2.230 + raise ProgramError("Modules with unresolvable dependencies exist: %s" % ", ".join(usage.keys()))
2.231
2.232 ordered.remove("__main__")
2.233 ordered.append("__main__")
2.234 return ordered
2.235
2.236 - def check_ordering(self):
2.237 -
2.238 - "Check the ordering dependencies."
2.239 -
2.240 - mutual = set()
2.241 -
2.242 - # Get dependency relationships for each module.
2.243 + def order_modules(self):
2.244
2.245 - for module_name, modules in self.depends.items():
2.246 -
2.247 - # Find the reverse relationship.
2.248 + "Produce a module initialisation ordering."
2.249
2.250 - for provider in modules:
2.251 - if self.depends.has_key(provider) and module_name in self.depends[provider]:
2.252 -
2.253 - # Record the module names in order.
2.254 + ordered = self.order_objects()
2.255 + filtered = []
2.256
2.257 - mutual.add((module_name < provider and module_name or provider,
2.258 - module_name > provider and module_name or provider))
2.259 -
2.260 - if not mutual:
2.261 - return
2.262 -
2.263 - # Format the dependencies.
2.264 + for module_name in self.modules.keys():
2.265 + if module_name not in ordered:
2.266 + filtered.append(module_name)
2.267
2.268 - mutual = list(mutual)
2.269 - mutual.sort()
2.270 - l = []
2.271 + for path in ordered:
2.272 + if self.modules.has_key(path):
2.273 + filtered.append(path)
2.274
2.275 - for module_name, provider in mutual:
2.276 - refs = self.depend_refs.get((module_name, provider)) or set()
2.277 - refs.update(self.depend_refs.get((provider, module_name)) or set())
2.278 - refs = list(refs)
2.279 - refs.sort()
2.280 - l.append("%s <-> %s\n %s" % (module_name, provider, "\n ".join(map(str, refs))))
2.281 -
2.282 - raise ProgramError, "Modules may not depend on each other for non-static objects:\n%s" % "\n".join(l)
2.283 + return filtered
2.284
2.285 def find_dependency(self, ref):
2.286
3.1 --- a/inspector.py Fri Dec 16 18:45:33 2016 +0100
3.2 +++ b/inspector.py Sat Dec 17 00:10:54 2016 +0100
3.3 @@ -1421,7 +1421,12 @@
3.4
3.5 "Return any stored value for the given special 'name'."
3.6
3.7 - return self.special.get(name)
3.8 + value = self.special.get(name)
3.9 + if value:
3.10 + ref, paths = value
3.11 + else:
3.12 + ref = None
3.13 + return ref
3.14
3.15 def set_special(self, name, value):
3.16
3.17 @@ -1430,7 +1435,13 @@
3.18 'value'.
3.19 """
3.20
3.21 - self.special[name] = value
3.22 + if not self.special.has_key(name):
3.23 + paths = set()
3.24 + self.special[name] = value, paths
3.25 + else:
3.26 + _ref, paths = self.special[name]
3.27 +
3.28 + paths.add(self.get_namespace_path())
3.29
3.30 def set_special_literal(self, name, ref):
3.31
4.1 --- a/modules.py Fri Dec 16 18:45:33 2016 +0100
4.2 +++ b/modules.py Sat Dec 17 00:10:54 2016 +0100
4.3 @@ -431,8 +431,8 @@
4.4 f.readline() # "special:"
4.5 line = f.readline().rstrip()
4.6 while line:
4.7 - name, ref = line.split(" ", 1)
4.8 - self.special[name] = decode_reference(ref)
4.9 + name, ref, paths = self._get_fields(line, 3)
4.10 + self.special[name] = decode_reference(ref), paths.split(", ")
4.11 line = f.readline().rstrip()
4.12
4.13 def _get_members(self, f):
4.14 @@ -698,7 +698,7 @@
4.15 "deferred:"
4.16 deferred references
4.17 "special:"
4.18 - zero or more: special name " " reference
4.19 + zero or more: special name " " reference " " qualified names
4.20 (empty line)
4.21 "members:"
4.22 zero or more: qualified name " " reference
4.23 @@ -810,7 +810,8 @@
4.24 names = self.special.keys()
4.25 names.sort()
4.26 for name in names:
4.27 - print >>f, name, self.special[name]
4.28 + ref, paths = self.special[name]
4.29 + print >>f, name, ref, ", ".join(paths)
4.30
4.31 print >>f
4.32 print >>f, "members:"