mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 14:21:24 +00:00
[pycodestyle
] Handle brace escapes for t-strings in logical lines (#19358)
Tracks both f and t-strings in the logical line rules for `pycodestyle`. Progress towards #15506
This commit is contained in:
parent
f4d0273532
commit
00e7d1ffd6
8 changed files with 145 additions and 14 deletions
|
@ -189,3 +189,18 @@ f"{ham[lower + 1 :, "columnname"]}"
|
||||||
#: Okay: https://github.com/astral-sh/ruff/issues/12023
|
#: Okay: https://github.com/astral-sh/ruff/issues/12023
|
||||||
f"{x = :.2f}"
|
f"{x = :.2f}"
|
||||||
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"]}"
|
||||||
|
|
||||||
|
|
|
@ -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]):
|
class PEP696GoodWithNonEmptyBases[A: object="foo"[::-1], B: object =[[["foo", "bar"]]], C: object= bytes](object, something_dynamic[x::-1]):
|
||||||
pass
|
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}
|
||||||
|
|
|
@ -126,7 +126,7 @@ impl AlwaysFixableViolation for WhitespaceBeforePunctuation {
|
||||||
|
|
||||||
/// E201, E202, E203
|
/// E201, E202, E203
|
||||||
pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) {
|
pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) {
|
||||||
let mut fstrings = 0u32;
|
let mut interpolated_strings = 0u32;
|
||||||
let mut brackets = vec![];
|
let mut brackets = vec![];
|
||||||
let mut prev_token = None;
|
let mut prev_token = None;
|
||||||
let mut iter = line.tokens().iter().peekable();
|
let mut iter = line.tokens().iter().peekable();
|
||||||
|
@ -134,8 +134,10 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) {
|
||||||
while let Some(token) = iter.next() {
|
while let Some(token) = iter.next() {
|
||||||
let kind = token.kind();
|
let kind = token.kind();
|
||||||
match kind {
|
match kind {
|
||||||
TokenKind::FStringStart => fstrings += 1,
|
TokenKind::FStringStart | TokenKind::TStringStart => interpolated_strings += 1,
|
||||||
TokenKind::FStringEnd => fstrings = fstrings.saturating_sub(1),
|
TokenKind::FStringEnd | TokenKind::TStringEnd => {
|
||||||
|
interpolated_strings = interpolated_strings.saturating_sub(1);
|
||||||
|
}
|
||||||
TokenKind::Lsqb => {
|
TokenKind::Lsqb => {
|
||||||
brackets.push(kind);
|
brackets.push(kind);
|
||||||
}
|
}
|
||||||
|
@ -161,7 +163,9 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) {
|
||||||
// Here, `{{` / `}} would be interpreted as a single raw `{` / `}`
|
// Here, `{{` / `}} would be interpreted as a single raw `{` / `}`
|
||||||
// character.
|
// character.
|
||||||
match symbol {
|
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);
|
let (trailing, trailing_len) = line.trailing_whitespace(token);
|
||||||
if !matches!(trailing, Whitespace::None) {
|
if !matches!(trailing, Whitespace::None) {
|
||||||
if let Some(mut diagnostic) = context.report_diagnostic_if_enabled(
|
if let Some(mut diagnostic) = context.report_diagnostic_if_enabled(
|
||||||
|
@ -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 !matches!(prev_token, Some(TokenKind::Comma)) {
|
||||||
if let (Whitespace::Single | Whitespace::Many | Whitespace::Tab, offset) =
|
if let (Whitespace::Single | Whitespace::Many | Whitespace::Tab, offset) =
|
||||||
line.leading_whitespace(token)
|
line.leading_whitespace(token)
|
||||||
|
@ -286,7 +292,7 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &LintContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if fstrings > 0
|
if interpolated_strings > 0
|
||||||
&& symbol == ':'
|
&& symbol == ':'
|
||||||
&& matches!(prev_token, Some(TokenKind::Equal))
|
&& matches!(prev_token, Some(TokenKind::Equal))
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,7 +41,7 @@ impl AlwaysFixableViolation for MissingWhitespace {
|
||||||
|
|
||||||
/// E231
|
/// E231
|
||||||
pub(crate) fn missing_whitespace(line: &LogicalLine, context: &LintContext) {
|
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 definition_state = DefinitionState::from_tokens(line.tokens());
|
||||||
let mut brackets = Vec::new();
|
let mut brackets = Vec::new();
|
||||||
let mut iter = line.tokens().iter().peekable();
|
let mut iter = line.tokens().iter().peekable();
|
||||||
|
@ -50,21 +50,23 @@ pub(crate) fn missing_whitespace(line: &LogicalLine, context: &LintContext) {
|
||||||
let kind = token.kind();
|
let kind = token.kind();
|
||||||
definition_state.visit_token_kind(kind);
|
definition_state.visit_token_kind(kind);
|
||||||
match kind {
|
match kind {
|
||||||
TokenKind::FStringStart => fstrings += 1,
|
TokenKind::FStringStart | TokenKind::TStringStart => interpolated_strings += 1,
|
||||||
TokenKind::FStringEnd => fstrings = fstrings.saturating_sub(1),
|
TokenKind::FStringEnd | TokenKind::TStringEnd => {
|
||||||
TokenKind::Lsqb if fstrings == 0 => {
|
interpolated_strings = interpolated_strings.saturating_sub(1);
|
||||||
|
}
|
||||||
|
TokenKind::Lsqb if interpolated_strings == 0 => {
|
||||||
brackets.push(kind);
|
brackets.push(kind);
|
||||||
}
|
}
|
||||||
TokenKind::Rsqb if fstrings == 0 => {
|
TokenKind::Rsqb if interpolated_strings == 0 => {
|
||||||
brackets.pop();
|
brackets.pop();
|
||||||
}
|
}
|
||||||
TokenKind::Lbrace if fstrings == 0 => {
|
TokenKind::Lbrace if interpolated_strings == 0 => {
|
||||||
brackets.push(kind);
|
brackets.push(kind);
|
||||||
}
|
}
|
||||||
TokenKind::Rbrace if fstrings == 0 => {
|
TokenKind::Rbrace if interpolated_strings == 0 => {
|
||||||
brackets.pop();
|
brackets.pop();
|
||||||
}
|
}
|
||||||
TokenKind::Colon if fstrings > 0 => {
|
TokenKind::Colon if interpolated_strings > 0 => {
|
||||||
// Colon in f-string, no space required. This will yield false
|
// Colon in f-string, no space required. This will yield false
|
||||||
// negatives for cases like the following as it's hard to
|
// negatives for cases like the following as it's hard to
|
||||||
// differentiate between the usage of a colon in a f-string.
|
// differentiate between the usage of a colon in a f-string.
|
||||||
|
|
|
@ -183,3 +183,23 @@ E20.py:145:5: E201 [*] Whitespace after '['
|
||||||
146 146 |
|
146 146 |
|
||||||
147 147 | #: Okay
|
147 147 | #: Okay
|
||||||
148 148 | ham[lower + offset :: upper + offset]
|
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}"
|
||||||
|
|
|
@ -165,3 +165,23 @@ E20.py:172:12: E202 [*] Whitespace before ']'
|
||||||
173 173 |
|
173 173 |
|
||||||
174 174 | #: E203:1:10
|
174 174 | #: E203:1:10
|
||||||
175 175 | ham[upper :]
|
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}"
|
||||||
|
|
|
@ -345,3 +345,19 @@ E20.py:187:17: E203 [*] Whitespace before ':'
|
||||||
188 188 |
|
188 188 |
|
||||||
189 189 | #: Okay: https://github.com/astral-sh/ruff/issues/12023
|
189 189 | #: Okay: https://github.com/astral-sh/ruff/issues/12023
|
||||||
190 190 | f"{x = :.2f}"
|
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 |
|
||||||
|
|
|
@ -905,3 +905,38 @@ E23.py:126:99: E231 [*] Missing whitespace after ':'
|
||||||
127 127 | pass
|
127 127 | pass
|
||||||
128 128 |
|
128 128 |
|
||||||
129 129 | # Should be no E231 errors on any of these:
|
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…
Add table
Add a link
Reference in a new issue