1.1 --- a/importer.py Sat Sep 03 22:38:21 2016 +0200
1.2 +++ b/importer.py Sat Sep 03 22:43:44 2016 +0200
1.3 @@ -49,11 +49,10 @@
1.4 self.cache = cache
1.5 self.verbose = verbose
1.6
1.7 + self.to_import = set()
1.8 +
1.9 self.modules = {}
1.10 - self.modules_ordered = []
1.11 - self.loading = set()
1.12 - self.hidden = {}
1.13 - self.revealing = {}
1.14 + self.accessing_modules = {}
1.15 self.invalidated = set()
1.16
1.17 self.objects = {}
1.18 @@ -230,45 +229,33 @@
1.19
1.20 # Module management.
1.21
1.22 + def queue_module(self, name, module):
1.23 +
1.24 + """
1.25 + Queue the module with the given 'name' for import from the given
1.26 + 'module'.
1.27 + """
1.28 +
1.29 + if not self.modules.has_key(name):
1.30 + self.to_import.add(name)
1.31 +
1.32 + init_item(self.accessing_modules, name, set)
1.33 + self.accessing_modules[name].add(module.name)
1.34 +
1.35 def get_modules(self):
1.36
1.37 "Return all modules known to the importer."
1.38
1.39 return self.modules.values()
1.40
1.41 - def get_module(self, name, hidden=False):
1.42 + def get_module(self, name):
1.43
1.44 "Return the module with the given 'name'."
1.45
1.46 if not self.modules.has_key(name):
1.47 return None
1.48
1.49 - # Obtain the module and attempt to reveal it.
1.50 -
1.51 - module = self.modules[name]
1.52 - if not hidden:
1.53 - self.reveal_module(module)
1.54 - return module
1.55 -
1.56 - def reveal_module(self, module):
1.57 -
1.58 - "Check if 'module' is hidden and reveal it."
1.59 -
1.60 - if module.name in self.hidden:
1.61 - del self.hidden[module.name]
1.62 -
1.63 - # Reveal referenced modules.
1.64 -
1.65 - module.reveal_referenced()
1.66 -
1.67 - def set_revealing(self, module, name, instigator):
1.68 -
1.69 - """
1.70 - Make the revealing of 'module' conditional on 'name' for the given
1.71 - 'instigator' of the reveal operation.
1.72 - """
1.73 -
1.74 - self.revealing[module.name].add((name, instigator))
1.75 + return self.modules[name]
1.76
1.77 # Program operations.
1.78
1.79 @@ -287,10 +274,20 @@
1.80
1.81 m = self.load_from_file(filename)
1.82
1.83 + # Load any queued modules.
1.84 +
1.85 + while self.to_import:
1.86 + for name in list(self.to_import): # avoid mutation issue
1.87 + self.load(name)
1.88 +
1.89 + # Resolve dependencies between modules.
1.90 +
1.91 + self.resolve()
1.92 +
1.93 # Resolve dependencies within the program.
1.94
1.95 - for module in self.modules_ordered:
1.96 - module.resolve()
1.97 + for module in self.modules.values():
1.98 + module.complete()
1.99
1.100 return m
1.101
1.102 @@ -299,12 +296,38 @@
1.103 "Finalise the inspected program."
1.104
1.105 self.finalise_classes()
1.106 - self.remove_hidden()
1.107 self.to_cache()
1.108 self.set_class_types()
1.109 self.define_instantiators()
1.110 self.collect_constants()
1.111
1.112 + # Supporting operations.
1.113 +
1.114 + def resolve(self):
1.115 +
1.116 + "Resolve dependencies between modules."
1.117 +
1.118 + resolved = {}
1.119 +
1.120 + for name, ref in self.objects.items():
1.121 + if ref.has_kind("<depends>"):
1.122 + ref = self.find_dependency(ref)
1.123 + if ref:
1.124 + resolved[name] = ref
1.125 +
1.126 + for name, ref in resolved.items():
1.127 + self.objects[name] = ref
1.128 +
1.129 + def find_dependency(self, ref):
1.130 +
1.131 + "Find the ultimate dependency for 'ref'."
1.132 +
1.133 + found = set()
1.134 + while ref and ref.has_kind("<depends>") and not ref in found:
1.135 + found.add(ref)
1.136 + ref = self.objects.get(ref.get_origin())
1.137 + return ref
1.138 +
1.139 def finalise_classes(self):
1.140
1.141 "Finalise the class relationships and attributes."
1.142 @@ -395,41 +418,6 @@
1.143 if attrs:
1.144 self.all_shadowed_attrs[name] = attrs
1.145
1.146 - def remove_hidden(self):
1.147 -
1.148 - "Remove all hidden modules."
1.149 -
1.150 - # First reveal any modules exposing names.
1.151 -
1.152 - for modname, names in self.revealing.items():
1.153 - module = self.modules[modname]
1.154 -
1.155 - # Obtain the imported names and determine whether they should cause
1.156 - # the module to be revealed.
1.157 -
1.158 - for (name, instigator) in names:
1.159 - if module is not instigator:
1.160 -
1.161 - # Only if an object is provided by the module should the
1.162 - # module be revealed. References to objects in other modules
1.163 - # should not in themselves expose the module in which those
1.164 - # references occur.
1.165 -
1.166 - ref = module.get_global(name)
1.167 - if ref and ref.provided_by_module(module.name):
1.168 - self.reveal_module(module)
1.169 - instigator.revealed.add(module)
1.170 -
1.171 - # Then remove all modules that are still hidden.
1.172 -
1.173 - for modname in self.hidden:
1.174 - module = self.modules[modname]
1.175 - module.unpropagate()
1.176 - del self.modules[modname]
1.177 - ref = self.objects.get(modname)
1.178 - if ref and ref.get_kind() == "<module>":
1.179 - del self.objects[modname]
1.180 -
1.181 def set_class_types(self):
1.182
1.183 "Set the type of each class."
1.184 @@ -533,30 +521,22 @@
1.185 else:
1.186 return None
1.187
1.188 - def load(self, name, return_leaf=False, hidden=False):
1.189 + def load(self, name):
1.190
1.191 """
1.192 Load the module or package with the given 'name'. Return an object
1.193 referencing the loaded module or package, or None if no such module or
1.194 package exists.
1.195 -
1.196 - Where 'return_leaf' is specified, the final module in the chain is
1.197 - returned. Where 'hidden' is specified, the module is marked as hidden.
1.198 """
1.199
1.200 - if return_leaf:
1.201 - name_for_return = name
1.202 - else:
1.203 - name_for_return = name.split(".")[0]
1.204 -
1.205 # Loaded modules are returned immediately.
1.206 # Modules may be known but not yet loading (having been registered as
1.207 # submodules), loading, loaded, or completely unknown.
1.208
1.209 - module = self.get_module(name, hidden)
1.210 + module = self.get_module(name)
1.211
1.212 if module:
1.213 - return self.modules[name_for_return]
1.214 + return self.modules[name]
1.215
1.216 # Otherwise, modules are loaded.
1.217
1.218 @@ -568,7 +548,7 @@
1.219
1.220 path = name.split(".")
1.221 path_so_far = []
1.222 - top = module = None
1.223 + module = None
1.224
1.225 for p in path:
1.226
1.227 @@ -593,18 +573,11 @@
1.228 # Get the module itself.
1.229
1.230 d, filename = m
1.231 - submodule = self.load_from_file(filename, module_name, hidden)
1.232 -
1.233 - if module is None:
1.234 - top = submodule
1.235 + module = self.load_from_file(filename, module_name)
1.236
1.237 - module = submodule
1.238 -
1.239 - # Return either the deepest or the uppermost module.
1.240 + return module
1.241
1.242 - return return_leaf and module or top
1.243 -
1.244 - def load_from_file(self, filename, module_name=None, hidden=False):
1.245 + def load_from_file(self, filename, module_name=None):
1.246
1.247 "Load the module from the given 'filename'."
1.248
1.249 @@ -617,7 +590,7 @@
1.250
1.251 # Try to load from cache.
1.252
1.253 - module = self.load_from_cache(filename, module_name, hidden)
1.254 + module = self.load_from_cache(filename, module_name)
1.255 if module:
1.256 return module
1.257
1.258 @@ -627,10 +600,7 @@
1.259 self.add_module(module_name, module)
1.260 self.update_cache_validity(module)
1.261
1.262 - # Initiate loading if not already in progress.
1.263 -
1.264 - if not module.loaded and module not in self.loading:
1.265 - self._load(module, module_name, hidden, lambda m: m.parse, filename)
1.266 + self._load(module, module_name, lambda m: m.parse, filename)
1.267
1.268 return module
1.269
1.270 @@ -638,7 +608,9 @@
1.271
1.272 "Make 'module' valid in the cache, but invalidate accessing modules."
1.273
1.274 - self.invalidated.update(module.accessing_modules)
1.275 + accessing = self.accessing_modules.get(module.name)
1.276 + if accessing:
1.277 + self.invalidated.update(accessing)
1.278 if module.name in self.invalidated:
1.279 self.invalidated.remove(module.name)
1.280
1.281 @@ -654,48 +626,35 @@
1.282 else:
1.283 return True
1.284
1.285 - def load_from_cache(self, filename, module_name, hidden=False):
1.286 + def load_from_cache(self, filename, module_name):
1.287
1.288 "Return a module residing in the cache."
1.289
1.290 module = self.modules.get(module_name)
1.291
1.292 - if not self.source_is_new(filename, module_name):
1.293 + if not module and not self.source_is_new(filename, module_name):
1.294 + module = inspector.CachedModule(module_name, self)
1.295 + self.add_module(module_name, module)
1.296
1.297 - if not module:
1.298 - module = inspector.CachedModule(module_name, self)
1.299 - self.add_module(module_name, module)
1.300 -
1.301 - if not module.loaded and module not in self.loading:
1.302 - filename = join(self.cache, module_name)
1.303 - self._load(module, module_name, hidden, lambda m: m.from_cache, filename)
1.304 + filename = join(self.cache, module_name)
1.305 + self._load(module, module_name, lambda m: m.from_cache, filename)
1.306
1.307 return module
1.308
1.309 - def _load(self, module, module_name, hidden, fn, filename):
1.310 + def _load(self, module, module_name, fn, filename):
1.311
1.312 """
1.313 - Load 'module' for the given 'module_name', with the module being hidden
1.314 - if 'hidden' is a true value, and with 'fn' performing an invocation on
1.315 - the module with the given 'filename'.
1.316 + Load 'module' for the given 'module_name', and with 'fn' performing an
1.317 + invocation on the module with the given 'filename'.
1.318 """
1.319
1.320 - # Indicate that the module is hidden if requested.
1.321 + # Load the module.
1.322
1.323 - if hidden:
1.324 - self.hidden[module_name] = module
1.325 -
1.326 - # Indicate that loading is in progress and load the module.
1.327 -
1.328 - self.loading.add(module)
1.329 if self.verbose:
1.330 print >>sys.stderr, "Loading", filename
1.331 fn(module)(filename)
1.332 if self.verbose:
1.333 print >>sys.stderr, "Loaded", filename
1.334 - self.loading.remove(module)
1.335 -
1.336 - self.modules_ordered.append(module)
1.337
1.338 def add_module(self, module_name, module):
1.339
1.340 @@ -706,5 +665,7 @@
1.341
1.342 self.modules[module_name] = module
1.343 self.objects[module_name] = Reference("<module>", module_name)
1.344 + if module_name in self.to_import:
1.345 + self.to_import.remove(module_name)
1.346
1.347 # vim: tabstop=4 expandtab shiftwidth=4