diff --git a/crates/ruff_linter/resources/test/fixtures/pep8_naming/N802.py b/crates/ruff_linter/resources/test/fixtures/pep8_naming/N802.py index fca3520737..fd563e80c7 100644 --- a/crates/ruff_linter/resources/test/fixtures/pep8_naming/N802.py +++ b/crates/ruff_linter/resources/test/fixtures/pep8_naming/N802.py @@ -48,6 +48,39 @@ from typing import override, overload def BAD_FUNC(): pass + @overload def BAD_FUNC(): pass + + +import ast +from ast import NodeTransformer + + +class Visitor(ast.NodeVisitor): + def visit_Constant(self, node): + pass + + def bad_Name(self): + pass + +class ExtendsVisitor(Visitor): + def visit_Constant(self, node): + pass + +class Transformer(NodeTransformer): + def visit_Constant(self, node): + pass + + +from http.server import BaseHTTPRequestHandler + + +class MyRequestHandler(BaseHTTPRequestHandler): + def do_GET(self): + pass + + def dont_GET(self): + pass + diff --git a/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_function_name.rs b/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_function_name.rs index 566c552929..a1133d66f4 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_function_name.rs +++ b/crates/ruff_linter/src/rules/pep8_naming/rules/invalid_function_name.rs @@ -1,9 +1,7 @@ -use ruff_python_ast::{Decorator, Stmt}; - use ruff_macros::{ViolationMetadata, derive_message_formats}; -use ruff_python_ast::identifier::Identifier; +use ruff_python_ast::{Decorator, Stmt, identifier::Identifier}; use ruff_python_semantic::SemanticModel; -use ruff_python_semantic::analyze::visibility; +use ruff_python_semantic::analyze::{class::any_base_class, visibility}; use ruff_python_stdlib::str; use crate::Violation; @@ -84,6 +82,41 @@ pub(crate) fn invalid_function_name( return; } + let parent_class = semantic + .current_statement_parent() + .and_then(|parent| parent.as_class_def_stmt()); + + // Ignore the visit_* methods of the ast.NodeVisitor and ast.NodeTransformer classes. + if name.starts_with("visit_") + && parent_class.is_some_and(|class| { + any_base_class(class, semantic, &mut |superclass| { + let qualified = semantic.resolve_qualified_name(superclass); + qualified.is_some_and(|name| { + matches!(name.segments(), ["ast", "NodeVisitor" | "NodeTransformer"]) + }) + }) + }) + { + return; + } + + // Ignore the do_* methods of the http.server.BaseHTTPRequestHandler class + if name.starts_with("do_") + && parent_class.is_some_and(|class| { + any_base_class(class, semantic, &mut |superclass| { + let qualified = semantic.resolve_qualified_name(superclass); + qualified.is_some_and(|name| { + matches!( + name.segments(), + ["http", "server", "BaseHTTPRequestHandler"] + ) + }) + }) + }) + { + return; + } + checker.report_diagnostic( InvalidFunctionName { name: name.to_string(), diff --git a/crates/ruff_linter/src/rules/pep8_naming/snapshots/ruff_linter__rules__pep8_naming__tests__N802_N802.py.snap b/crates/ruff_linter/src/rules/pep8_naming/snapshots/ruff_linter__rules__pep8_naming__tests__N802_N802.py.snap index f2e5311d5e..31df3eed9a 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/snapshots/ruff_linter__rules__pep8_naming__tests__N802_N802.py.snap +++ b/crates/ruff_linter/src/rules/pep8_naming/snapshots/ruff_linter__rules__pep8_naming__tests__N802_N802.py.snap @@ -37,3 +37,21 @@ N802.py:40:9: N802 Function name `testTest` should be lowercase | ^^^^^^^^ N802 41 | assert True | + +N802.py:65:9: N802 Function name `bad_Name` should be lowercase + | +63 | pass +64 | +65 | def bad_Name(self): + | ^^^^^^^^ N802 +66 | pass + | + +N802.py:84:9: N802 Function name `dont_GET` should be lowercase + | +82 | pass +83 | +84 | def dont_GET(self): + | ^^^^^^^^ N802 +85 | pass + |