# HG changeset patch # User Paul Boddie # Date 1341532330 -7200 # Node ID d161d4ac7dc02a8d15d088ae82889eeb55112908 # Parent 5d7586cd3780d2a24993927b6c4ec5043a694754 Added module global modification/invalidation upon imports. Reverted the separate module processing phases introduced in 8824b42d7b71. Established a mechanism for modifying module globals, updating Attr assignment statistics and recording each name. Changed the update method on the Attr class to use a separate assignments update method, fixing the number of assignments recorded. Changed the get_context and get_value methods on the Attr class to test the number of assignments as well as the length of the list of recorded values. Changed one of the tests to more thoroughly demonstrate the effects of global modification on attribute usage and constant value identification. diff -r 5d7586cd3780 -r d161d4ac7dc0 micropython/__init__.py --- a/micropython/__init__.py Wed Jul 04 01:25:34 2012 +0200 +++ b/micropython/__init__.py Fri Jul 06 01:52:10 2012 +0200 @@ -94,8 +94,6 @@ "Finalise the program." - self.importer.complete_modules() - # Need the tables to finalise. objtable = self.get_object_table() @@ -442,7 +440,6 @@ # Status information. - self.completed = 0 self.vacuumed = 0 self.finalised = 0 @@ -458,28 +455,6 @@ return self.modules[name] - def complete_modules(self): - - "Complete the processing of modules." - - if self.completed: - return - - processed = set() - modules = self.get_modules() - - while len(processed) != len(modules): - for module in modules: - if module in processed: - continue - if self.verbose: - print >>sys.stderr, "Completing", module.full_name() - module.process_functions() - processed.add(module) - modules = self.get_modules() - - self.completed = 1 - # General maintenance. def vacuum(self, objtable): diff -r 5d7586cd3780 -r d161d4ac7dc0 micropython/data.py --- a/micropython/data.py Wed Jul 04 01:25:34 2012 +0200 +++ b/micropython/data.py Fri Jul 06 01:52:10 2012 +0200 @@ -1061,13 +1061,19 @@ return [v for (c, v) in self.context_values] def get_context(self): - if len(self.context_values) == 1: + + "Get the context referenced by the attribute." + + if self.assignments == 1 and len(self.context_values) == 1: return self.get_contexts()[0] else: return None def get_value(self): - if len(self.context_values) == 1: + + "Get the value referenced by the attribute." + + if self.assignments == 1 and len(self.context_values) == 1: return self.get_values()[0] else: return None @@ -1089,18 +1095,20 @@ not (make_instance(), make_instance()) in context_values: return + self.update_assignments(len(set(context_values)), single_assignment) + self.context_values.update(context_values) + + def update_assignments(self, n, single_assignment): if self.assignments is None: if single_assignment: - self.assignments = len(set(context_values)) + self.assignments = n else: - self.assignments = AtLeast(len(set(context_values))) + self.assignments = AtLeast(n) else: if single_assignment: - self.assignments += 1 + self.assignments += n else: - self.assignments += AtLeast(1) - - self.context_values.update(context_values) + self.assignments += AtLeast(n) def is_constant(self): @@ -2061,6 +2069,24 @@ return dict(self) + def modify_name(self, name): + + """ + Modify a global 'name' invalidating various assumptions about its + behaviour based on the module namespace being "safe" and suitable for + attribute usage and constant value observations. + """ + + self.modified_names.add(name) + + # Establish an attribute directly in the namespace if not present. + + if not self.namespace.has_key(name): + self.namespace[name] = Attr(None, self, name) + + attr = self.namespace[name] + attr.update_assignments(1, False) + # Attribute usage methods that apply to module globals in certain # circumstances. diff -r 5d7586cd3780 -r d161d4ac7dc0 micropython/inspect.py --- a/micropython/inspect.py Wed Jul 04 01:25:34 2012 +0200 +++ b/micropython/inspect.py Fri Jul 06 01:52:10 2012 +0200 @@ -90,9 +90,8 @@ order: 1. parse - 2. process_functions - 3. vacuum - 4. finalise + 2. vacuum + 3. finalise A module importer can be expected to perform these invocations. """ @@ -158,6 +157,10 @@ self.finalise_attribute_usage() + # Visit functions. + + self.process_functions() + # Add references to other modules declared using the __all__ global. if self.has_key("__all__"): @@ -196,11 +199,11 @@ for name in n.names: # Each name may potentially be assigned many times. - # NOTE: We don't try and find out the specifics at this - # NOTE: point and just set an instance instead. + # We don't try and find out the specifics at this point and + # just indicate that the name cannot be relied upon for + # various observations. - self.set(name, make_instance(), False) - self.modified_names.add(name) + module.modify_name(name) else: self.process_globals(n) @@ -1007,8 +1010,8 @@ if module and not module.loaded: print >>sys.stderr, "Warning: a circular import of %s is being attempted in %s" % (node.modname, self.full_name()) - #if module is None: - # print >>sys.stderr, "Warning:", node.modname, "not imported." + if module is None and self.importer.verbose: + print >>sys.stderr, "Warning:", node.modname, "not imported." for name, alias in node.names: @@ -1036,7 +1039,16 @@ self.store(alias or name, attr) self.use_specific_attribute(module.full_name(), name) if isinstance(attr.get_value(), Module) and not attr.get_value().loaded: - self.importer.load(attr.get_value().name, importer=node) + submodule = self.importer.load(attr.get_value().name, importer=node) + + # For circular imports, invalidate attribute usage for + # all exported names of submodules whose names are + # specified in the from statement. + + if submodule and not submodule.loaded: + for n in submodule.keys(): + submodule.modify_name(n) + continue # Support the import of names from missing modules. @@ -1053,7 +1065,15 @@ self.store(n, attr) self.use_specific_attribute(module.full_name(), n) if isinstance(attr.get_value(), Module) and not attr.get_value().loaded: - self.importer.load(attr.get_value().name, importer=node) + submodule = self.importer.load(attr.get_value().name, importer=node) + + # For circular imports, invalidate attribute usage + # for all exported names of submodules provided by + # the module. + + if submodule and not submodule.loaded: + for subn in submodule.keys(): + submodule.modify_name(subn) def visitFunction(self, node): return self._visitFunction(node, node.name) @@ -1131,6 +1151,15 @@ module = self.importer.load(name, importer=node) or UnresolvedName(None, name.split(".")[0], self) self.store(name.split(".")[0], module) + circular = module and not module.loaded + + # For circular imports, invalidate attribute usage for all exported + # names of modules. + + if module and not module.loaded: + for n in module.keys(): + module.modify_name(n) + visitInvert = _visitUnary def visitKeyword(self, node): diff -r 5d7586cd3780 -r d161d4ac7dc0 tests/changed/__init__.py --- a/tests/changed/__init__.py Wed Jul 04 01:25:34 2012 +0200 +++ b/tests/changed/__init__.py Fri Jul 06 01:52:10 2012 +0200 @@ -1,12 +1,18 @@ #!/usr/bin/env python +class C: + p = 123 + +class D: + p = 456 + def f(): import changed.modifier def getx(): - return x + return x.p # ambiguous -x = 123 +x = C f() # import modifier which changes x in this module # vim: tabstop=4 expandtab shiftwidth=4 diff -r 5d7586cd3780 -r d161d4ac7dc0 tests/changed/modifier.py --- a/tests/changed/modifier.py Wed Jul 04 01:25:34 2012 +0200 +++ b/tests/changed/modifier.py Fri Jul 06 01:52:10 2012 +0200 @@ -4,6 +4,6 @@ # Change x in the partially imported module. -changed.x = 456 +changed.x = changed.D # vim: tabstop=4 expandtab shiftwidth=4 diff -r 5d7586cd3780 -r d161d4ac7dc0 tests/changed_globals.py --- a/tests/changed_globals.py Wed Jul 04 01:25:34 2012 +0200 +++ b/tests/changed_globals.py Fri Jul 06 01:52:10 2012 +0200 @@ -2,7 +2,7 @@ import changed -result1_456 = changed.x +result1_456 = changed.x.p result2_456 = changed.getx() # vim: tabstop=4 expandtab shiftwidth=4