1.1 --- a/TO_DO.txt Tue Aug 17 00:07:54 2010 +0200
1.2 +++ b/TO_DO.txt Fri Aug 20 01:43:51 2010 +0200
1.3 @@ -1,3 +1,5 @@
1.4 +Consider merging the InspectedModule.store tests with the scope conflict handling.
1.5 +
1.6 Support class attribute positioning similar to instance attribute positioning, potentially
1.7 (for both) based on usage observations. For example, if __iter__ is used on two classes,
1.8 the class attribute could be exposed at a similar relative position to the class (and
2.1 --- a/micropython/data.py Tue Aug 17 00:07:54 2010 +0200
2.2 +++ b/micropython/data.py Fri Aug 20 01:43:51 2010 +0200
2.3 @@ -229,7 +229,7 @@
2.4 self.module.set(name, value, 0)
2.5 else:
2.6 self._set(name, value, single_assignment)
2.7 - self.note_scope(name, "local")
2.8 + self.define_scope(name, "local")
2.9
2.10 def set_module(self, name, value):
2.11
2.12 @@ -317,7 +317,7 @@
2.13
2.14 if not self.namespace.has_key(name):
2.15 self.globals.add(name)
2.16 - self.note_scope(name, "global")
2.17 + self.define_scope(name, "global")
2.18 return 1
2.19 else:
2.20 return 0
2.21 @@ -532,7 +532,13 @@
2.22 self.scope_usage.append(scope_usage)
2.23
2.24 def _abandon_branch(self):
2.25 - pass
2.26 +
2.27 + """
2.28 + Abandon scope usage, permitting locally different scopes for names,
2.29 + provided these cannot "escape" from the branch.
2.30 + """
2.31 +
2.32 + self.scope_usage[-1] = AbandonedBranchScope()
2.33
2.34 def _shelve_branch(self):
2.35
2.36 @@ -613,6 +619,11 @@
2.37 elif scope_usage.has_key(name):
2.38 scope = scope_usage[name]
2.39
2.40 + # For abandoned branches, no scope is asserted for a name.
2.41 +
2.42 + elif isinstance(shelved_usage, AbandonedBranchScope):
2.43 + scope = None
2.44 +
2.45 # If no scope is recorded, find a suitable external source.
2.46
2.47 else:
2.48 @@ -624,15 +635,36 @@
2.49 if not new_scope_usage.has_key(name):
2.50 new_scope_usage[name] = scope
2.51 elif new_scope_usage[name] != scope:
2.52 - raise InspectError("Scope conflict for %r: defined as both %s and %s." % (name, new_scope_usage[name], scope))
2.53 + new_scope_usage[name] = ScopeConflict(scope, new_scope_usage[name])
2.54 +
2.55 + self.scope_usage[-1] = new_scope_usage
2.56
2.57 # Scope usage methods.
2.58
2.59 + def define_scope(self, name, scope):
2.60 +
2.61 + """
2.62 + Define 'name' as being from the given 'scope' in the current namespace.
2.63 + """
2.64 +
2.65 + self.scope_usage[-1][name] = scope
2.66 +
2.67 def note_scope(self, name, scope):
2.68
2.69 - "Note usage of 'name' from the given 'scope' in the current namespace."
2.70 + """
2.71 + Note usage of 'name' from the given 'scope' in the current namespace.
2.72 + If a conflict has been recorded previously, raise an exception.
2.73 + """
2.74 +
2.75 + scope_usage = self.scope_usage[-1]
2.76
2.77 - self.scope_usage[-1][name] = scope
2.78 + if scope_usage.has_key(name):
2.79 + found_scope = scope_usage[name]
2.80 + if isinstance(found_scope, ScopeConflict):
2.81 + raise InspectError("Scope conflict for %r: defined as both %s and %s." % (
2.82 + name, found_scope.old_scope, found_scope.new_scope))
2.83 +
2.84 + scope_usage[name] = scope
2.85
2.86 def used_in_scope(self, name, scope):
2.87
2.88 @@ -644,6 +676,43 @@
2.89 scope_usage = self.scope_usage[-1]
2.90 return scope_usage.get(name) == scope
2.91
2.92 +# Special helper classes for scope resolution.
2.93 +
2.94 +class AbandonedBranchScope:
2.95 +
2.96 + """
2.97 + A class providing a value or state for an abandoned branch distinct from an
2.98 + empty scope dictionary.
2.99 + """
2.100 +
2.101 + def has_key(self, name):
2.102 + return 0
2.103 +
2.104 + def __setitem__(self, name, value):
2.105 + pass
2.106 +
2.107 + def __getitem__(self, name):
2.108 + raise KeyError, name
2.109 +
2.110 + def get(self, name, default=None):
2.111 + return default
2.112 +
2.113 + def keys(self):
2.114 + return []
2.115 +
2.116 + values = items = keys
2.117 +
2.118 +class ScopeConflict:
2.119 +
2.120 + """
2.121 + A scope conflict caused when different code branches contribute different
2.122 + sources of names.
2.123 + """
2.124 +
2.125 + def __init__(self, old_scope, new_scope):
2.126 + self.old_scope = old_scope
2.127 + self.new_scope = new_scope
2.128 +
2.129 # Program data structures.
2.130
2.131 class Attr:
3.1 --- a/micropython/inspect.py Tue Aug 17 00:07:54 2010 +0200
3.2 +++ b/micropython/inspect.py Fri Aug 20 01:43:51 2010 +0200
3.3 @@ -739,6 +739,7 @@
3.4 self.new_branch()
3.5 self.dispatch(node.body)
3.6 self.shelve_branch()
3.7 +
3.8 self.in_loop = in_loop
3.9
3.10 # Maintain a branch for the else clause or the current retained usage
3.11 @@ -994,10 +995,10 @@
3.12
3.13 self.shelve_branch()
3.14
3.15 + self.new_branch()
3.16 if node.else_ is not None:
3.17 - self.new_branch()
3.18 self.dispatch(node.else_)
3.19 - self.shelve_branch()
3.20 + self.shelve_branch()
3.21
3.22 self.merge_branches()
3.23 return None
4.1 --- a/test_all.py Tue Aug 17 00:07:54 2010 +0200
4.2 +++ b/test_all.py Fri Aug 20 01:43:51 2010 +0200
4.3 @@ -49,14 +49,15 @@
4.4 else:
4.5 rm = rsvp.machine(p)
4.6 success = rm.test(m)
4.7 - print "Test successful?", success
4.8 + print "Test successful?", success and "Yes" or "No"
4.9 results.append((filename, success))
4.10
4.11 - print
4.12 - print "Failed tests:"
4.13 - for result in results:
4.14 - if not result[1]:
4.15 - print result[0]
4.16 + failed = [result[0] for result in results if not result[1]]
4.17 + if failed:
4.18 + print
4.19 + print "Failed tests:"
4.20 + for filename in failed:
4.21 + print filename
4.22
4.23 print
4.24 print "All successful?"
5.1 --- a/tests/failure/shadow_globals_builtins_conflict.py Tue Aug 17 00:07:54 2010 +0200
5.2 +++ b/tests/failure/shadow_globals_builtins_conflict.py Fri Aug 20 01:43:51 2010 +0200
5.3 @@ -8,4 +8,6 @@
5.4 def len(arg):
5.5 return 0
5.6
5.7 +z = len(x)
5.8 +
5.9 # vim: tabstop=4 expandtab shiftwidth=4
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/tests/failure/shadow_locals_builtins_conflict.py Fri Aug 20 01:43:51 2010 +0200
6.3 @@ -0,0 +1,15 @@
6.4 +#!/usr/bin/env python
6.5 +
6.6 +def test():
6.7 + x = [1, 2, 3]
6.8 + if x:
6.9 + y = len(x)
6.10 + else:
6.11 + def len(arg):
6.12 + return 0
6.13 +
6.14 + return len(x)
6.15 +
6.16 +test()
6.17 +
6.18 +# vim: tabstop=4 expandtab shiftwidth=4