comparison env/lib/python3.7/site-packages/libpasteurize/fixes/fix_kwargs.py @ 0:26e78fe6e8c4 draft

"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author shellac
date Sat, 02 May 2020 07:14:21 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:26e78fe6e8c4
1 u"""
2 Fixer for Python 3 function parameter syntax
3 This fixer is rather sensitive to incorrect py3k syntax.
4 """
5
6 # Note: "relevant" parameters are parameters following the first STAR in the list.
7
8 from lib2to3 import fixer_base
9 from lib2to3.fixer_util import token, String, Newline, Comma, Name
10 from libfuturize.fixer_util import indentation, suitify, DoubleStar
11
12 _assign_template = u"%(name)s = %(kwargs)s['%(name)s']; del %(kwargs)s['%(name)s']"
13 _if_template = u"if '%(name)s' in %(kwargs)s: %(assign)s"
14 _else_template = u"else: %(name)s = %(default)s"
15 _kwargs_default_name = u"_3to2kwargs"
16
17 def gen_params(raw_params):
18 u"""
19 Generator that yields tuples of (name, default_value) for each parameter in the list
20 If no default is given, then it is default_value is None (not Leaf(token.NAME, 'None'))
21 """
22 assert raw_params[0].type == token.STAR and len(raw_params) > 2
23 curr_idx = 2 # the first place a keyword-only parameter name can be is index 2
24 max_idx = len(raw_params)
25 while curr_idx < max_idx:
26 curr_item = raw_params[curr_idx]
27 prev_item = curr_item.prev_sibling
28 if curr_item.type != token.NAME:
29 curr_idx += 1
30 continue
31 if prev_item is not None and prev_item.type == token.DOUBLESTAR:
32 break
33 name = curr_item.value
34 nxt = curr_item.next_sibling
35 if nxt is not None and nxt.type == token.EQUAL:
36 default_value = nxt.next_sibling
37 curr_idx += 2
38 else:
39 default_value = None
40 yield (name, default_value)
41 curr_idx += 1
42
43 def remove_params(raw_params, kwargs_default=_kwargs_default_name):
44 u"""
45 Removes all keyword-only args from the params list and a bare star, if any.
46 Does not add the kwargs dict if needed.
47 Returns True if more action is needed, False if not
48 (more action is needed if no kwargs dict exists)
49 """
50 assert raw_params[0].type == token.STAR
51 if raw_params[1].type == token.COMMA:
52 raw_params[0].remove()
53 raw_params[1].remove()
54 kw_params = raw_params[2:]
55 else:
56 kw_params = raw_params[3:]
57 for param in kw_params:
58 if param.type != token.DOUBLESTAR:
59 param.remove()
60 else:
61 return False
62 else:
63 return True
64
65 def needs_fixing(raw_params, kwargs_default=_kwargs_default_name):
66 u"""
67 Returns string with the name of the kwargs dict if the params after the first star need fixing
68 Otherwise returns empty string
69 """
70 found_kwargs = False
71 needs_fix = False
72
73 for t in raw_params[2:]:
74 if t.type == token.COMMA:
75 # Commas are irrelevant at this stage.
76 continue
77 elif t.type == token.NAME and not found_kwargs:
78 # Keyword-only argument: definitely need to fix.
79 needs_fix = True
80 elif t.type == token.NAME and found_kwargs:
81 # Return 'foobar' of **foobar, if needed.
82 return t.value if needs_fix else u''
83 elif t.type == token.DOUBLESTAR:
84 # Found either '*' from **foobar.
85 found_kwargs = True
86 else:
87 # Never found **foobar. Return a synthetic name, if needed.
88 return kwargs_default if needs_fix else u''
89
90 class FixKwargs(fixer_base.BaseFix):
91
92 run_order = 7 # Run after function annotations are removed
93
94 PATTERN = u"funcdef< 'def' NAME parameters< '(' arglist=typedargslist< params=any* > ')' > ':' suite=any >"
95
96 def transform(self, node, results):
97 params_rawlist = results[u"params"]
98 for i, item in enumerate(params_rawlist):
99 if item.type == token.STAR:
100 params_rawlist = params_rawlist[i:]
101 break
102 else:
103 return
104 # params is guaranteed to be a list starting with *.
105 # if fixing is needed, there will be at least 3 items in this list:
106 # [STAR, COMMA, NAME] is the minimum that we need to worry about.
107 new_kwargs = needs_fixing(params_rawlist)
108 # new_kwargs is the name of the kwargs dictionary.
109 if not new_kwargs:
110 return
111 suitify(node)
112
113 # At this point, params_rawlist is guaranteed to be a list
114 # beginning with a star that includes at least one keyword-only param
115 # e.g., [STAR, NAME, COMMA, NAME, COMMA, DOUBLESTAR, NAME] or
116 # [STAR, COMMA, NAME], or [STAR, COMMA, NAME, COMMA, DOUBLESTAR, NAME]
117
118 # Anatomy of a funcdef: ['def', 'name', parameters, ':', suite]
119 # Anatomy of that suite: [NEWLINE, INDENT, first_stmt, all_other_stmts]
120 # We need to insert our new stuff before the first_stmt and change the
121 # first_stmt's prefix.
122
123 suite = node.children[4]
124 first_stmt = suite.children[2]
125 ident = indentation(first_stmt)
126
127 for name, default_value in gen_params(params_rawlist):
128 if default_value is None:
129 suite.insert_child(2, Newline())
130 suite.insert_child(2, String(_assign_template %{u'name':name, u'kwargs':new_kwargs}, prefix=ident))
131 else:
132 suite.insert_child(2, Newline())
133 suite.insert_child(2, String(_else_template %{u'name':name, u'default':default_value}, prefix=ident))
134 suite.insert_child(2, Newline())
135 suite.insert_child(2, String(_if_template %{u'assign':_assign_template %{u'name':name, u'kwargs':new_kwargs}, u'name':name, u'kwargs':new_kwargs}, prefix=ident))
136 first_stmt.prefix = ident
137 suite.children[2].prefix = u""
138
139 # Now, we need to fix up the list of params.
140
141 must_add_kwargs = remove_params(params_rawlist)
142 if must_add_kwargs:
143 arglist = results[u'arglist']
144 if len(arglist.children) > 0 and arglist.children[-1].type != token.COMMA:
145 arglist.append_child(Comma())
146 arglist.append_child(DoubleStar(prefix=u" "))
147 arglist.append_child(Name(new_kwargs))