1.1 --- a/importer.py Fri Dec 16 18:45:33 2016 +0100
1.2 +++ b/importer.py Sat Dec 17 00:10:54 2016 +0100
1.3 @@ -381,6 +381,7 @@
1.4 """
1.5
1.6 self.finalise_classes()
1.7 + self.add_init_dependencies()
1.8 self.to_cache()
1.9
1.10 if self.missing:
1.11 @@ -400,7 +401,6 @@
1.12
1.13 self.waiting = {}
1.14 self.depends = {}
1.15 - self.depend_refs = {}
1.16
1.17 for module in self.modules.values():
1.18
1.19 @@ -456,10 +456,6 @@
1.20 if self.verbose:
1.21 print >>sys.stderr, "Requiring", provider, "for", ref, "from", module.name
1.22
1.23 - # Record a module ordering dependency.
1.24 -
1.25 - self.test_dependency(found, module.name, provider)
1.26 -
1.27 module.deferred = original_deferred
1.28
1.29 # Check modules again to see if they are now required and should now
1.30 @@ -468,30 +464,7 @@
1.31 for module_name in self.waiting.keys():
1.32 self.require_providers(module_name)
1.33
1.34 - def test_dependency(self, ref, module_name, provider):
1.35 -
1.36 - """
1.37 - Test 'ref' for establishing a dependency from 'module_name' to
1.38 - 'provider'.
1.39 - """
1.40 -
1.41 - if not ref.static() or self.uses_dynamic_callable(ref):
1.42 - self.add_provider(module_name, provider)
1.43 - self.add_dependency(module_name, provider, ref)
1.44 -
1.45 - def add_provider(self, module_name, provider):
1.46 -
1.47 - "Add a dependency for 'module_name' of 'provider'."
1.48 -
1.49 - init_item(self.depends, module_name, set)
1.50 - self.depends[module_name].add(provider)
1.51 -
1.52 - def add_dependency(self, module_name, provider, ref):
1.53 -
1.54 - "Add dependency details for 'module_name' and 'provider' involving 'ref'."
1.55 -
1.56 - init_item(self.depend_refs, (module_name, provider), set)
1.57 - self.depend_refs[(module_name, provider)].add(ref)
1.58 + self.add_special_dependencies()
1.59
1.60 def require_providers(self, module_name):
1.61
1.62 @@ -508,36 +481,85 @@
1.63 print >>sys.stderr, "Requiring", provider
1.64 self.require_providers(provider)
1.65
1.66 - def uses_callable(self, ref):
1.67 + def add_special_dependencies(self):
1.68 +
1.69 + "Add dependencies due to the use of special names in namespaces."
1.70 +
1.71 + for module in self.modules.values():
1.72 + for ref, paths in module.special.values():
1.73 + for path in paths:
1.74 + self.add_dependency(path, ref.get_origin())
1.75 +
1.76 + def add_init_dependencies(self):
1.77 +
1.78 + "Add dependencies related to object initialisation."
1.79
1.80 - "Return whether 'ref' refers to a callable."
1.81 + for name in self.classes.keys():
1.82 + if self.is_dynamic_class(name):
1.83 +
1.84 + # Make subclasses depend on any class with non-static
1.85 + # attributes, plus its module for the initialisation.
1.86
1.87 - # Find the function or method associated with the reference.
1.88 + for subclass in self.subclasses[name]:
1.89 + ref = Reference("<class>", subclass)
1.90 + self.add_dependency(subclass, name)
1.91 + self.add_dependency(subclass, self.get_module_provider(ref))
1.92 +
1.93 + # Also make the class dependent on its module for
1.94 + # initialisation.
1.95 +
1.96 + ref = Reference("<class>", name)
1.97 + self.add_dependency(name, self.get_module_provider(ref))
1.98
1.99 - if ref.has_kind("<function>"):
1.100 - return ref.get_origin()
1.101 - elif ref.has_kind("<class>"):
1.102 - return "%s.__init__" % ref.get_origin()
1.103 - else:
1.104 + for name in self.function_defaults.keys():
1.105 + if self.is_dynamic_callable(name):
1.106 +
1.107 + # Make functions with defaults requiring initialisation depend
1.108 + # on the parent scope.
1.109 +
1.110 + ref = Reference("<function>", name)
1.111 + self.add_dependency(name, ref.parent())
1.112 +
1.113 + def add_dependency(self, path, origin):
1.114 +
1.115 + "Add dependency details for 'path' involving 'origin'."
1.116 +
1.117 + if origin:
1.118 + init_item(self.depends, path, set)
1.119 + self.depends[path].add(origin)
1.120 +
1.121 + special_attributes = ("__args__", "__file__", "__fn__", "__fname__", "__mname__", "__name__")
1.122 +
1.123 + def is_dynamic_class(self, name):
1.124 +
1.125 + """
1.126 + Return whether 'name' refers to a class with members that must be
1.127 + initialised dynamically.
1.128 + """
1.129 +
1.130 + attrs = self.all_class_attrs.get(name)
1.131 +
1.132 + if not attrs:
1.133 return False
1.134
1.135 - def uses_dynamic_callable(self, ref):
1.136 + for attrname, attr in attrs.items():
1.137 + if attrname in self.special_attributes:
1.138 + continue
1.139 + if not attr or not self.get_object(attr).static():
1.140 + return True
1.141 +
1.142 + return False
1.143 +
1.144 + def is_dynamic_callable(self, name):
1.145
1.146 """
1.147 - Return whether 'ref' refers to a callable employing defaults that may
1.148 + Return whether 'name' refers to a callable employing defaults that may
1.149 need initialising before the callable can be used.
1.150 """
1.151
1.152 - origin = self.uses_callable(ref)
1.153 - if not origin:
1.154 - return False
1.155 -
1.156 - if ref.has_kind("<class>"):
1.157 - return True
1.158 -
1.159 # Find any defaults for the function or method.
1.160
1.161 - defaults = self.function_defaults.get(origin)
1.162 + defaults = self.function_defaults.get(name)
1.163 if not defaults:
1.164 return False
1.165
1.166 @@ -549,26 +571,21 @@
1.167
1.168 return False
1.169
1.170 - def order_modules(self):
1.171 -
1.172 - "Produce a module initialisation ordering."
1.173 + def order_objects(self):
1.174
1.175 - self.check_ordering()
1.176 -
1.177 - module_names = self.modules.keys()
1.178 + "Produce an object initialisation ordering."
1.179
1.180 # Record the number of modules using or depending on each module.
1.181
1.182 usage = {}
1.183
1.184 - for module_name in module_names:
1.185 - usage[module_name] = 0
1.186 + for path in self.depends.keys():
1.187 + usage[path] = 0
1.188
1.189 - for module_name, depends in self.depends.items():
1.190 - if module_name in module_names:
1.191 - for provider in depends:
1.192 - if provider in module_names:
1.193 - usage[provider] += 1
1.194 + for path, depends in self.depends.items():
1.195 + for origin in depends:
1.196 + init_item(usage, origin, lambda: 0)
1.197 + usage[origin] += 1
1.198
1.199 # Produce an ordering by obtaining exposed modules (required by modules
1.200 # already processed) and putting them at the start of the list.
1.201 @@ -576,60 +593,45 @@
1.202 ordered = []
1.203
1.204 while usage:
1.205 - for module_name, n in usage.items():
1.206 + have_next = False
1.207 +
1.208 + for path, n in usage.items():
1.209 if n == 0:
1.210 - ordered.insert(0, module_name)
1.211 - module_names = self.depends.get(module_name)
1.212 + ordered.insert(0, path)
1.213 + depends = self.depends.get(path)
1.214 +
1.215 + # Reduce usage of the referenced objects.
1.216
1.217 - # Reduce usage of the referenced modules.
1.218 + if depends:
1.219 + for origin in depends:
1.220 + usage[origin] -= 1
1.221
1.222 - if module_names:
1.223 - for name in module_names:
1.224 - usage[name] -= 1
1.225 + del usage[path]
1.226 + have_next = True
1.227
1.228 - del usage[module_name]
1.229 + if not have_next:
1.230 + raise ProgramError("Modules with unresolvable dependencies exist: %s" % ", ".join(usage.keys()))
1.231
1.232 ordered.remove("__main__")
1.233 ordered.append("__main__")
1.234 return ordered
1.235
1.236 - def check_ordering(self):
1.237 -
1.238 - "Check the ordering dependencies."
1.239 -
1.240 - mutual = set()
1.241 -
1.242 - # Get dependency relationships for each module.
1.243 + def order_modules(self):
1.244
1.245 - for module_name, modules in self.depends.items():
1.246 -
1.247 - # Find the reverse relationship.
1.248 + "Produce a module initialisation ordering."
1.249
1.250 - for provider in modules:
1.251 - if self.depends.has_key(provider) and module_name in self.depends[provider]:
1.252 -
1.253 - # Record the module names in order.
1.254 + ordered = self.order_objects()
1.255 + filtered = []
1.256
1.257 - mutual.add((module_name < provider and module_name or provider,
1.258 - module_name > provider and module_name or provider))
1.259 -
1.260 - if not mutual:
1.261 - return
1.262 -
1.263 - # Format the dependencies.
1.264 + for module_name in self.modules.keys():
1.265 + if module_name not in ordered:
1.266 + filtered.append(module_name)
1.267
1.268 - mutual = list(mutual)
1.269 - mutual.sort()
1.270 - l = []
1.271 + for path in ordered:
1.272 + if self.modules.has_key(path):
1.273 + filtered.append(path)
1.274
1.275 - for module_name, provider in mutual:
1.276 - refs = self.depend_refs.get((module_name, provider)) or set()
1.277 - refs.update(self.depend_refs.get((provider, module_name)) or set())
1.278 - refs = list(refs)
1.279 - refs.sort()
1.280 - l.append("%s <-> %s\n %s" % (module_name, provider, "\n ".join(map(str, refs))))
1.281 -
1.282 - raise ProgramError, "Modules may not depend on each other for non-static objects:\n%s" % "\n".join(l)
1.283 + return filtered
1.284
1.285 def find_dependency(self, ref):
1.286