[pycodestyle] Avoid false positives and negatives related to type parameter default syntax (E225, E251) (#15214)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions

This commit is contained in:
InSync 2025-01-01 17:28:25 +07:00 committed by GitHub
parent 79682a28b8
commit af95f6b577
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 34 additions and 11 deletions

View file

@ -183,3 +183,8 @@ if i == -1:
if a == 1:
print(a)
# No E225: `=` is not an operator in this case
def _[T: str=None](): ...
def _(t: str=None): ...

View file

@ -75,3 +75,7 @@ def pep_696_good[A = int, B: object = str, C:object = memoryview]():
class PEP696Good[A = int, B: object = str, C:object = memoryview]:
def pep_696_good_method[A = int, B: object = str, C:object = memoryview](self):
pass
# https://github.com/astral-sh/ruff/issues/15202
type Coro[T: object = Any] = Coroutine[None, None, T]

View file

@ -5,7 +5,7 @@ use ruff_text_size::Ranged;
use crate::checkers::logical_lines::LogicalLinesContext;
use crate::rules::pycodestyle::helpers::is_non_logical_token;
use crate::rules::pycodestyle::rules::logical_lines::LogicalLine;
use crate::rules::pycodestyle::rules::logical_lines::{DefinitionState, LogicalLine};
/// ## What it does
/// Checks for missing whitespace around all operators.
@ -146,6 +146,7 @@ pub(crate) fn missing_whitespace_around_operator(
line: &LogicalLine,
context: &mut LogicalLinesContext,
) {
let mut definition_state = DefinitionState::from_tokens(line.tokens());
let mut tokens = line.tokens().iter().peekable();
let first_token = tokens
.by_ref()
@ -162,6 +163,8 @@ pub(crate) fn missing_whitespace_around_operator(
while let Some(token) = tokens.next() {
let kind = token.kind();
definition_state.visit_token_kind(kind);
if is_non_logical_token(kind) {
continue;
}
@ -174,8 +177,11 @@ pub(crate) fn missing_whitespace_around_operator(
_ => {}
};
let needs_space = if kind == TokenKind::Equal && (parens > 0 || fstrings > 0) {
let needs_space = if kind == TokenKind::Equal
&& (parens > 0 || fstrings > 0 || definition_state.in_type_params())
{
// Allow keyword args, defaults: foo(bar=None) and f-strings: f'{foo=}'
// Also ignore `foo[T=int]`, which is handled by E251.
NeedsSpace::No
} else if kind == TokenKind::Slash {
// Tolerate the "/" operator in function definition

View file

@ -480,7 +480,8 @@ struct Line {
enum DefinitionState {
InClass(TypeParamsState),
InFunction(TypeParamsState),
NotInClassOrFunction,
InTypeAlias(TypeParamsState),
NotInDefinition,
}
impl DefinitionState {
@ -494,11 +495,12 @@ impl DefinitionState {
TokenKind::Async if matches!(token_kinds.next(), Some(TokenKind::Def)) => {
Self::InFunction(TypeParamsState::default())
}
_ => Self::NotInClassOrFunction,
TokenKind::Type => Self::InTypeAlias(TypeParamsState::default()),
_ => Self::NotInDefinition,
};
return state;
}
Self::NotInClassOrFunction
Self::NotInDefinition
}
const fn in_function_definition(self) -> bool {
@ -507,8 +509,10 @@ impl DefinitionState {
const fn type_params_state(self) -> Option<TypeParamsState> {
match self {
Self::InClass(state) | Self::InFunction(state) => Some(state),
Self::NotInClassOrFunction => None,
Self::InClass(state) | Self::InFunction(state) | Self::InTypeAlias(state) => {
Some(state)
}
Self::NotInDefinition => None,
}
}
@ -521,10 +525,10 @@ impl DefinitionState {
fn visit_token_kind(&mut self, token_kind: TokenKind) {
let type_params_state_mut = match self {
Self::InClass(type_params_state) | Self::InFunction(type_params_state) => {
type_params_state
}
Self::NotInClassOrFunction => return,
Self::InClass(type_params_state)
| Self::InFunction(type_params_state)
| Self::InTypeAlias(type_params_state) => type_params_state,
Self::NotInDefinition => return,
};
match token_kind {
TokenKind::Lpar if type_params_state_mut.before_type_params() => {

View file

@ -186,3 +186,5 @@ E22.py:184:5: E221 [*] Multiple spaces before operator
184 |-if a == 1:
184 |+if a == 1:
185 185 | print(a)
186 186 |
187 187 |

View file

@ -123,3 +123,5 @@ E22.py:184:9: E222 [*] Multiple spaces after operator
184 |-if a == 1:
184 |+if a == 1:
185 185 | print(a)
186 186 |
187 187 |