Avoid triggering N806 on TypeAlias assignments (#7119)

This commit is contained in:
Charlie Marsh 2023-09-04 09:44:28 +01:00 committed by GitHub
parent 1067261a55
commit 5ec73a6137
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 26 deletions

View file

@ -1,8 +1,6 @@
import collections import collections
from collections import namedtuple from collections import namedtuple
from typing import TypeVar from typing import TypeAlias, TypeVar, NewType, NamedTuple, TypedDict
from typing import NewType
from typing import NamedTuple, TypedDict
GLOBAL: str = "foo" GLOBAL: str = "foo"
@ -21,9 +19,11 @@ def assign():
T = TypeVar("T") T = TypeVar("T")
UserId = NewType("UserId", int) UserId = NewType("UserId", int)
Employee = NamedTuple('Employee', [('name', str), ('id', int)]) Employee = NamedTuple("Employee", [("name", str), ("id", int)])
Point2D = TypedDict('Point2D', {'in': int, 'x-y': int}) Point2D = TypedDict("Point2D", {"in": int, "x-y": int})
IntOrStr: TypeAlias = int | str
def aug_assign(rank, world_size): def aug_assign(rank, world_size):

View file

@ -22,6 +22,7 @@ pub(super) fn is_acronym(name: &str, asname: &str) -> bool {
name.chars().filter(|c| c.is_uppercase()).join("") == asname name.chars().filter(|c| c.is_uppercase()).join("") == asname
} }
/// Returns `true` if the statement is an assignment to a named tuple.
pub(super) fn is_named_tuple_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool { pub(super) fn is_named_tuple_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool {
let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else { let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else {
return false; return false;
@ -30,13 +31,12 @@ pub(super) fn is_named_tuple_assignment(stmt: &Stmt, semantic: &SemanticModel) -
return false; return false;
}; };
semantic.resolve_call_path(func).is_some_and(|call_path| { semantic.resolve_call_path(func).is_some_and(|call_path| {
matches!( matches!(call_path.as_slice(), ["collections", "namedtuple"])
call_path.as_slice(), || semantic.match_typing_call_path(&call_path, "NamedTuple")
["collections", "namedtuple"] | ["typing", "NamedTuple"]
)
}) })
} }
/// Returns `true` if the statement is an assignment to a `TypedDict`.
pub(super) fn is_typed_dict_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool { pub(super) fn is_typed_dict_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool {
let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else { let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else {
return false; return false;
@ -44,11 +44,10 @@ pub(super) fn is_typed_dict_assignment(stmt: &Stmt, semantic: &SemanticModel) ->
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else { let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {
return false; return false;
}; };
semantic semantic.match_typing_expr(func, "TypedDict")
.resolve_call_path(func)
.is_some_and(|call_path| matches!(call_path.as_slice(), ["typing", "TypedDict"]))
} }
/// Returns `true` if the statement is an assignment to a `TypeVar` or `NewType`.
pub(super) fn is_type_var_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool { pub(super) fn is_type_var_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool {
let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else { let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else {
return false; return false;
@ -56,9 +55,18 @@ pub(super) fn is_type_var_assignment(stmt: &Stmt, semantic: &SemanticModel) -> b
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else { let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {
return false; return false;
}; };
semantic semantic.resolve_call_path(func).is_some_and(|call_path| {
.resolve_call_path(func) semantic.match_typing_call_path(&call_path, "TypeVar")
.is_some_and(|call_path| matches!(call_path.as_slice(), ["typing", "TypeVar" | "NewType"])) || semantic.match_typing_call_path(&call_path, "NewType")
})
}
/// Returns `true` if the statement is an assignment to a `TypeAlias`.
pub(super) fn is_type_alias_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool {
let Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. }) = stmt else {
return false;
};
semantic.match_typing_expr(annotation, "TypeAlias")
} }
pub(super) fn is_typed_dict_class(arguments: Option<&Arguments>, semantic: &SemanticModel) -> bool { pub(super) fn is_typed_dict_class(arguments: Option<&Arguments>, semantic: &SemanticModel) -> bool {

View file

@ -70,6 +70,7 @@ pub(crate) fn non_lowercase_variable_in_function(checker: &mut Checker, expr: &E
if helpers::is_named_tuple_assignment(parent, checker.semantic()) if helpers::is_named_tuple_assignment(parent, checker.semantic())
|| helpers::is_typed_dict_assignment(parent, checker.semantic()) || helpers::is_typed_dict_assignment(parent, checker.semantic())
|| helpers::is_type_var_assignment(parent, checker.semantic()) || helpers::is_type_var_assignment(parent, checker.semantic())
|| helpers::is_type_alias_assignment(parent, checker.semantic())
{ {
return; return;
} }

View file

@ -1,23 +1,23 @@
--- ---
source: crates/ruff/src/rules/pep8_naming/mod.rs source: crates/ruff/src/rules/pep8_naming/mod.rs
--- ---
N806.py:14:5: N806 Variable `Camel` in function should be lowercase N806.py:12:5: N806 Variable `Camel` in function should be lowercase
| |
12 | GLOBAL = "bar" 10 | GLOBAL = "bar"
13 | lower = 0 11 | lower = 0
14 | Camel = 0 12 | Camel = 0
| ^^^^^ N806 | ^^^^^ N806
15 | CONSTANT = 0 13 | CONSTANT = 0
16 | _ = 0 14 | _ = 0
| |
N806.py:15:5: N806 Variable `CONSTANT` in function should be lowercase N806.py:13:5: N806 Variable `CONSTANT` in function should be lowercase
| |
13 | lower = 0 11 | lower = 0
14 | Camel = 0 12 | Camel = 0
15 | CONSTANT = 0 13 | CONSTANT = 0
| ^^^^^^^^ N806 | ^^^^^^^^ N806
16 | _ = 0 14 | _ = 0
| |