Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/libcst/_parser/conversions/params.py: 29%
111 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:43 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:43 +0000
1# Copyright (c) Meta Platforms, Inc. and affiliates.
2#
3# This source code is licensed under the MIT license found in the
4# LICENSE file in the root directory of this source tree.
5# pyre-unsafe
7from typing import Any, List, Optional, Sequence, Union
9from libcst._exceptions import PartialParserSyntaxError
10from libcst._maybe_sentinel import MaybeSentinel
11from libcst._nodes.expression import (
12 Annotation,
13 Name,
14 Param,
15 Parameters,
16 ParamSlash,
17 ParamStar,
18)
19from libcst._nodes.op import AssignEqual, Comma
20from libcst._parser.custom_itertools import grouper
21from libcst._parser.production_decorator import with_production
22from libcst._parser.types.config import ParserConfig
23from libcst._parser.types.partials import ParamStarPartial
24from libcst._parser.whitespace_parser import parse_parenthesizable_whitespace
27@with_production( # noqa: C901: too complex
28 "typedargslist",
29 """(
30 (tfpdef_assign (',' tfpdef_assign)* ',' tfpdef_posind [',' [ tfpdef_assign (
31 ',' tfpdef_assign)* [',' [
32 tfpdef_star (',' tfpdef_assign)* [',' [tfpdef_starstar [',']]]
33 | tfpdef_starstar [',']]]
34 | tfpdef_star (',' tfpdef_assign)* [',' [tfpdef_starstar [',']]]
35 | tfpdef_starstar [',']]] )
36 | (tfpdef_assign (',' tfpdef_assign)* [',' [
37 tfpdef_star (',' tfpdef_assign)* [',' [tfpdef_starstar [',']]]
38 | tfpdef_starstar [',']]]
39 | tfpdef_star (',' tfpdef_assign)* [',' [tfpdef_starstar [',']]]
40 | tfpdef_starstar [','])
41 )""",
42 version=">=3.8",
43)
44@with_production( # noqa: C901: too complex
45 "typedargslist",
46 (
47 "(tfpdef_assign (',' tfpdef_assign)* "
48 + "[',' [tfpdef_star (',' tfpdef_assign)* [',' [tfpdef_starstar [',']]] | tfpdef_starstar [',']]]"
49 + "| tfpdef_star (',' tfpdef_assign)* [',' [tfpdef_starstar [',']]] | tfpdef_starstar [','])"
50 ),
51 version=">=3.6,<=3.7",
52)
53@with_production( # noqa: C901: too complex
54 "typedargslist",
55 (
56 "(tfpdef_assign (',' tfpdef_assign)* "
57 + "[',' [tfpdef_star (',' tfpdef_assign)* [',' tfpdef_starstar] | tfpdef_starstar]]"
58 + "| tfpdef_star (',' tfpdef_assign)* [',' tfpdef_starstar] | tfpdef_starstar)"
59 ),
60 version="<=3.5",
61)
62@with_production(
63 "varargslist",
64 """vfpdef_assign (',' vfpdef_assign)* ',' vfpdef_posind [',' [ (vfpdef_assign (',' vfpdef_assign)* [',' [
65 vfpdef_star (',' vfpdef_assign)* [',' [vfpdef_starstar [',']]]
66 | vfpdef_starstar [',']]]
67 | vfpdef_star (',' vfpdef_assign)* [',' [vfpdef_starstar [',']]]
68 | vfpdef_starstar [',']) ]] | (vfpdef_assign (',' vfpdef_assign)* [',' [
69 vfpdef_star (',' vfpdef_assign)* [',' [vfpdef_starstar [',']]]
70 | vfpdef_starstar [',']]]
71 | vfpdef_star (',' vfpdef_assign)* [',' [vfpdef_starstar [',']]]
72 | vfpdef_starstar [',']
73 )""",
74 version=">=3.8",
75)
76@with_production(
77 "varargslist",
78 (
79 "(vfpdef_assign (',' vfpdef_assign)* "
80 + "[',' [vfpdef_star (',' vfpdef_assign)* [',' [vfpdef_starstar [',']]] | vfpdef_starstar [',']]]"
81 + "| vfpdef_star (',' vfpdef_assign)* [',' [vfpdef_starstar [',']]] | vfpdef_starstar [','])"
82 ),
83 version=">=3.6,<=3.7",
84)
85@with_production(
86 "varargslist",
87 (
88 "(vfpdef_assign (',' vfpdef_assign)* "
89 + "[',' [vfpdef_star (',' vfpdef_assign)* [',' vfpdef_starstar] | vfpdef_starstar]]"
90 + "| vfpdef_star (',' vfpdef_assign)* [',' vfpdef_starstar] | vfpdef_starstar)"
91 ),
92 version="<=3.5",
93)
94def convert_argslist( # noqa: C901
95 config: ParserConfig, children: Sequence[Any]
96) -> Any:
97 posonly_params: List[Param] = []
98 posonly_ind: Union[ParamSlash, MaybeSentinel] = MaybeSentinel.DEFAULT
99 params: List[Param] = []
100 seen_default: bool = False
101 star_arg: Union[Param, ParamStar, MaybeSentinel] = MaybeSentinel.DEFAULT
102 kwonly_params: List[Param] = []
103 star_kwarg: Optional[Param] = None
105 def add_param(
106 current_param: Optional[List[Param]], param: Union[Param, ParamStar]
107 ) -> Optional[List[Param]]:
108 nonlocal star_arg
109 nonlocal star_kwarg
110 nonlocal seen_default
111 nonlocal posonly_params
112 nonlocal posonly_ind
113 nonlocal params
115 if isinstance(param, ParamStar):
116 # Only can add this if we don't already have a "*" or a "*param".
117 if current_param is params:
118 star_arg = param
119 current_param = kwonly_params
120 else:
121 # Example code:
122 # def fn(*abc, *): ...
123 # This should be unreachable, the grammar already disallows it.
124 raise Exception(
125 "Cannot have multiple star ('*') markers in a single argument "
126 + "list."
127 )
128 elif isinstance(param, ParamSlash):
129 # Only can add this if we don't already have a "/" or a "*" or a "*param".
130 if current_param is params and len(posonly_params) == 0:
131 posonly_ind = param
132 posonly_params = params
133 params = []
134 current_param = params
135 else:
136 # Example code:
137 # def fn(foo, /, *, /, bar): ...
138 # This should be unreachable, the grammar already disallows it.
139 raise Exception(
140 "Cannot have multiple slash ('/') markers in a single argument "
141 + "list."
142 )
143 elif isinstance(param.star, str) and param.star == "" and param.default is None:
144 # Can only add this if we're in the params or kwonly_params section
145 if current_param is params and not seen_default:
146 params.append(param)
147 elif current_param is kwonly_params:
148 kwonly_params.append(param)
149 else:
150 # Example code:
151 # def fn(first=None, second): ...
152 # This code is reachable, so we should use a PartialParserSyntaxError.
153 raise PartialParserSyntaxError(
154 "Cannot have a non-default argument following a default argument."
155 )
156 elif (
157 isinstance(param.star, str)
158 and param.star == ""
159 and param.default is not None
160 ):
161 # Can only add this if we're not yet at star args.
162 if current_param is params:
163 seen_default = True
164 params.append(param)
165 elif current_param is kwonly_params:
166 kwonly_params.append(param)
167 else:
168 # Example code:
169 # def fn(**kwargs, trailing=None)
170 # This should be unreachable, the grammar already disallows it.
171 raise Exception("Cannot have any arguments after a kwargs expansion.")
172 elif (
173 isinstance(param.star, str) and param.star == "*" and param.default is None
174 ):
175 # Can only add this if we're in params, since we only allow one of
176 # "*" or "*param".
177 if current_param is params:
178 star_arg = param
179 current_param = kwonly_params
180 else:
181 # Example code:
182 # def fn(*first, *second): ...
183 # This should be unreachable, the grammar already disallows it.
184 raise Exception(
185 "Expected a keyword argument but found a starred positional "
186 + "argument expansion."
187 )
188 elif (
189 isinstance(param.star, str) and param.star == "**" and param.default is None
190 ):
191 # Can add this in all cases where we don't have a star_kwarg
192 # yet.
193 if current_param is not None:
194 star_kwarg = param
195 current_param = None
196 else:
197 # Example code:
198 # def fn(**first, **second)
199 # This should be unreachable, the grammar already disallows it.
200 raise Exception(
201 "Multiple starred keyword argument expansions are not allowed in a "
202 + "single argument list"
203 )
204 else:
205 # The state machine should never end up here.
206 raise Exception("Logic error!")
208 return current_param
210 # The parameter list we are adding to
211 current: Optional[List[Param]] = params
213 # We should have every other item in the group as a param or a comma by now,
214 # so split them up, add commas and then put them in the appropriate group.
215 for parameter, comma in grouper(children, 2):
216 if comma is None:
217 if isinstance(parameter, ParamStarPartial):
218 # Example:
219 # def fn(abc, *): ...
220 #
221 # There's also the case where we have bare * with a trailing comma.
222 # That's handled later.
223 #
224 # It's not valid to construct a ParamStar object without a comma, so we
225 # need to catch the non-comma case separately.
226 raise PartialParserSyntaxError(
227 "Named (keyword) arguments must follow a bare *."
228 )
229 else:
230 current = add_param(current, parameter)
231 else:
232 comma = Comma(
233 whitespace_before=parse_parenthesizable_whitespace(
234 config, comma.whitespace_before
235 ),
236 whitespace_after=parse_parenthesizable_whitespace(
237 config, comma.whitespace_after
238 ),
239 )
240 if isinstance(parameter, ParamStarPartial):
241 current = add_param(current, ParamStar(comma=comma))
242 else:
243 current = add_param(current, parameter.with_changes(comma=comma))
245 if isinstance(star_arg, ParamStar) and len(kwonly_params) == 0:
246 # Example:
247 # def fn(abc, *,): ...
248 #
249 # This will raise a validation error, but we want to make sure to raise a syntax
250 # error instead.
251 #
252 # The case where there's no trailing comma is already handled by this point, so
253 # this conditional is only for the case where we have a trailing comma.
254 raise PartialParserSyntaxError(
255 "Named (keyword) arguments must follow a bare *."
256 )
258 return Parameters(
259 posonly_params=tuple(posonly_params),
260 posonly_ind=posonly_ind,
261 params=tuple(params),
262 star_arg=star_arg,
263 kwonly_params=tuple(kwonly_params),
264 star_kwarg=star_kwarg,
265 )
268@with_production("tfpdef_star", "'*' [tfpdef]")
269@with_production("vfpdef_star", "'*' [vfpdef]")
270def convert_fpdef_star(config: ParserConfig, children: Sequence[Any]) -> Any:
271 if len(children) == 1:
272 (star,) = children
273 return ParamStarPartial()
274 else:
275 star, param = children
276 return param.with_changes(
277 star=star.string,
278 whitespace_after_star=parse_parenthesizable_whitespace(
279 config, star.whitespace_after
280 ),
281 )
284@with_production("tfpdef_starstar", "'**' tfpdef")
285@with_production("vfpdef_starstar", "'**' vfpdef")
286def convert_fpdef_starstar(config: ParserConfig, children: Sequence[Any]) -> Any:
287 starstar, param = children
288 return param.with_changes(
289 star=starstar.string,
290 whitespace_after_star=parse_parenthesizable_whitespace(
291 config, starstar.whitespace_after
292 ),
293 )
296@with_production("tfpdef_assign", "tfpdef ['=' test]")
297@with_production("vfpdef_assign", "vfpdef ['=' test]")
298def convert_fpdef_assign(config: ParserConfig, children: Sequence[Any]) -> Any:
299 if len(children) == 1:
300 (child,) = children
301 return child
303 param, equal, default = children
304 return param.with_changes(
305 equal=AssignEqual(
306 whitespace_before=parse_parenthesizable_whitespace(
307 config, equal.whitespace_before
308 ),
309 whitespace_after=parse_parenthesizable_whitespace(
310 config, equal.whitespace_after
311 ),
312 ),
313 default=default.value,
314 )
317@with_production("tfpdef", "NAME [':' test]")
318@with_production("vfpdef", "NAME")
319def convert_fpdef(config: ParserConfig, children: Sequence[Any]) -> Any:
320 if len(children) == 1:
321 # This is just a parameter
322 (child,) = children
323 namenode = Name(child.string)
324 annotation = None
325 else:
326 # This is a parameter with a type hint
327 name, colon, typehint = children
328 namenode = Name(name.string)
329 annotation = Annotation(
330 whitespace_before_indicator=parse_parenthesizable_whitespace(
331 config, colon.whitespace_before
332 ),
333 whitespace_after_indicator=parse_parenthesizable_whitespace(
334 config, colon.whitespace_after
335 ),
336 annotation=typehint.value,
337 )
339 return Param(star="", name=namenode, annotation=annotation, default=None)
342@with_production("tfpdef_posind", "'/'")
343@with_production("vfpdef_posind", "'/'")
344def convert_fpdef_slash(config: ParserConfig, children: Sequence[Any]) -> Any:
345 return ParamSlash()