@@ -233,7 +233,23 @@ def visit_Index(self, node: ast.Index) -> ast.expr:
233
233
return node .value
234
234
235
235
236
- def _annotation_is_ellipsis_callable (annotation : ast .expr | None ) -> bool :
236
+ def _is_name (node : ast .expr | None , name : str ) -> bool :
237
+ """Return `True` if `node` is the AST representation of `name`."""
238
+ return isinstance (node , ast .Name ) and node .id == name
239
+
240
+
241
+ def _is_attribute (node : ast .expr | None , attribute : str ) -> bool :
242
+ """Determine whether `node` is the AST representation of `attribute`.
243
+ Only works if `attribute has a single `.` delimiter, e.g. "collection.abc".
244
+ """
245
+ return (
246
+ isinstance (node , ast .Attribute )
247
+ and isinstance (node .value , ast .Name )
248
+ and [node .value .id , node .attr ] == attribute .split ("." )
249
+ )
250
+
251
+
252
+ def _is_ellipsis_callable (annotation : ast .expr | None ) -> bool :
237
253
"""Evaluate whether `annotation` is an "ellipsis callable".
238
254
239
255
Return `True` if `annotation` is either:
@@ -254,7 +270,6 @@ def _annotation_is_ellipsis_callable(annotation: ast.expr | None) -> bool:
254
270
return False
255
271
256
272
# Now we know it's e.g. `Foo[..., bar]`
257
-
258
273
subscripted_object = annotation .value
259
274
260
275
if isinstance (subscripted_object , ast .Name ):
@@ -267,18 +282,13 @@ def _annotation_is_ellipsis_callable(annotation: ast.expr | None) -> bool:
267
282
return False
268
283
269
284
# Now we know it's an attribute e.g. `Foo.Callable[..., bar]`
270
-
271
285
module = subscripted_object .value
286
+ return _is_name (module , "typing" ) or _is_attribute (module , "collections.abc" )
272
287
273
- if isinstance (module , ast .Name ):
274
- return module .id == "typing"
275
288
276
- return (
277
- isinstance (module , ast .Attribute )
278
- and isinstance (module .value , ast .Name )
279
- and module .value .id == "collections"
280
- and module .attr == "abc"
281
- )
289
+ def _is_Any (annotation : ast .expr | None ) -> bool :
290
+ """Return `True` if `annotation` is `Any` or `typing.Any`"""
291
+ return _is_name (annotation , "Any" ) or _is_attribute (annotation , "typing.Any" )
282
292
283
293
284
294
def _should_use_ParamSpec (function : ast .FunctionDef | ast .AsyncFunctionDef ) -> bool :
@@ -290,37 +300,19 @@ def _should_use_ParamSpec(function: ast.FunctionDef | ast.AsyncFunctionDef) -> b
290
300
)
291
301
292
302
if not any (
293
- _annotation_is_ellipsis_callable (arg_node .annotation )
294
- for arg_node in non_variadic_args
303
+ _is_ellipsis_callable (arg_node .annotation ) for arg_node in non_variadic_args
295
304
):
296
305
return False
297
306
298
307
# First check for functions like `def foo(func: Callable[P, R]) -> Callable[P, R]: ...`
299
-
300
- if _annotation_is_ellipsis_callable (function .returns ):
308
+ if _is_ellipsis_callable (function .returns ):
301
309
return True
302
310
303
311
# Now check for functions like `def foo(__func: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R: ...`
304
-
305
- vararg , kwarg = arguments .vararg , arguments .kwarg
306
- if not (isinstance (vararg , ast .arg ) and isinstance (kwarg , ast .arg )):
307
- return False
308
-
309
- for annotation in (vararg .annotation , kwarg .annotation ):
310
- if isinstance (annotation , ast .Name ):
311
- if annotation .id != "Any" :
312
- return False
313
- elif isinstance (annotation , ast .Attribute ):
314
- if not (
315
- isinstance (annotation .value , ast .Name )
316
- and annotation .value .id == "typing"
317
- and annotation .value == "Any"
318
- ):
319
- return False
320
- else :
321
- return False
322
-
323
- return True
312
+ return all (
313
+ (isinstance (arg , ast .arg ) and _is_Any (arg .annotation ))
314
+ for arg in (arguments .vararg , arguments .kwarg )
315
+ )
324
316
325
317
326
318
def _unparse_assign_node (node : ast .Assign | ast .AnnAssign ) -> str :
0 commit comments