|
1 """Fixer for except statements with named exceptions. |
|
2 |
|
3 The following cases will be converted: |
|
4 |
|
5 - "except E, T:" where T is a name: |
|
6 |
|
7 except E as T: |
|
8 |
|
9 - "except E, T:" where T is not a name, tuple or list: |
|
10 |
|
11 except E as t: |
|
12 T = t |
|
13 |
|
14 This is done because the target of an "except" clause must be a |
|
15 name. |
|
16 |
|
17 - "except E, T:" where T is a tuple or list literal: |
|
18 |
|
19 except E as t: |
|
20 T = t.args |
|
21 """ |
|
22 # Author: Collin Winter |
|
23 |
|
24 # Local imports |
|
25 from .. import pytree |
|
26 from ..pgen2 import token |
|
27 from .. import fixer_base |
|
28 from ..fixer_util import Assign, Attr, Name, is_tuple, is_list |
|
29 |
|
30 def find_excepts(nodes): |
|
31 for i, n in enumerate(nodes): |
|
32 if isinstance(n, pytree.Node): |
|
33 if n.children[0].value == 'except': |
|
34 yield (n, nodes[i+2]) |
|
35 |
|
36 class FixExcept(fixer_base.BaseFix): |
|
37 |
|
38 PATTERN = """ |
|
39 try_stmt< 'try' ':' suite |
|
40 cleanup=(except_clause ':' suite)+ |
|
41 tail=(['except' ':' suite] |
|
42 ['else' ':' suite] |
|
43 ['finally' ':' suite]) > |
|
44 """ |
|
45 |
|
46 def transform(self, node, results): |
|
47 syms = self.syms |
|
48 |
|
49 tail = [n.clone() for n in results["tail"]] |
|
50 |
|
51 try_cleanup = [ch.clone() for ch in results["cleanup"]] |
|
52 for except_clause, e_suite in find_excepts(try_cleanup): |
|
53 if len(except_clause.children) == 4: |
|
54 (E, comma, N) = except_clause.children[1:4] |
|
55 comma.replace(Name("as", prefix=" ")) |
|
56 |
|
57 if N.type != token.NAME: |
|
58 # Generate a new N for the except clause |
|
59 new_N = Name(self.new_name(), prefix=" ") |
|
60 target = N.clone() |
|
61 target.set_prefix("") |
|
62 N.replace(new_N) |
|
63 new_N = new_N.clone() |
|
64 |
|
65 # Insert "old_N = new_N" as the first statement in |
|
66 # the except body. This loop skips leading whitespace |
|
67 # and indents |
|
68 #TODO(cwinter) suite-cleanup |
|
69 suite_stmts = e_suite.children |
|
70 for i, stmt in enumerate(suite_stmts): |
|
71 if isinstance(stmt, pytree.Node): |
|
72 break |
|
73 |
|
74 # The assignment is different if old_N is a tuple or list |
|
75 # In that case, the assignment is old_N = new_N.args |
|
76 if is_tuple(N) or is_list(N): |
|
77 assign = Assign(target, Attr(new_N, Name('args'))) |
|
78 else: |
|
79 assign = Assign(target, new_N) |
|
80 |
|
81 #TODO(cwinter) stopgap until children becomes a smart list |
|
82 for child in reversed(suite_stmts[:i]): |
|
83 e_suite.insert_child(0, child) |
|
84 e_suite.insert_child(i, assign) |
|
85 elif N.get_prefix() == "": |
|
86 # No space after a comma is legal; no space after "as", |
|
87 # not so much. |
|
88 N.set_prefix(" ") |
|
89 |
|
90 #TODO(cwinter) fix this when children becomes a smart list |
|
91 children = [c.clone() for c in node.children[:3]] + try_cleanup + tail |
|
92 return pytree.Node(node.type, children) |