1.1 --- a/micropython/__init__.py Sat Jul 07 20:40:09 2012 +0200
1.2 +++ b/micropython/__init__.py Sun Jul 08 02:26:27 2012 +0200
1.3 @@ -94,6 +94,8 @@
1.4
1.5 "Finalise the program."
1.6
1.7 + self.importer.complete_modules()
1.8 +
1.9 # Need the tables to finalise.
1.10
1.11 objtable = self.get_object_table()
1.12 @@ -416,6 +418,10 @@
1.13
1.14 self.importers = {}
1.15
1.16 + # Modules involved in circular imports.
1.17 +
1.18 + self.circular_imports = set()
1.19 +
1.20 # Constant records.
1.21
1.22 self.constant_values = {}
1.23 @@ -440,6 +446,7 @@
1.24
1.25 # Status information.
1.26
1.27 + self.completed = 0
1.28 self.vacuumed = 0
1.29 self.finalised = 0
1.30
1.31 @@ -455,6 +462,18 @@
1.32
1.33 return self.modules[name]
1.34
1.35 + def complete_modules(self):
1.36 +
1.37 + "Complete the processing of modules."
1.38 +
1.39 + if self.completed:
1.40 + return
1.41 +
1.42 + self.get_module("__builtins__").complete()
1.43 + self.get_module("__main__").complete()
1.44 +
1.45 + self.completed = 1
1.46 +
1.47 # General maintenance.
1.48
1.49 def vacuum(self, objtable):
1.50 @@ -968,58 +987,43 @@
1.51 # the search path.
1.52
1.53 path = name.split(".")
1.54 - m = self.find_in_path(path[0])
1.55 - if not m:
1.56 - if self.verbose:
1.57 - print >>sys.stderr, "Not found (%s)" % path[0]
1.58 - return None # NOTE: Import error.
1.59 - d, filename = m
1.60 + path_so_far = []
1.61 + top = module = None
1.62
1.63 - # Either acquire a reference to an already-imported module, or load the
1.64 - # module from a file.
1.65 + for p in path:
1.66
1.67 - top = module = self.load_from_file(filename, path[0], importer)
1.68 -
1.69 - # For hierarchical names, traverse each path component...
1.70 + # Get the module's filesystem details.
1.71
1.72 - if len(path) > 1:
1.73 - if not d:
1.74 - if self.verbose:
1.75 - print >>sys.stderr, "No package (%s)" % filename
1.76 - return None # NOTE: Import error (package not found).
1.77 + if not path_so_far:
1.78 + m = self.find_in_path(p)
1.79 else:
1.80 - self.add_submodules(d, module)
1.81 + m = self.find(d, p)
1.82
1.83 - path_so_far = path[:1]
1.84 - for p in path[1:]:
1.85 - path_so_far.append(p)
1.86 + if not m:
1.87 + if self.verbose:
1.88 + print >>sys.stderr, "Not found (%s)" % p
1.89 + return None # NOTE: Import error.
1.90
1.91 - # Find the package or module concerned.
1.92 + # Get the module itself.
1.93
1.94 - m = self.find(d, p)
1.95 - if not m:
1.96 - if self.verbose:
1.97 - print >>sys.stderr, "Not found (%s)" % p
1.98 - return None # NOTE: Import error.
1.99 - d, filename = m
1.100 - module_name = ".".join(path_so_far)
1.101 + d, filename = m
1.102 + path_so_far.append(p)
1.103 + module_name = ".".join(path_so_far)
1.104 + submodule = self.load_from_file(filename, module_name, importer)
1.105
1.106 - # Either reference an imported module or load one from a file.
1.107 -
1.108 - submodule = self.load_from_file(filename, module_name, importer)
1.109 -
1.110 - if d:
1.111 - self.add_submodules(d, module)
1.112 -
1.113 + if module is None:
1.114 + top = submodule
1.115 + else:
1.116 # Store the submodule within its parent module.
1.117
1.118 module.set_module(p, submodule)
1.119 - module = submodule
1.120 +
1.121 + module = submodule
1.122
1.123 - # Stop descending if no package was found.
1.124 + # Stop descending if no package was found.
1.125
1.126 - if not d:
1.127 - break
1.128 + if not d:
1.129 + break
1.130
1.131 # Return either the deepest or the uppermost module.
1.132
2.1 --- a/micropython/data.py Sat Jul 07 20:40:09 2012 +0200
2.2 +++ b/micropython/data.py Sun Jul 08 02:26:27 2012 +0200
2.3 @@ -2023,6 +2023,11 @@
2.4
2.5 self.all_objects = set()
2.6
2.7 + # Whether the module is imported in a circular fashion, exposing the
2.8 + # unfinished namespace to external modification.
2.9 +
2.10 + self.circular_import = self in importer.circular_imports
2.11 +
2.12 # A set of global names that cannot support combined attribute usage
2.13 # observations because they may be modified within functions during
2.14 # initialisation.
2.15 @@ -2084,29 +2089,46 @@
2.16 if not self.namespace.has_key(name):
2.17 self.namespace[name] = Attr(None, self, name)
2.18
2.19 + # Indicate that there is at least one assignment to the name, although
2.20 + # no actual values are recorded.
2.21 +
2.22 attr = self.namespace[name]
2.23 attr.update_assignments(1, False)
2.24
2.25 + def set(self, name, value, single_assignment=1):
2.26 +
2.27 + """
2.28 + Set the module attribute with the given 'name', having the given 'value'
2.29 + that is assigned once if 'single_assignment' is omitted to given as a
2.30 + true value.
2.31 +
2.32 + Where the module is imported before it is completely initialised, all
2.33 + assignments will be regarded as multiple assignments since it is
2.34 + possible that an external assignment to the name may occur.
2.35 + """
2.36 +
2.37 + NamespaceDict.set(self, name, value, not self.circular_import and single_assignment)
2.38 +
2.39 # Attribute usage methods that apply to module globals in certain
2.40 # circumstances.
2.41
2.42 def _use_attribute(self, name, attrname, value=None):
2.43 - if name not in self.modified_names:
2.44 + if name not in self.modified_names and not self.circular_import:
2.45 return NamespaceDict._use_attribute(self, name, attrname, value)
2.46 else:
2.47 self.importer.use_name(attrname, self.full_name(), value)
2.48 return []
2.49
2.50 def _define_attribute_user_for_name(self, node, name):
2.51 - if name not in self.modified_names:
2.52 + if name not in self.modified_names and not self.circular_import:
2.53 NamespaceDict._define_attribute_user_for_name(self, node, name)
2.54
2.55 def _init_attribute_user_for_name(self, node, name):
2.56 - if name not in self.modified_names:
2.57 + if name not in self.modified_names and not self.circular_import:
2.58 NamespaceDict._init_attribute_user_for_name(self, node, name)
2.59
2.60 def _define_attribute_accessor(self, name, attrname, node, value):
2.61 - if name not in self.modified_names:
2.62 + if name not in self.modified_names and not self.circular_import:
2.63 NamespaceDict._define_attribute_accessor(self, name, attrname, node, value)
2.64 else:
2.65 self.importer.use_name(attrname, self.full_name(), value)
3.1 --- a/micropython/inspect.py Sat Jul 07 20:40:09 2012 +0200
3.2 +++ b/micropython/inspect.py Sun Jul 08 02:26:27 2012 +0200
3.3 @@ -90,8 +90,9 @@
3.4 order:
3.5
3.6 1. parse
3.7 - 2. vacuum
3.8 - 3. finalise
3.9 + 2. process
3.10 + 3. vacuum
3.11 + 4. finalise
3.12
3.13 A module importer can be expected to perform these invocations.
3.14 """
3.15 @@ -111,6 +112,7 @@
3.16 self.optimisations = self.importer.optimisations
3.17 self.builtins = self.importer.modules.get("__builtins__")
3.18 self.loaded = 0
3.19 + self.completed = 0
3.20
3.21 # Current expression state.
3.22
3.23 @@ -129,28 +131,33 @@
3.24
3.25 "Parse the file having the given 'filename'."
3.26
3.27 - module = compiler.parseFile(filename)
3.28 - self.process(module)
3.29 + self.astnode = module = compiler.parseFile(filename)
3.30 +
3.31 + # Detect and record imports and globals declared in the module.
3.32 +
3.33 + self.process_structure(module)
3.34
3.35 - def process(self, module):
3.36 + def complete(self):
3.37 + if not self.completed:
3.38 + self.completed = 1
3.39 + self.process()
3.40 + if self.importer.verbose:
3.41 + print >>sys.stderr, "Completed import of", self.full_name()
3.42 +
3.43 + def process(self):
3.44 + return self.process_module(self.astnode)
3.45 +
3.46 + def process_module(self, module):
3.47
3.48 """
3.49 - Process the given 'module', visiting only module-level code and only
3.50 - extracting global declarations from functions. In order to support
3.51 - deferred imports, the function code is processed separately after all
3.52 - modules have been initially processed.
3.53 + Process the given 'module', visiting module-level code and function
3.54 + code.
3.55 """
3.56
3.57 - self.astnode = module
3.58 -
3.59 # Add __name__ to the namespace.
3.60
3.61 self.store("__name__", self._visitConst(self.full_name()))
3.62
3.63 - # Detect and record globals declared in the module.
3.64 -
3.65 - self.process_globals(module)
3.66 -
3.67 # Visit module-level code, also recording global names.
3.68
3.69 processed = self.dispatch(module)
3.70 @@ -187,14 +194,18 @@
3.71 self._visitFunctionBody(node, namespaces)
3.72 namespaces[-1].finalise_attribute_usage()
3.73
3.74 - def process_globals(self, node):
3.75 + def process_structure(self, node):
3.76
3.77 """
3.78 Within the given 'node', process global declarations, adjusting the
3.79 - module namespace.
3.80 + module namespace, and import statements, building a module dependency
3.81 + hierarchy.
3.82 """
3.83
3.84 for n in node.getChildNodes():
3.85 +
3.86 + # Module global detection.
3.87 +
3.88 if isinstance(n, compiler.ast.Global):
3.89 for name in n.names:
3.90
3.91 @@ -203,10 +214,95 @@
3.92 # just indicate that the name cannot be relied upon for
3.93 # various observations.
3.94
3.95 - module.modify_name(name)
3.96 + self.modify_name(name)
3.97 +
3.98 + # Module import declarations.
3.99 +
3.100 + elif isinstance(n, compiler.ast.From):
3.101 +
3.102 + # Load the mentioned module.
3.103 +
3.104 + module = self.importer.load(n.modname, 1, importer=n)
3.105 + self.record_import(n.modname, n)
3.106 +
3.107 + # Speculatively load modules for names beneath the module.
3.108 +
3.109 + for name, alias in n.names:
3.110 + modname = n.modname + "." + name
3.111 + self.record_import(modname, n)
3.112 +
3.113 + elif isinstance(n, compiler.ast.Import):
3.114 +
3.115 + # Load the mentioned module.
3.116 +
3.117 + for name, alias in n.names:
3.118 + self.record_import(name, n)
3.119 +
3.120 + # Nodes using operator module functions.
3.121 +
3.122 + elif operator_functions.has_key(n.__class__.__name__) or \
3.123 + isinstance(n, (compiler.ast.AugAssign, compiler.ast.Compare)):
3.124 +
3.125 + n._module = self.importer.load("operator")
3.126
3.127 else:
3.128 - self.process_globals(n)
3.129 + self.process_structure(n)
3.130 +
3.131 + def get_module_paths(self, name):
3.132 +
3.133 + """
3.134 + Return the paths of modules leading to the module having the given
3.135 + 'name'.
3.136 + """
3.137 +
3.138 + names = []
3.139 + parts = []
3.140 + for part in name.split("."):
3.141 + parts.append(part)
3.142 + names.append(".".join(parts))
3.143 + return names
3.144 +
3.145 + def record_import(self, name, node):
3.146 +
3.147 + """
3.148 + Record an import of a module with the given 'name' occurring at the
3.149 + given 'node'.
3.150 + """
3.151 +
3.152 + module = self.importer.load(name, 1, importer=node)
3.153 + if module and not module.loaded:
3.154 + self.importer.circular_imports.add(module)
3.155 +
3.156 + def complete_import(self, name, return_leaf):
3.157 +
3.158 + """
3.159 + Complete the import of the module with the given 'name', returning the
3.160 + module itself if 'return_leaf' is a true value, or returning the root of
3.161 + the module hierarchy if 'return_leaf' is a false value.
3.162 + """
3.163 +
3.164 + top = module = None
3.165 +
3.166 + for modname in self.get_module_paths(name):
3.167 +
3.168 + # Attempt to get the module, returning None for non-existent
3.169 + # modules.
3.170 +
3.171 + try:
3.172 + module = self.importer.get_module(modname)
3.173 + except KeyError:
3.174 + return None
3.175 +
3.176 + if module:
3.177 + module.complete()
3.178 +
3.179 + if top is None:
3.180 + top = module
3.181 +
3.182 + if return_leaf:
3.183 + return module
3.184 + else:
3.185 + return top
3.186
3.187 def vacuum(self):
3.188
3.189 @@ -531,10 +627,11 @@
3.190
3.191 # Generic support for classes of operations.
3.192
3.193 - def _ensureOperators(self):
3.194 + def _ensureOperators(self, node):
3.195 attr, scope, namespace = self._get_with_scope("$operator")
3.196 if attr is None:
3.197 - module = self.importer.load("operator")
3.198 + module = node._module
3.199 + module.complete()
3.200 self["$operator"] = module
3.201 else:
3.202 module = attr.get_value()
3.203 @@ -544,7 +641,7 @@
3.204
3.205 "Accounting method for the operator 'node'."
3.206
3.207 - operator_module = self._ensureOperators()
3.208 + operator_module = self._ensureOperators(node)
3.209 operator_fn = operator_functions[operator_name or node.__class__.__name__]
3.210 self.use_specific_attribute(operator_module.full_name(), operator_fn)
3.211 return self.OP(node)
3.212 @@ -795,7 +892,7 @@
3.213 # Accounting.
3.214
3.215 operator_fn = operator_functions.get(node.op)
3.216 - operator_module = self._ensureOperators()
3.217 + operator_module = self._ensureOperators(node)
3.218 self.use_specific_attribute(operator_module.full_name(), operator_fn)
3.219
3.220 # Process the assignment.
3.221 @@ -920,7 +1017,7 @@
3.222 # For operators, reference the specific function involved.
3.223
3.224 if operator_fn is not None:
3.225 - operator_module = self._ensureOperators()
3.226 + operator_module = self._ensureOperators(node)
3.227 self.use_specific_attribute(operator_module.full_name(), operator_fn)
3.228
3.229 # Define __contains__ usage on the next node.
3.230 @@ -1003,12 +1100,7 @@
3.231 self.resume_broken_branches()
3.232
3.233 def visitFrom(self, node):
3.234 - module = self.importer.load(node.modname, 1, importer=node)
3.235 - if module and not module.loaded:
3.236 - print >>sys.stderr, "Warning: a circular import of %s is being attempted in %s" % (node.modname, self.full_name())
3.237 -
3.238 - if module is None and self.importer.verbose:
3.239 - print >>sys.stderr, "Warning:", node.modname, "not imported."
3.240 + module = self.complete_import(node.modname, True)
3.241
3.242 for name, alias in node.names:
3.243
3.244 @@ -1020,13 +1112,9 @@
3.245
3.246 # Missing names may refer to submodules.
3.247
3.248 - if not module.has_key(name):
3.249 - submodule = self.importer.load(node.modname + "." + name, 1, importer=node)
3.250 - if submodule:
3.251 - if not submodule.loaded:
3.252 - print >>sys.stderr, "Warning: a circular import of %s.%s is being attempted in %s" % (
3.253 - node.modname, name, self.full_name())
3.254 -
3.255 + submodule = self.complete_import(node.modname + "." + name, True)
3.256 + if submodule:
3.257 + if not module.has_key(name):
3.258 module.store(name, submodule)
3.259
3.260 # Complete the import if the name was found.
3.261 @@ -1035,17 +1123,6 @@
3.262 attr = module[name]
3.263 self.store(alias or name, attr)
3.264 self.use_specific_attribute(module.full_name(), name)
3.265 - if isinstance(attr.get_value(), Module) and not attr.get_value().loaded:
3.266 - submodule = self.importer.load(attr.get_value().name, importer=node)
3.267 -
3.268 - # For circular imports, invalidate attribute usage for
3.269 - # all exported names of submodules whose names are
3.270 - # specified in the from statement.
3.271 -
3.272 - if submodule and not submodule.loaded:
3.273 - for n in submodule.keys():
3.274 - submodule.modify_name(n)
3.275 -
3.276 continue
3.277
3.278 # Support the import of names from missing modules.
3.279 @@ -1061,16 +1138,6 @@
3.280 attr = module[n]
3.281 self.store(n, attr)
3.282 self.use_specific_attribute(module.full_name(), n)
3.283 - if isinstance(attr.get_value(), Module) and not attr.get_value().loaded:
3.284 - submodule = self.importer.load(attr.get_value().name, importer=node)
3.285 -
3.286 - # For circular imports, invalidate attribute usage
3.287 - # for all exported names of submodules provided by
3.288 - # the module.
3.289 -
3.290 - if submodule and not submodule.loaded:
3.291 - for subn in submodule.keys():
3.292 - submodule.modify_name(subn)
3.293
3.294 def visitFunction(self, node):
3.295 return self._visitFunction(node, node.name)
3.296 @@ -1141,21 +1208,12 @@
3.297
3.298 def visitImport(self, node):
3.299 for name, alias in node.names:
3.300 + module = self.complete_import(name, alias)
3.301 if alias is not None:
3.302 - module = self.importer.load(name, 1, importer=node) or UnresolvedName(None, name, self)
3.303 - self.store(alias, module)
3.304 + self.store(alias, module or UnresolvedName(None, name, self))
3.305 else:
3.306 - module = self.importer.load(name, importer=node) or UnresolvedName(None, name.split(".")[0], self)
3.307 - self.store(name.split(".")[0], module)
3.308 -
3.309 - circular = module and not module.loaded
3.310 -
3.311 - # For circular imports, invalidate attribute usage for all exported
3.312 - # names of modules.
3.313 -
3.314 - if module and not module.loaded:
3.315 - for n in module.keys():
3.316 - module.modify_name(n)
3.317 + name_used = name.split(".")[0]
3.318 + self.store(name_used, module or UnresolvedName(None, name_used, self))
3.319
3.320 visitInvert = _visitOperator
3.321