|
1 | 1 | import argparse |
2 | 2 | from dataclasses import dataclass, field |
| 3 | +import enum |
3 | 4 | import json |
4 | 5 | from typing import Dict, List, Optional, Sequence, Set, Tuple, Type |
5 | 6 | from typing_extensions import TypedDict |
@@ -35,6 +36,11 @@ class PyanalyzeSuggestion(TypedDict): |
35 | 36 | imports: List[str] |
36 | 37 |
|
37 | 38 |
|
| 39 | +class DecoratorKind(enum.Enum): |
| 40 | + asynq = 1 |
| 41 | + abstractmethod = 2 |
| 42 | + |
| 43 | + |
38 | 44 | @dataclass |
39 | 45 | class State: |
40 | 46 | annotate_optionals: List[NamedParam] |
@@ -85,6 +91,8 @@ class AutotypeCommand(VisitorBasedCodemodCommand): |
85 | 91 | DESCRIPTION: str = "Automatically adds simple type annotations." |
86 | 92 | METADATA_DEPENDENCIES = (PositionProvider,) |
87 | 93 |
|
| 94 | + state: State |
| 95 | + |
88 | 96 | @staticmethod |
89 | 97 | def add_args(arg_parser: argparse.ArgumentParser) -> None: |
90 | 98 | arg_parser.add_argument( |
@@ -246,6 +254,10 @@ def __init__( |
246 | 254 | only_without_imports=only_without_imports, |
247 | 255 | ) |
248 | 256 |
|
| 257 | + def is_stub(self) -> bool: |
| 258 | + filename = self.context.filename |
| 259 | + return filename is not None and filename.endswith(".pyi") |
| 260 | + |
249 | 261 | def visit_FunctionDef(self, node: libcst.FunctionDef) -> None: |
250 | 262 | self.state.seen_return_statement.append(False) |
251 | 263 | self.state.seen_raise_statement.append(False) |
@@ -277,7 +289,9 @@ def leave_Lambda( |
277 | 289 | def leave_FunctionDef( |
278 | 290 | self, original_node: libcst.FunctionDef, updated_node: libcst.FunctionDef |
279 | 291 | ) -> libcst.CSTNode: |
280 | | - is_asynq = any(is_asynq_decorator(dec) for dec in original_node.decorators) |
| 292 | + kinds = {get_decorator_kind(decorator) for decorator in updated_node.decorators} |
| 293 | + is_asynq = DecoratorKind.asynq in kinds |
| 294 | + is_abstractmethod = DecoratorKind.abstractmethod in kinds |
281 | 295 | seen_return = self.state.seen_return_statement.pop() |
282 | 296 | seen_raise = self.state.seen_raise_statement.pop() |
283 | 297 | seen_yield = self.state.seen_yield.pop() |
@@ -338,6 +352,8 @@ def leave_FunctionDef( |
338 | 352 | and not seen_raise |
339 | 353 | and not seen_return |
340 | 354 | and (is_asynq or not seen_yield) |
| 355 | + and not is_abstractmethod |
| 356 | + and not self.is_stub() |
341 | 357 | ): |
342 | 358 | return updated_node.with_changes( |
343 | 359 | returns=libcst.Annotation(annotation=libcst.Name(value="None")) |
@@ -555,16 +571,26 @@ def type_of_expression(expr: libcst.BaseExpression) -> Optional[Type[object]]: |
555 | 571 | return None |
556 | 572 |
|
557 | 573 |
|
558 | | -def is_asynq_decorator(dec: libcst.Decorator) -> bool: |
559 | | - """Is this @asynq()?""" |
560 | | - if not isinstance(dec.decorator, libcst.Call): |
561 | | - return False |
562 | | - call = dec.decorator |
563 | | - if not isinstance(call.func, libcst.Name): |
564 | | - return False |
565 | | - if call.func.value != "asynq": |
566 | | - return False |
567 | | - if call.args: |
568 | | - # @asynq() with custom arguments may do something unexpected |
569 | | - return False |
570 | | - return True |
| 574 | +def get_decorator_kind(dec: libcst.Decorator) -> Optional[DecoratorKind]: |
| 575 | + """Is this @asynq() or @abstractmethod?""" |
| 576 | + if isinstance(dec.decorator, libcst.Call): |
| 577 | + call = dec.decorator |
| 578 | + if not isinstance(call.func, libcst.Name): |
| 579 | + return None |
| 580 | + if call.func.value != "asynq": |
| 581 | + return None |
| 582 | + if call.args: |
| 583 | + # @asynq() with custom arguments may do something unexpected |
| 584 | + return None |
| 585 | + return DecoratorKind.asynq |
| 586 | + elif isinstance(dec.decorator, libcst.Name): |
| 587 | + if dec.decorator.value == "abstractmethod": |
| 588 | + return DecoratorKind.abstractmethod |
| 589 | + elif isinstance(dec.decorator, libcst.Attribute): |
| 590 | + if ( |
| 591 | + dec.decorator.attr.value == "abstractmethod" |
| 592 | + and isinstance(dec.decorator.value, libcst.Name) |
| 593 | + and dec.decorator.value.value == "abc" |
| 594 | + ): |
| 595 | + return DecoratorKind.abstractmethod |
| 596 | + return None |
0 commit comments