Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pycodestyle/E20.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,18 @@
#: Okay: https://github.com/astral-sh/ruff/issues/12023
f"{x = :.2f}"
f"{(x) = :.2f}"

# t-strings
t"{ {'a': 1} }"
t"{[ { {'a': 1} } ]}"
t"normal { {t"{ { [1, 2] } }" } } normal"

t"{x = :.2f}"
t"{(x) = :.2f}"

#: Okay
t"{ham[lower +1 :, "columnname"]}"

#: E203:1:13
t"{ham[lower + 1 :, "columnname"]}"

17 changes: 17 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pycodestyle/E23.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,20 @@ class PEP696GoodWithEmptyBases[A: object="foo"[::-1], B: object =[[["foo", "bar"

class PEP696GoodWithNonEmptyBases[A: object="foo"[::-1], B: object =[[["foo", "bar"]]], C: object= bytes](object, something_dynamic[x::-1]):
pass

# E231
t"{(a,b)}"

# Okay because it's hard to differentiate between the usages of a colon in a t-string
t"{a:=1}"
t"{ {'a':1} }"
t"{a:.3f}"
t"{(a:=1)}"
t"{(lambda x:x)}"
t"normal{t"{a:.3f}"}normal"

#: Okay
snapshot.file_uri[len(t's3://{self.s3_bucket_name}/'):]

#: E231
{len(t's3://{self.s3_bucket_name}/'):1}
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,18 @@ impl AlwaysFixableViolation for WhitespaceBeforePunctuation {

/// E201, E202, E203
pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) {
let mut fstrings = 0u32;
let mut interpolated_strings = 0u32;
let mut brackets = vec![];
let mut prev_token = None;
let mut iter = line.tokens().iter().peekable();

while let Some(token) = iter.next() {
let kind = token.kind();
match kind {
TokenKind::FStringStart => fstrings += 1,
TokenKind::FStringEnd => fstrings = fstrings.saturating_sub(1),
TokenKind::FStringStart | TokenKind::TStringStart => interpolated_strings += 1,
TokenKind::FStringEnd | TokenKind::TStringEnd => {
interpolated_strings = interpolated_strings.saturating_sub(1);
}
TokenKind::Lsqb => {
brackets.push(kind);
}
Expand All @@ -161,7 +163,9 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) {
// Here, `{{` / `}} would be interpreted as a single raw `{` / `}`
// character.
match symbol {
BracketOrPunctuation::OpenBracket(symbol) if symbol != '{' || fstrings == 0 => {
BracketOrPunctuation::OpenBracket(symbol)
if symbol != '{' || interpolated_strings == 0 =>
{
let (trailing, trailing_len) = line.trailing_whitespace(token);
if !matches!(trailing, Whitespace::None) {
if let Some(mut diagnostic) = context.report_diagnostic_if_enabled(
Expand All @@ -173,7 +177,9 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) {
}
}
}
BracketOrPunctuation::CloseBracket(symbol) if symbol != '}' || fstrings == 0 => {
BracketOrPunctuation::CloseBracket(symbol)
if symbol != '}' || interpolated_strings == 0 =>
{
if !matches!(prev_token, Some(TokenKind::Comma)) {
if let (Whitespace::Single | Whitespace::Many | Whitespace::Tab, offset) =
line.leading_whitespace(token)
Expand Down Expand Up @@ -286,7 +292,7 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) {
}
}
} else {
if fstrings > 0
if interpolated_strings > 0
&& symbol == ':'
&& matches!(prev_token, Some(TokenKind::Equal))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl AlwaysFixableViolation for MissingWhitespace {

/// E231
pub(crate) fn missing_whitespace(line: &LogicalLine, context: &LintContext) {
let mut fstrings = 0u32;
let mut interpolated_strings = 0u32;
let mut definition_state = DefinitionState::from_tokens(line.tokens());
let mut brackets = Vec::new();
let mut iter = line.tokens().iter().peekable();
Expand All @@ -50,21 +50,23 @@ pub(crate) fn missing_whitespace(line: &LogicalLine, context: &LintContext) {
let kind = token.kind();
definition_state.visit_token_kind(kind);
match kind {
TokenKind::FStringStart => fstrings += 1,
TokenKind::FStringEnd => fstrings = fstrings.saturating_sub(1),
TokenKind::Lsqb if fstrings == 0 => {
TokenKind::FStringStart | TokenKind::TStringStart => interpolated_strings += 1,
TokenKind::FStringEnd | TokenKind::TStringEnd => {
interpolated_strings = interpolated_strings.saturating_sub(1);
}
TokenKind::Lsqb if interpolated_strings == 0 => {
brackets.push(kind);
}
TokenKind::Rsqb if fstrings == 0 => {
TokenKind::Rsqb if interpolated_strings == 0 => {
brackets.pop();
}
TokenKind::Lbrace if fstrings == 0 => {
TokenKind::Lbrace if interpolated_strings == 0 => {
brackets.push(kind);
}
TokenKind::Rbrace if fstrings == 0 => {
TokenKind::Rbrace if interpolated_strings == 0 => {
brackets.pop();
}
TokenKind::Colon if fstrings > 0 => {
TokenKind::Colon if interpolated_strings > 0 => {
// Colon in f-string, no space required. This will yield false
// negatives for cases like the following as it's hard to
// differentiate between the usage of a colon in a f-string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,23 @@ E20.py:145:5: E201 [*] Whitespace after '['
146 146 |
147 147 | #: Okay
148 148 | ham[lower + offset :: upper + offset]

E20.py:195:5: E201 [*] Whitespace after '['
|
193 | # t-strings
194 | t"{ {'a': 1} }"
195 | t"{[ { {'a': 1} } ]}"
| ^ E201
196 | t"normal { {t"{ { [1, 2] } }" } } normal"
|
= help: Remove whitespace before '['

ℹ Safe fix
192 192 |
193 193 | # t-strings
194 194 | t"{ {'a': 1} }"
195 |-t"{[ { {'a': 1} } ]}"
195 |+t"{[{ {'a': 1} } ]}"
196 196 | t"normal { {t"{ { [1, 2] } }" } } normal"
197 197 |
198 198 | t"{x = :.2f}"
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,23 @@ E20.py:172:12: E202 [*] Whitespace before ']'
173 173 |
174 174 | #: E203:1:10
175 175 | ham[upper :]

E20.py:195:18: E202 [*] Whitespace before ']'
|
193 | # t-strings
194 | t"{ {'a': 1} }"
195 | t"{[ { {'a': 1} } ]}"
| ^ E202
196 | t"normal { {t"{ { [1, 2] } }" } } normal"
|
= help: Remove whitespace before ']'

ℹ Safe fix
192 192 |
193 193 | # t-strings
194 194 | t"{ {'a': 1} }"
195 |-t"{[ { {'a': 1} } ]}"
195 |+t"{[ { {'a': 1} }]}"
196 196 | t"normal { {t"{ { [1, 2] } }" } } normal"
197 197 |
198 198 | t"{x = :.2f}"
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,19 @@ E20.py:187:17: E203 [*] Whitespace before ':'
188 188 |
189 189 | #: Okay: https://github.com/astral-sh/ruff/issues/12023
190 190 | f"{x = :.2f}"

E20.py:205:17: E203 [*] Whitespace before ':'
|
204 | #: E203:1:13
205 | t"{ham[lower + 1 :, "columnname"]}"
| ^^ E203
|
= help: Remove whitespace before ':'

ℹ Safe fix
202 202 | t"{ham[lower +1 :, "columnname"]}"
203 203 |
204 204 | #: E203:1:13
205 |-t"{ham[lower + 1 :, "columnname"]}"
205 |+t"{ham[lower + 1:, "columnname"]}"
206 206 |
Original file line number Diff line number Diff line change
Expand Up @@ -905,3 +905,38 @@ E23.py:126:99: E231 [*] Missing whitespace after ':'
127 127 | pass
128 128 |
129 129 | # Should be no E231 errors on any of these:

E23.py:147:6: E231 [*] Missing whitespace after ','
|
146 | # E231
147 | t"{(a,b)}"
| ^ E231
148 |
149 | # Okay because it's hard to differentiate between the usages of a colon in a t-string
|
= help: Add missing whitespace

ℹ Safe fix
144 144 | pass
145 145 |
146 146 | # E231
147 |-t"{(a,b)}"
147 |+t"{(a, b)}"
148 148 |
149 149 | # Okay because it's hard to differentiate between the usages of a colon in a t-string
150 150 | t"{a:=1}"

E23.py:161:37: E231 [*] Missing whitespace after ':'
|
160 | #: E231
161 | {len(t's3://{self.s3_bucket_name}/'):1}
| ^ E231
|
= help: Add missing whitespace

ℹ Safe fix
158 158 | snapshot.file_uri[len(t's3://{self.s3_bucket_name}/'):]
159 159 |
160 160 | #: E231
161 |-{len(t's3://{self.s3_bucket_name}/'):1}
161 |+{len(t's3://{self.s3_bucket_name}/'): 1}
Loading