Home | History | Annotate | Download | only in fixes
      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, syms
     29 
     30 def find_excepts(nodes):
     31     for i, n in enumerate(nodes):
     32         if n.type == syms.except_clause:
     33             if n.children[0].value == u'except':
     34                 yield (n, nodes[i+2])
     35 
     36 class FixExcept(fixer_base.BaseFix):
     37     BM_compatible = True
     38 
     39     PATTERN = """
     40     try_stmt< 'try' ':' (simple_stmt | suite)
     41                   cleanup=(except_clause ':' (simple_stmt | suite))+
     42                   tail=(['except' ':' (simple_stmt | suite)]
     43                         ['else' ':' (simple_stmt | suite)]
     44                         ['finally' ':' (simple_stmt | suite)]) >
     45     """
     46 
     47     def transform(self, node, results):
     48         syms = self.syms
     49 
     50         tail = [n.clone() for n in results["tail"]]
     51 
     52         try_cleanup = [ch.clone() for ch in results["cleanup"]]
     53         for except_clause, e_suite in find_excepts(try_cleanup):
     54             if len(except_clause.children) == 4:
     55                 (E, comma, N) = except_clause.children[1:4]
     56                 comma.replace(Name(u"as", prefix=u" "))
     57 
     58                 if N.type != token.NAME:
     59                     # Generate a new N for the except clause

     60                     new_N = Name(self.new_name(), prefix=u" ")
     61                     target = N.clone()
     62                     target.prefix = u""
     63                     N.replace(new_N)
     64                     new_N = new_N.clone()
     65 
     66                     # Insert "old_N = new_N" as the first statement in

     67                     #  the except body. This loop skips leading whitespace

     68                     #  and indents

     69                     #TODO(cwinter) suite-cleanup

     70                     suite_stmts = e_suite.children
     71                     for i, stmt in enumerate(suite_stmts):
     72                         if isinstance(stmt, pytree.Node):
     73                             break
     74 
     75                     # The assignment is different if old_N is a tuple or list

     76                     # In that case, the assignment is old_N = new_N.args

     77                     if is_tuple(N) or is_list(N):
     78                         assign = Assign(target, Attr(new_N, Name(u'args')))
     79                     else:
     80                         assign = Assign(target, new_N)
     81 
     82                     #TODO(cwinter) stopgap until children becomes a smart list

     83                     for child in reversed(suite_stmts[:i]):
     84                         e_suite.insert_child(0, child)
     85                     e_suite.insert_child(i, assign)
     86                 elif N.prefix == u"":
     87                     # No space after a comma is legal; no space after "as",

     88                     # not so much.

     89                     N.prefix = u" "
     90 
     91         #TODO(cwinter) fix this when children becomes a smart list

     92         children = [c.clone() for c in node.children[:3]] + try_cleanup + tail
     93         return pytree.Node(node.type, children)
     94