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

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 

6 

7from typing import Any, List, Optional, Sequence, Union 

8 

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 

25 

26 

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 

104 

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 

114 

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!") 

207 

208 return current_param 

209 

210 # The parameter list we are adding to 

211 current: Optional[List[Param]] = params 

212 

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)) 

244 

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 ) 

257 

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 ) 

266 

267 

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 ) 

282 

283 

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 ) 

294 

295 

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 

302 

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 ) 

315 

316 

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 ) 

338 

339 return Param(star="", name=namenode, annotation=annotation, default=None) 

340 

341 

342@with_production("tfpdef_posind", "'/'") 

343@with_production("vfpdef_posind", "'/'") 

344def convert_fpdef_slash(config: ParserConfig, children: Sequence[Any]) -> Any: 

345 return ParamSlash()