# HG changeset patch # User Paul Boddie # Date 1340925062 -7200 # Node ID a18af4a45b7466e123591998856bebf196f7fb93 # Parent 875b09b3d3559964eecd81066a1eb0290a64f9f2 Added support for module attributes that can be assigned (rebound) externally. Added a test demonstrating external module attribute modification. Updated the documentation to note the consequences of less restrictive module attribute definition. diff -r 875b09b3d355 -r a18af4a45b74 TO_DO.txt --- a/TO_DO.txt Thu Jun 28 22:58:02 2012 +0200 +++ b/TO_DO.txt Fri Jun 29 01:11:02 2012 +0200 @@ -26,6 +26,10 @@ Class and Module Attribute Assignment ===================================== +Allow unrestricted class and module assignment (but not new external binding of +attributes), eliminating run-time checks on object types in instructions like +StoreAttrIndex. This may involve less specific objects being identified during inspection. + Verify that the context information is correctly set, particularly for the unoptimised cases. diff -r 875b09b3d355 -r a18af4a45b74 docs/assignment.txt --- a/docs/assignment.txt Thu Jun 28 22:58:02 2012 +0200 +++ b/docs/assignment.txt Fri Jun 29 01:11:02 2012 +0200 @@ -40,6 +40,8 @@ class -> class StoreAddressContext StoreAttrIndex Assignment of new class attributes is only permitted during initialisation + unless attribute usage observations are employed to detect possible attribute + assignments Access types: diff -r 875b09b3d355 -r a18af4a45b74 docs/concepts.txt --- a/docs/concepts.txt Thu Jun 28 22:58:02 2012 +0200 +++ b/docs/concepts.txt Fri Jun 29 01:11:02 2012 +0200 @@ -74,23 +74,23 @@ completely imported (and thus defined) before an import statement can make it available to other modules. Consider the following package root definition: - # Module test: + # Module changed: def f(): - import test.modifier + import changed.modifier x = 123 f() - # Module test.modifier: - import test - test.x = 456 + # Module changed.modifier: + import changed + changed.x = 456 -Here, an import of test will initially set x to 123, but then the function f -will be invoked and cause the test.modifier module to be imported. Since the -test module is already being imported, the import statement will not try to -perform the import operation again, but it will make the partially defined -module available for access. Thus, the test.modifier module will then set x to -456, and independent modification of the test namespace will have been -performed. +Here, an import of changed will initially set x to 123, but then the function +f will be invoked and cause the changed.modifier module to be imported. Since +the changed module is already being imported, the import statement will not +try to perform the import operation again, but it will make the partially +defined module available for access. Thus, the changed.modifier module will +then set x to 456, and independent modification of the changed namespace will +have been performed. In conclusion, module globals cannot therefore be regarded as immune to operations that would disrupt usage observations. Consequently, only locals diff -r 875b09b3d355 -r a18af4a45b74 docs/optimisations.txt --- a/docs/optimisations.txt Thu Jun 28 22:58:02 2012 +0200 +++ b/docs/optimisations.txt Fri Jun 29 01:11:02 2012 +0200 @@ -36,6 +36,11 @@ In the above, this_module is a reference to the current module. + Where assignments to module attributes are permitted from outside a + module, modules can no longer be relied upon to provide constant attribute + values, even if a cursory inspection of module-level code would suggest + that some attributes are effectively constant. + 2. For classes, "safety" is enforced by ensuring that assignments to class attributes are only permitted within the class definition, outside methods. This would mean that classes would be "sealed" at definition time diff -r 875b09b3d355 -r a18af4a45b74 docs/rationale.txt --- a/docs/rationale.txt Thu Jun 28 22:58:02 2012 +0200 +++ b/docs/rationale.txt Fri Jun 29 01:11:02 2012 +0200 @@ -110,6 +110,7 @@ * Classes and functions define module attributes * Outside the module: * Attributes can be accessed but not set + * This can be relaxed with attribute usage observations * Definition from within means more predictable content Micropython classes @@ -120,6 +121,7 @@ * Functions define methods * Outside the class: * Attributes can be accessed but not set + * This can be relaxed with attribute usage observations * Definition from within means more predictable content Micropython instances diff -r 875b09b3d355 -r a18af4a45b74 micropython/inspect.py --- a/micropython/inspect.py Thu Jun 28 22:58:02 2012 +0200 +++ b/micropython/inspect.py Fri Jun 29 01:11:02 2012 +0200 @@ -62,8 +62,8 @@ assignments to provide non-constant values. Assignments to names are only really considered to cause the targets of such -assignments to provide constant values if the targets reside in the module -namespace or in class namespaces, subject to the above conditions. +assignments to provide constant values if the targets reside in class +namespaces, subject to the above conditions. Assignments to names within functions are not generally considered to cause the targets of such assignments to provide constant values since functions can be @@ -370,7 +370,7 @@ return (self.namespaces[-1:] or [self])[0] - def use_name(self, name, node=None, value=None): + def use_name(self, name, node=None, value=None, ns=None): """ Use the given 'name' within the current namespace/unit, either in @@ -378,14 +378,17 @@ None) or unconditionally. """ - if node is not None and isinstance(node, compiler.ast.Name): + unit = self.get_namespace() + + # Handle attribute usage situations within the current unit. + + if node is not None and isinstance(node, compiler.ast.Name) and ns is unit: self.use_attribute(node.name, name, value) # For general name usage, declare usage of the given name from this # particular unit. else: - unit = self.get_namespace() self.importer.use_name(name, unit.full_name(), value) def use_constant(self, const): @@ -546,7 +549,7 @@ # Get the attribute and record its usage. - if isinstance(value, (Class, Module)): + if isinstance(value, Class): # Check for class.__class__. @@ -600,7 +603,7 @@ if expr.parent is self.get_namespace() and not self.get_namespace() is self: self.define_attribute_accessor(expr.name, attrname, node, value) else: - self.use_name(attrname, node.expr, value) + self.use_name(attrname, node.expr, value, ns=expr.parent) def _visitConst(self, value): diff -r 875b09b3d355 -r a18af4a45b74 tests/changed/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/changed/__init__.py Fri Jun 29 01:11:02 2012 +0200 @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +def f(): + import changed.modifier + +def getx(): + return x + +x = 123 +f() # import modifier which changes x in this module + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 875b09b3d355 -r a18af4a45b74 tests/changed/modifier.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/changed/modifier.py Fri Jun 29 01:11:02 2012 +0200 @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +import changed # access the package root + +# Change x in the partially imported module. + +changed.x = 456 + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 875b09b3d355 -r a18af4a45b74 tests/changed_globals.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/changed_globals.py Fri Jun 29 01:11:02 2012 +0200 @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +import changed + +result1_456 = changed.x +result2_456 = changed.getx() + +# vim: tabstop=4 expandtab shiftwidth=4