diff --git a/doc/whatsnew/fragments/8830.false_positive b/doc/whatsnew/fragments/8830.false_positive new file mode 100644 index 0000000000..1d7edeeb4f --- /dev/null +++ b/doc/whatsnew/fragments/8830.false_positive @@ -0,0 +1,4 @@ +Exempt parents with only type annotations from the ``invalid-enum-extension`` +message. + +Closes #8830 diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py index 9b0bd50b1d..995e3cfd90 100644 --- a/pylint/checkers/classes/class_checker.py +++ b/pylint/checkers/classes/class_checker.py @@ -891,12 +891,21 @@ def _check_consistent_mro(self, node: nodes.ClassDef) -> None: def _check_enum_base(self, node: nodes.ClassDef, ancestor: nodes.ClassDef) -> None: members = ancestor.getattr("__members__") if members and isinstance(members[0], nodes.Dict) and members[0].items: - self.add_message( - "invalid-enum-extension", - args=ancestor.name, - node=node, - confidence=INFERENCE, - ) + for _, name_node in members[0].items: + # Exempt type annotations without value assignments + if all( + isinstance(item.parent, nodes.AnnAssign) + and item.parent.value is None + for item in ancestor.getattr(name_node.name) + ): + continue + self.add_message( + "invalid-enum-extension", + args=ancestor.name, + node=node, + confidence=INFERENCE, + ) + break if ancestor.is_subtype_of("enum.IntFlag"): # Collect integer flag assignments present on the class diff --git a/tests/functional/i/invalid/invalid_enum_extension.py b/tests/functional/i/invalid/invalid_enum_extension.py index 5af11af387..9ccb458af3 100644 --- a/tests/functional/i/invalid/invalid_enum_extension.py +++ b/tests/functional/i/invalid/invalid_enum_extension.py @@ -21,6 +21,33 @@ class D(C): x = 3 +# Similarly, items that are only type annotations are okay. +class ColorEnum(Enum): + red: int + green: int + blue: int + + def __init__(self, red: int, green: int, blue: int) -> None: + self.red = red + self.green = green + self.blue = blue + + +class Pastel(ColorEnum): + SAGE = (170, 200, 167) + + +class IncorrectColorEnum(Enum): + red: None = None + + def __init__(self, red: None) -> None: + self.red = red + + +class IncorrectPastel(IncorrectColorEnum): # [invalid-enum-extension] + SOME_COLOR = 170 + + class CustomFlags(IntFlag): SUPPORT_OPEN = 1 SUPPORT_CLOSE = 2 diff --git a/tests/functional/i/invalid/invalid_enum_extension.txt b/tests/functional/i/invalid/invalid_enum_extension.txt index dbe799e08b..42f0deaf28 100644 --- a/tests/functional/i/invalid/invalid_enum_extension.txt +++ b/tests/functional/i/invalid/invalid_enum_extension.txt @@ -1 +1,2 @@ invalid-enum-extension:12:0:12:7:B:"Extending inherited Enum class ""A""":INFERENCE +invalid-enum-extension:47:0:47:21:IncorrectPastel:"Extending inherited Enum class ""IncorrectColorEnum""":INFERENCE