diff --git a/crates/ruff/resources/test/fixtures/pep8_naming/N815.py b/crates/ruff/resources/test/fixtures/pep8_naming/N815.py index db767423d2..d3578d3cfb 100644 --- a/crates/ruff/resources/test/fixtures/pep8_naming/N815.py +++ b/crates/ruff/resources/test/fixtures/pep8_naming/N815.py @@ -13,3 +13,11 @@ class C: myObj2 = namedtuple("MyObj2", ["a", "b"]) Employee = NamedTuple('Employee', [('name', str), ('id', int)]) Point2D = TypedDict('Point2D', {'in': int, 'x-y': int}) + + +class D(TypedDict): + lower: int + CONSTANT: str + mixedCase: bool + _mixedCase: list + mixed_Case: set diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index b2800250ec..bb4b6119f7 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -4456,8 +4456,14 @@ impl<'a> Checker<'a> { .rules .enabled(Rule::MixedCaseVariableInClassScope) { - if matches!(self.ctx.scope().kind, ScopeKind::Class(..)) { - pep8_naming::rules::mixed_case_variable_in_class_scope(self, expr, parent, id); + if let ScopeKind::Class(class) = &self.ctx.scope().kind { + pep8_naming::rules::mixed_case_variable_in_class_scope( + self, + expr, + parent, + id, + class.bases, + ); } } diff --git a/crates/ruff/src/rules/pep8_naming/helpers.rs b/crates/ruff/src/rules/pep8_naming/helpers.rs index 85133ac6fd..4e64ec65a8 100644 --- a/crates/ruff/src/rules/pep8_naming/helpers.rs +++ b/crates/ruff/src/rules/pep8_naming/helpers.rs @@ -1,5 +1,5 @@ use itertools::Itertools; -use rustpython_parser::ast::{ExprKind, Stmt, StmtKind}; +use rustpython_parser::ast::{Expr, ExprKind, Stmt, StmtKind}; use ruff_python_stdlib::str::{is_lower, is_upper}; @@ -70,6 +70,12 @@ pub fn is_type_var_assignment(checker: &Checker, stmt: &Stmt) -> bool { }) } +pub fn is_typeddict(checker: &Checker, bases: &[Expr]) -> bool { + bases + .iter() + .any(|base| checker.ctx.match_typing_expr(base, "TypedDict")) +} + #[cfg(test)] mod tests { use super::{is_acronym, is_camelcase, is_mixed_case}; diff --git a/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs b/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs index 68534cf6c4..eaac8576d4 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs @@ -26,6 +26,7 @@ pub fn mixed_case_variable_in_class_scope( expr: &Expr, stmt: &Stmt, name: &str, + bases: &[Expr], ) { if checker .settings @@ -36,7 +37,10 @@ pub fn mixed_case_variable_in_class_scope( { return; } - if helpers::is_mixed_case(name) && !helpers::is_namedtuple_assignment(checker, stmt) { + if helpers::is_mixed_case(name) + && !helpers::is_namedtuple_assignment(checker, stmt) + && !helpers::is_typeddict(checker, bases) + { checker.diagnostics.push(Diagnostic::new( MixedCaseVariableInClassScope { name: name.to_string(),