Skip to content

Commit a6dccd4

Browse files
authored
Merge pull request #461 from vermaport/fix-raise
Fix raise
2 parents b7fd8a5 + b362804 commit a6dccd4

File tree

4 files changed

+236
-160
lines changed

4 files changed

+236
-160
lines changed

src/future/utils/__init__.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,34 @@ def raise_(tp, value=None, tb=None):
406406
allows re-raising exceptions with the cls value and traceback on
407407
Python 2 and 3.
408408
"""
409-
if value is not None and isinstance(tp, Exception):
410-
raise TypeError("instance exception may not have a separate value")
411-
if value is not None:
412-
exc = tp(value)
413-
else:
409+
if isinstance(tp, Exception):
410+
# If the first object is an instance, the type of the exception
411+
# is the class of the instance, the instance itself is the value,
412+
# and the second object must be None.
413+
if value is not None:
414+
raise TypeError("instance exception may not have a separate value")
414415
exc = tp
416+
elif not issubclass(tp, Exception):
417+
# If the first object is a class, it becomes the type of the
418+
# exception.
419+
raise TypeError("class must derive from Exception")
420+
else:
421+
# The second object is used to determine the exception value: If it
422+
# is an instance of the class, the instance becomes the exception
423+
# value. If the second object is a tuple, it is used as the argument
424+
# list for the class constructor; if it is None, an empty argument
425+
# list is used, and any other object is treated as a single argument
426+
# to the constructor. The instance so created by calling the
427+
# constructor is used as the exception value.
428+
if isinstance(value, tp):
429+
exc = value
430+
elif isinstance(value, tuple):
431+
exc = tp(*value)
432+
elif value is None:
433+
exc = tp()
434+
else:
435+
exc = tp(value)
436+
415437
if exc.__traceback__ is not tb:
416438
raise exc.with_traceback(tb)
417439
raise exc

src/libfuturize/fixes/fix_raise.py

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,39 @@
44
55
raise -> raise
66
raise E -> raise E
7-
raise E, V -> raise E(V)
7+
raise E, 5 -> raise E(5)
8+
raise E, 5, T -> raise E(5).with_traceback(T)
9+
raise E, None, T -> raise E.with_traceback(T)
810
9-
raise (((E, E'), E''), E'''), V -> raise E(V)
11+
raise (((E, E'), E''), E'''), 5 -> raise E(5)
12+
raise "foo", V, T -> warns about string exceptions
1013
14+
raise E, (V1, V2) -> raise E(V1, V2)
15+
raise E, (V1, V2), T -> raise E(V1, V2).with_traceback(T)
1116
12-
CAVEATS:
13-
1) "raise E, V" will be incorrectly translated if V is an exception
14-
instance. The correct Python 3 idiom is
1517
16-
raise E from V
18+
CAVEATS:
19+
1) "raise E, V, T" cannot be translated safely in general. If V
20+
is not a tuple or a (number, string, None) literal, then:
1721
18-
but since we can't detect instance-hood by syntax alone and since
19-
any client code would have to be changed as well, we don't automate
20-
this.
22+
raise E, V, T -> from future.utils import raise_
23+
raise_(E, V, T)
2124
"""
22-
# Author: Collin Winter, Armin Ronacher
25+
# Author: Collin Winter, Armin Ronacher, Mark Huang
2326

2427
# Local imports
2528
from lib2to3 import pytree, fixer_base
2629
from lib2to3.pgen2 import token
27-
from lib2to3.fixer_util import Name, Call, is_tuple
30+
from lib2to3.fixer_util import Name, Call, is_tuple, Comma, Attr, ArgList
31+
32+
from libfuturize.fixer_util import touch_import_top
33+
2834

2935
class FixRaise(fixer_base.BaseFix):
3036

3137
BM_compatible = True
3238
PATTERN = """
33-
raise_stmt< 'raise' exc=any [',' val=any] >
39+
raise_stmt< 'raise' exc=any [',' val=any [',' tb=any]] >
3440
"""
3541

3642
def transform(self, node, results):
@@ -55,19 +61,47 @@ def transform(self, node, results):
5561
exc = exc.children[1].children[0].clone()
5662
exc.prefix = u" "
5763

58-
if "val" not in results:
59-
# One-argument raise
60-
new = pytree.Node(syms.raise_stmt, [Name(u"raise"), exc])
61-
new.prefix = node.prefix
62-
return new
63-
64-
val = results["val"].clone()
65-
if is_tuple(val):
66-
args = [c.clone() for c in val.children[1:-1]]
64+
if "tb" in results:
65+
tb = results["tb"].clone()
66+
else:
67+
tb = None
68+
69+
if "val" in results:
70+
val = results["val"].clone()
71+
if is_tuple(val):
72+
# Assume that exc is a subclass of Exception and call exc(*val).
73+
args = [c.clone() for c in val.children[1:-1]]
74+
exc = Call(exc, args)
75+
elif val.type in (token.NUMBER, token.STRING):
76+
# Handle numeric and string literals specially, e.g.
77+
# "raise Exception, 5" -> "raise Exception(5)".
78+
val.prefix = u""
79+
exc = Call(exc, [val])
80+
elif val.type == token.NAME and val.value == u"None":
81+
# Handle None specially, e.g.
82+
# "raise Exception, None" -> "raise Exception".
83+
pass
84+
else:
85+
# val is some other expression. If val evaluates to an instance
86+
# of exc, it should just be raised. If val evaluates to None,
87+
# a default instance of exc should be raised (as above). If val
88+
# evaluates to a tuple, exc(*val) should be called (as
89+
# above). Otherwise, exc(val) should be called. We can only
90+
# tell what to do at runtime, so defer to future.utils.raise_(),
91+
# which handles all of these cases.
92+
touch_import_top(u"future.utils", u"raise_", node)
93+
exc.prefix = u""
94+
args = [exc, Comma(), val]
95+
if tb is not None:
96+
args += [Comma(), tb]
97+
return Call(Name(u"raise_"), args)
98+
99+
if tb is not None:
100+
tb.prefix = ""
101+
exc_list = Attr(exc, Name('with_traceback')) + [ArgList([tb])]
67102
else:
68-
val.prefix = u""
69-
args = [val]
103+
exc_list = [exc]
70104

71105
return pytree.Node(syms.raise_stmt,
72-
[Name(u"raise"), Call(exc, args)],
106+
[Name(u"raise")] + exc_list,
73107
prefix=node.prefix)

0 commit comments

Comments
 (0)