1.1 --- a/annotate.py Sun Nov 26 15:45:53 2006 +0100
1.2 +++ b/annotate.py Sun Nov 26 21:28:11 2006 +0100
1.3 @@ -234,27 +234,52 @@
1.4 # With Return nodes inside the body/else sections, the changes are
1.5 # communicated to the caller.
1.6
1.7 - conditional.test = self.dispatch(conditional.test)
1.8 - saved_namespace = self.namespace
1.9 - is_global = self.namespace is self.global_namespace
1.10 + is_module = self.namespace is self.module.namespace
1.11 +
1.12 + # Where the test is closely associated with the body, save the namespace
1.13 + # before entering the test.
1.14 +
1.15 + if conditional.isolate_test:
1.16 + saved_namespace = self.namespace
1.17 + self.namespace = Namespace()
1.18 + if is_module:
1.19 + self.module.namespace = self.namespace
1.20 + self.namespace.merge_namespace(saved_namespace)
1.21
1.22 - self.namespace = Namespace()
1.23 - if is_global:
1.24 - self.module.namespace = self.global_namespace = self.namespace
1.25 - self.namespace.merge_namespace(saved_namespace)
1.26 + conditional.test = self.dispatch(conditional.test)
1.27 +
1.28 + # Where the test may affect the body and the else clause, save the
1.29 + # namespace after processing the test.
1.30 +
1.31 + if not conditional.isolate_test:
1.32 + saved_namespace = self.namespace
1.33 + self.namespace = Namespace()
1.34 + if is_module:
1.35 + self.module.namespace = self.namespace
1.36 + self.namespace.merge_namespace(saved_namespace)
1.37 +
1.38 + # Process the body clause.
1.39 +
1.40 conditional.body = self.dispatches(conditional.body)
1.41 body_namespace = self.namespace
1.42
1.43 + # Use the saved namespace as a template for the else clause.
1.44 +
1.45 self.namespace = Namespace()
1.46 - if is_global:
1.47 - self.module.namespace = self.global_namespace = self.namespace
1.48 + if is_module:
1.49 + self.module.namespace = self.namespace
1.50 self.namespace.merge_namespace(saved_namespace)
1.51 +
1.52 + # Process the else clause.
1.53 +
1.54 conditional.else_ = self.dispatches(conditional.else_)
1.55 else_namespace = self.namespace
1.56
1.57 + # Merge the body and else namespaces.
1.58 +
1.59 self.namespace = Namespace()
1.60 - if is_global:
1.61 - self.module.namespace = self.global_namespace = self.namespace
1.62 + if is_module:
1.63 + self.module.namespace = self.namespace
1.64 self.namespace.merge_namespace(body_namespace)
1.65 self.namespace.merge_namespace(else_namespace)
1.66
1.67 @@ -272,21 +297,74 @@
1.68 return subprogram
1.69
1.70 def visitTry(self, try_):
1.71 + is_module = self.namespace is self.module.namespace
1.72 +
1.73 try_.body = self.dispatches(try_.body)
1.74 - try_.handler = self.dispatches(try_.handler)
1.75 +
1.76 + # Save the namespace from the body.
1.77 +
1.78 + body_namespace = Namespace()
1.79 + body_namespace.merge_namespace(self.namespace)
1.80 +
1.81 + # Process the handler.
1.82 +
1.83 + if hasattr(try_, "handler"):
1.84 + try_.handler = self.dispatches(try_.handler)
1.85 +
1.86 + # Save the namespace from the handler.
1.87 +
1.88 + handler_namespace = Namespace()
1.89 + handler_namespace.merge_namespace(self.namespace)
1.90 +
1.91 + # Remember the raised exceptions encountered so far.
1.92 +
1.93 raises = self.namespace.raises
1.94
1.95 - # Empty the raised exceptions for the else clause.
1.96 + # Process the else clause.
1.97 +
1.98 + if hasattr(try_, "else_"):
1.99 +
1.100 + # Restore the body namespace for the else clause.
1.101 +
1.102 + self.namespace = body_namespace
1.103 + if is_module:
1.104 + self.module.namespace = self.namespace
1.105 +
1.106 + # Empty the raised exceptions for the else clause.
1.107
1.108 - self.namespace.raises = []
1.109 - try_.else_ = self.dispatches(try_.else_)
1.110 - self.namespace.raises = raises
1.111 + self.namespace.raises = []
1.112 + try_.else_ = self.dispatches(try_.else_)
1.113 + self.namespace.raises = raises
1.114 +
1.115 + # Merge the namespaces.
1.116 +
1.117 + self.namespace = Namespace()
1.118 + if is_module:
1.119 + self.module.namespace = self.namespace
1.120 + self.namespace.merge_namespace(body_namespace)
1.121 + self.namespace.merge_namespace(handler_namespace)
1.122 +
1.123 + # Process the finally clause, if any.
1.124
1.125 try_.finally_ = self.dispatches(try_.finally_)
1.126 return try_
1.127
1.128 # Namespace operations.
1.129
1.130 + def visitCheckExc(self, checkexc):
1.131 + checkexc.expr = self.dispatch(checkexc.expr)
1.132 + expr_types = self.namespace.types
1.133 + choice_types = []
1.134 + choices = []
1.135 + for choice in checkexc.choices:
1.136 + choices.append(self.dispatch(choice))
1.137 + choice_types += self.namespace.types
1.138 + for expr_type in expr_types:
1.139 + if expr_type.type.get_class() not in choice_types:
1.140 + print "CheckExc", expr_type, "should be revoked!"
1.141 + self._prune_non_accesses(checkexc.expr, expr_type)
1.142 + return checkexc
1.143 +
1.144 def visitLoadAttr(self, loadattr):
1.145 loadattr.expr = self.dispatch(loadattr.expr)
1.146 types = []
1.147 @@ -300,7 +378,7 @@
1.148
1.149 # Revoke this type from any name involved.
1.150
1.151 - self._prune_non_accesses(loadattr, attr)
1.152 + self._prune_non_accesses(loadattr.expr, attr)
1.153
1.154 for attribute, accessor in attributes:
1.155 if attribute is not None:
1.156 @@ -315,7 +393,7 @@
1.157
1.158 # Revoke this type from any name involved.
1.159
1.160 - self._prune_non_accesses(loadattr, attr)
1.161 + self._prune_non_accesses(loadattr.expr, attr)
1.162
1.163 if not types:
1.164 print "No attribute found for", loadattr.name, "given", self.namespace.types
1.165 @@ -325,13 +403,15 @@
1.166 self.annotate(loadattr)
1.167 return loadattr
1.168
1.169 - def _prune_non_accesses(self, loadattr, attr):
1.170 - if isinstance(loadattr.expr, LoadName):
1.171 - self.namespace.revoke(loadattr.expr.name, attr)
1.172 - elif isinstance(loadattr.expr, LoadAttr):
1.173 - for expr_attr in loadattr.expr.expr.types:
1.174 + def _prune_non_accesses(self, expr, attr):
1.175 + if isinstance(expr, LoadName):
1.176 + self.namespace.revoke(expr.name, attr)
1.177 + elif isinstance(expr, LoadAttr):
1.178 + for expr_attr in expr.expr.types:
1.179 if hasattr(expr_attr.type, "namespace"):
1.180 - expr_attr.type.namespace.revoke(loadattr.expr.name, attr)
1.181 + expr_attr.type.namespace.revoke(expr.name, attr)
1.182 + elif isinstance(expr, LoadExc):
1.183 + self.namespace.revoke_exception_type(attr)
1.184
1.185 def visitLoadExc(self, loadexc):
1.186 self.namespace.types = self.namespace.raises[:]
1.187 @@ -887,6 +967,9 @@
1.188 def revoke(self, name, type):
1.189 self.names[name].remove(type)
1.190
1.191 + def revoke_exception_type(self, type):
1.192 + self.raises.remove(type)
1.193 +
1.194 def merge_namespace(self, namespace, no_return_locals=0):
1.195 self.merge_items(namespace.names.items())
1.196 combine(self.returns, namespace.returns)
2.1 --- a/simplified.py Sun Nov 26 15:45:53 2006 +0100
2.2 +++ b/simplified.py Sun Nov 26 21:28:11 2006 +0100
2.3 @@ -233,17 +233,6 @@
2.4 if getattr(self, "structure", 0):
2.5 self._pprint(indent + 2, "( ", "structure '%s'" % self.structure.name, stream=stream)
2.6
2.7 - # Statement-related details.
2.8 -
2.9 - if hasattr(self, "test"):
2.10 - self.test.pprint(indent + 2, "? ", stream=stream)
2.11 - for attr in "code", "body", "else_", "handler", "finally_", "choices":
2.12 - if hasattr(self, attr) and getattr(self, attr):
2.13 - self._pprint(indent, "", "%s {" % attr, stream=stream)
2.14 - for node in getattr(self, attr):
2.15 - node.pprint(indent + 2, stream=stream)
2.16 - self._pprint(indent, "", "}", stream=stream)
2.17 -
2.18 # Expression-related details.
2.19
2.20 if hasattr(self, "expr"):
2.21 @@ -265,6 +254,17 @@
2.22 if hasattr(self, "dstar") and self.dstar:
2.23 self.dstar.pprint(indent + 2, "( ", stream=stream)
2.24
2.25 + # Statement-related details.
2.26 +
2.27 + if hasattr(self, "test"):
2.28 + self.test.pprint(indent + 2, "? ", stream=stream)
2.29 + for attr in "code", "body", "else_", "handler", "finally_", "choices":
2.30 + if hasattr(self, attr) and getattr(self, attr):
2.31 + self._pprint(indent, "", "%s {" % attr, stream=stream)
2.32 + for node in getattr(self, attr):
2.33 + node.pprint(indent + 2, stream=stream)
2.34 + self._pprint(indent, "", "}", stream=stream)
2.35 +
2.36 # Annotations.
2.37
2.38 if hasattr(self, "accesses"):
2.39 @@ -291,16 +291,27 @@
2.40 class LoadAttr(Node): "Load an object attribute."
2.41 class LoadRef(Node): "Load a reference, typically a subprogram or a constant."
2.42 class LoadExc(Node): "Load a handled exception."
2.43 +class CheckExc(Node): "Check a handled exception."
2.44 class StoreTemp(Node): "Store a temporary value."
2.45 class StoreName(Node): "Associate a name with an object."
2.46 class StoreAttr(Node): "Associate an object's attribute with a value."
2.47 class ReleaseTemp(Node): "Release a temporary value."
2.48 -class Conditional(Node): "A conditional node consisting of a test and outcomes."
2.49 class Try(Node): "A try...except...else...finally grouping node."
2.50 class Raise(Node): "An exception raising node."
2.51 class Not(Node): "A negation of an expression."
2.52 class Invoke(Node): "An invocation."
2.53
2.54 +# Some behaviour is set as the default in conditional nodes but may be
2.55 +# overridden.
2.56 +
2.57 +class Conditional(Node):
2.58 +
2.59 + "A conditional node consisting of a test and outcomes."
2.60 +
2.61 + def __init__(self, *args, **kw):
2.62 + self.isolate_test = 0
2.63 + Node.__init__(self, *args, **kw)
2.64 +
2.65 # Invocations involve some more work to process calculated attributes.
2.66
2.67 class InvokeFunction(Invoke):
2.68 @@ -392,7 +403,10 @@
2.69 def full_name(self):
2.70 # NOTE: Wrap the result in a call to name(self, ...) where multiple
2.71 # NOTE: instances per class can occur.
2.72 - return self.namespace.load("__class__")[0].type._full_name
2.73 + return self.get_class()._full_name
2.74 +
2.75 + def get_class(self):
2.76 + return self.namespace.load("__class__")[0].type
2.77
2.78 def __repr__(self):
2.79 return "Instance of type '%s'" % self.full_name()
3.1 --- a/simplify.py Sun Nov 26 15:45:53 2006 +0100
3.2 +++ b/simplify.py Sun Nov 26 21:28:11 2006 +0100
3.3 @@ -352,13 +352,9 @@
3.4 # isinstance(<exc>, <spec>)
3.5
3.6 else:
3.7 - new_spec = self.dispatch(spec)
3.8 test = Conditional(
3.9 - test=InvokeFunction(
3.10 - expr=LoadName(name="isinstance"),
3.11 - args=[LoadExc(), new_spec],
3.12 - star=None,
3.13 - dstar=None)
3.14 + isolate_test=1,
3.15 + test=CheckExc(expr=LoadExc(), choices=self._visitTryExcept(spec))
3.16 )
3.17 test.body = []
3.18
3.19 @@ -390,6 +386,18 @@
3.20 result.handler = results
3.21 return result
3.22
3.23 + def _visitTryExcept(self, spec):
3.24 +
3.25 + "Return a list of nodes for the given exception type 'spec'."
3.26 +
3.27 + if isinstance(spec, compiler.ast.Tuple):
3.28 + nodes = []
3.29 + for node in spec.nodes:
3.30 + nodes += self._visitTryExcept(node)
3.31 + else:
3.32 + nodes = [self.dispatch(spec)]
3.33 + return nodes
3.34 +
3.35 comparison_methods = {
3.36 "==" : "__eq__", "!=" : "__ne__", "<" : "__lt__", "<=" : "__le__",
3.37 ">=" : "__ge__", ">" : "__gt__", "is" : None, "is not" : None
4.1 --- a/tests/tryexcept.py Sun Nov 26 15:45:53 2006 +0100
4.2 +++ b/tests/tryexcept.py Sun Nov 26 21:28:11 2006 +0100
4.3 @@ -18,5 +18,6 @@
4.4 a = f
4.5 else:
4.6 a = x
4.7 + return a
4.8
4.9 f()