# HG changeset patch # User paulb@localhost.localdomain # Date 1164552353 -3600 # Node ID f991da4a64a49ef2e86f1fcfcf6c330a3628a46c # Parent c835a3579518c42182d69756258520b666d4c5c8 Simplified the Annotator's process_node function signature. Introduced module namespace updates so that access to module globals always goes through the current top-level namespace. Changed the namespace merging to only update returned locals within blocks, not to propagate such things between blocks. Made the returned locals completely override the caller locals when returning from a block which shares locals with the caller. Fixed parameter types so that they are updated, not overwritten. Updated comments and tests. diff -r c835a3579518 -r f991da4a64a4 annotate.py --- a/annotate.py Sat Nov 25 03:07:54 2006 +0100 +++ b/annotate.py Sun Nov 26 15:45:53 2006 +0100 @@ -141,12 +141,12 @@ else: self.builtins_namespace = self.global_namespace - return self.process_node(module) + return self.process_node(module, self.global_namespace) - def process_node(self, node, locals=None): + def process_node(self, node, locals): """ - Process a subprogram or module 'node', indicating any initial 'locals'. + Process a subprogram or module 'node', indicating the initial 'locals'. Return an annotated subprogram or module. Note that this method may mutate nodes in the original program. """ @@ -157,11 +157,8 @@ # Determine the namespace. - if locals is not None: - self.current_namespaces.append(self.namespace) - self.namespace = locals - else: - self.namespace = self.global_namespace + self.current_namespaces.append(self.namespace) + self.namespace = locals # Add namespace details to any structure involved. @@ -191,8 +188,7 @@ # Restore the previous subprogram and namespace. - if locals is not None: - self.namespace = self.current_namespaces.pop() + self.namespace = self.current_namespaces.pop() self.current_subprograms.pop() return result @@ -610,14 +606,22 @@ if getattr(invoke, "share_locals", 0): namespace = Namespace() - namespace.merge_namespace(self.namespace) + namespace.merge_namespace(self.namespace, no_return_locals=1) + using_module_namespace = self.namespace is self.module.namespace elif getattr(target, "structure", None): namespace = Namespace() + using_module_namespace = 0 else: items = self.make_items(invoke, target, context) namespace = self.make_namespace(items) + using_module_namespace = 0 # Process the subprogram. + # In order to keep global accesses working, the module namespace must be + # adjusted. + + if using_module_namespace: + self.module.namespace = namespace self.process_node(target, namespace) @@ -628,16 +632,26 @@ self.namespace.set_types(self.last_returns) self.annotate(invoke) - # Otherwise, assuming it is a normal block, merge the locals. + # If it is a normal block, merge the locals. + # This can happen in addition to the above because for things like + # logical expressions, the namespace can be modified whilst values are + # returned as results. - elif getattr(invoke, "share_locals", 0): + if getattr(invoke, "share_locals", 0): + self.namespace.reset() for locals in self.returned_locals: - self.namespace.merge_namespace(locals) + self.namespace.merge_namespace(locals, no_return_locals=1) # Incorporate any raised exceptions. combine(self.namespace.raises, self.last_raises) + # In order to keep global accesses working, the module namespace must be + # adjusted. + + if using_module_namespace: + self.module.namespace = self.namespace + def process_args(self, invocation): """ @@ -759,9 +773,13 @@ # Record the parameter types. - subprogram.paramtypes = {} + if not hasattr(subprogram, "paramtypes"): + subprogram.paramtypes = {} + for param, types in items: - subprogram.paramtypes[param] = types + if not subprogram.paramtypes.has_key(param): + subprogram.paramtypes[param] = [] + combine(subprogram.paramtypes[param], types) return items @@ -867,12 +885,13 @@ __getitem__ = load def revoke(self, name, type): - print "Revoke", type, "from", name self.names[name].remove(type) - def merge_namespace(self, namespace): + def merge_namespace(self, namespace, no_return_locals=0): self.merge_items(namespace.names.items()) combine(self.returns, namespace.returns) + if not no_return_locals: + combine(self.return_locals, namespace.return_locals) combine(self.raises, namespace.raises) self.temp = {} self.temp.update(namespace.temp) @@ -896,6 +915,12 @@ namespace.merge_namespace(self) self.return_locals.append(namespace) + def reset(self): + + "Reset a namespace in preparation for merging with returned locals." + + self.names = {} + def __repr__(self): return repr(self.names) diff -r c835a3579518 -r f991da4a64a4 simplify.py --- a/simplify.py Sat Nov 25 03:07:54 2006 +0100 +++ b/simplify.py Sun Nov 26 15:45:53 2006 +0100 @@ -1059,7 +1059,7 @@ expr=LoadRef(ref=structure) ), InvokeBlock( - share_locals=0, + share_locals=0, # override the local sharing usually in InvokeBlock expr=LoadRef(ref=subprogram) ) ] diff -r c835a3579518 -r f991da4a64a4 tests/logical_attr.py --- a/tests/logical_attr.py Sat Nov 25 03:07:54 2006 +0100 +++ b/tests/logical_attr.py Sun Nov 26 15:45:53 2006 +0100 @@ -9,4 +9,9 @@ class D(X): y = True a = X() and Y() or B() and not C() or D() -b = a.x and a.y or a.b and not a.c +if a: + b = a.x and a.y or a.b and not a.c + c = a +else: + d = a +e = a