From cdbd0bd5cd5cdcde04ad04a094977c4e7a4b22a8 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 22 Jun 2023 15:52:36 -0400 Subject: [PATCH] Respect `abc` decorators when classifying function types (#5315) Closes #5307. --- .../test/fixtures/pep8_naming/N805.py | 8 ++++-- .../fixtures/pep8_naming/ignore_names/N805.py | 19 ++++++++++++- ...les__pep8_naming__tests__N805_N805.py.snap | 28 +++++++++---------- ...naming__tests__classmethod_decorators.snap | 10 +++---- ...ing__tests__ignore_names_N805_N805.py.snap | 10 +++---- .../src/analyze/function_type.rs | 12 ++++---- 6 files changed, 55 insertions(+), 32 deletions(-) diff --git a/crates/ruff/resources/test/fixtures/pep8_naming/N805.py b/crates/ruff/resources/test/fixtures/pep8_naming/N805.py index d45a9c3118..99fcf2ed0c 100644 --- a/crates/ruff/resources/test/fixtures/pep8_naming/N805.py +++ b/crates/ruff/resources/test/fixtures/pep8_naming/N805.py @@ -1,4 +1,4 @@ -from abc import ABCMeta +import abc import pydantic @@ -19,6 +19,10 @@ class Class: def class_method(cls): pass + @abc.abstractclassmethod + def abstract_class_method(cls): + pass + @staticmethod def static_method(x): return x @@ -41,7 +45,7 @@ class Class: ... -class MetaClass(ABCMeta): +class MetaClass(abc.ABCMeta): def bad_method(self): pass diff --git a/crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N805.py b/crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N805.py index ae5206483e..590df0bd6d 100644 --- a/crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N805.py +++ b/crates/ruff/resources/test/fixtures/pep8_naming/ignore_names/N805.py @@ -1,4 +1,4 @@ -from abc import ABCMeta +import abc import pydantic @@ -34,6 +34,23 @@ class Class: def stillBad(cls, my_field: str) -> str: pass + @classmethod + def badAllowed(cls): + pass + + @classmethod + def stillBad(cls): + pass + + @abc.abstractclassmethod + def badAllowed(cls): + pass + + @abc.abstractclassmethod + def stillBad(cls): + pass + + class PosOnlyClass: def badAllowed(this, blah, /, self, something: str): pass diff --git a/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__N805_N805.py.snap b/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__N805_N805.py.snap index 9c4369878a..714a639ab7 100644 --- a/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__N805_N805.py.snap +++ b/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__N805_N805.py.snap @@ -18,29 +18,29 @@ N805.py:12:30: N805 First argument of a method should be named `self` 13 | pass | -N805.py:27:15: N805 First argument of a method should be named `self` - | -26 | @pydantic.validator -27 | def lower(cls, my_field: str) -> str: - | ^^^ N805 -28 | pass - | - N805.py:31:15: N805 First argument of a method should be named `self` | -30 | @pydantic.validator("my_field") +30 | @pydantic.validator 31 | def lower(cls, my_field: str) -> str: | ^^^ N805 32 | pass | -N805.py:60:29: N805 First argument of a method should be named `self` +N805.py:35:15: N805 First argument of a method should be named `self` | -58 | pass -59 | -60 | def bad_method_pos_only(this, blah, /, self, something: str): +34 | @pydantic.validator("my_field") +35 | def lower(cls, my_field: str) -> str: + | ^^^ N805 +36 | pass + | + +N805.py:64:29: N805 First argument of a method should be named `self` + | +62 | pass +63 | +64 | def bad_method_pos_only(this, blah, /, self, something: str): | ^^^^ N805 -61 | pass +65 | pass | diff --git a/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__classmethod_decorators.snap b/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__classmethod_decorators.snap index a6d3bc8592..06c9407f28 100644 --- a/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__classmethod_decorators.snap +++ b/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__classmethod_decorators.snap @@ -18,13 +18,13 @@ N805.py:12:30: N805 First argument of a method should be named `self` 13 | pass | -N805.py:60:29: N805 First argument of a method should be named `self` +N805.py:64:29: N805 First argument of a method should be named `self` | -58 | pass -59 | -60 | def bad_method_pos_only(this, blah, /, self, something: str): +62 | pass +63 | +64 | def bad_method_pos_only(this, blah, /, self, something: str): | ^^^^ N805 -61 | pass +65 | pass | diff --git a/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__ignore_names_N805_N805.py.snap b/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__ignore_names_N805_N805.py.snap index 46bfb51c01..fee3fee254 100644 --- a/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__ignore_names_N805_N805.py.snap +++ b/crates/ruff/src/rules/pep8_naming/snapshots/ruff__rules__pep8_naming__tests__ignore_names_N805_N805.py.snap @@ -35,13 +35,13 @@ N805.py:34:18: N805 First argument of a method should be named `self` 35 | pass | -N805.py:41:18: N805 First argument of a method should be named `self` +N805.py:58:18: N805 First argument of a method should be named `self` | -39 | pass -40 | -41 | def stillBad(this, blah, /, self, something: str): +56 | pass +57 | +58 | def stillBad(this, blah, /, self, something: str): | ^^^^ N805 -42 | pass +59 | pass | diff --git a/crates/ruff_python_semantic/src/analyze/function_type.rs b/crates/ruff_python_semantic/src/analyze/function_type.rs index 46d984b276..63f9b8ce71 100644 --- a/crates/ruff_python_semantic/src/analyze/function_type.rs +++ b/crates/ruff_python_semantic/src/analyze/function_type.rs @@ -35,10 +35,12 @@ pub fn classify( semantic .resolve_call_path(map_callable(&decorator.expression)) .map_or(false, |call_path| { - matches!(call_path.as_slice(), ["", "staticmethod"]) - || staticmethod_decorators - .iter() - .any(|decorator| call_path == from_qualified_name(decorator)) + matches!( + call_path.as_slice(), + ["", "staticmethod"] | ["abc", "abstractstaticmethod"] + ) || staticmethod_decorators + .iter() + .any(|decorator| call_path == from_qualified_name(decorator)) }) }) { FunctionType::StaticMethod @@ -55,7 +57,7 @@ pub fn classify( || decorator_list.iter().any(|decorator| { // The method is decorated with a class method decorator (like `@classmethod`). semantic.resolve_call_path(map_callable(&decorator.expression)).map_or(false, |call_path| { - matches!(call_path.as_slice(), ["", "classmethod"]) || + matches!(call_path.as_slice(), ["", "classmethod"] | ["abc", "abstractclassmethod"]) || classmethod_decorators .iter() .any(|decorator| call_path == from_qualified_name(decorator))