Skip to content

Commit

Permalink
Update UP032 to unescape curly braces in literal parts of converted s…
Browse files Browse the repository at this point in the history
…trings (#8697)

Closes #8694
  • Loading branch information
zanieb authored Nov 16, 2023
1 parent dda31b6 commit d1e88dc
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 13 deletions.
17 changes: 17 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pyupgrade/UP032_0.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,20 @@ async def c():
"".format(new_dict, d)

)

# The first string will be converted to an f-string and the curly braces in the second should be converted to be unescaped
(
"{}"
"{{}}"
).format(a)

("{}" "{{}}").format(a)


# Both strings will be converted to an f-string and the curly braces in the second should left escaped
(
"{}"
"{{{}}}"
).format(a, b)

("{}" "{{{}}}").format(a, b)
13 changes: 13 additions & 0 deletions crates/ruff_linter/src/rules/pyupgrade/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,16 @@ pub(super) fn curly_escape(text: &str) -> Cow<'_, str> {
}
})
}

static DOUBLE_CURLY_BRACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"((\{\{)|(\}\}))").unwrap());

pub(super) fn curly_unescape(text: &str) -> Cow<'_, str> {
// Match all double curly braces and replace with a single
DOUBLE_CURLY_BRACES.replace_all(text, |caps: &Captures| {
if &caps[1] == "{{" {
"{".to_string()
} else {
"}".to_string()
}
})
}
10 changes: 6 additions & 4 deletions crates/ruff_linter/src/rules/pyupgrade/rules/f_strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::checkers::ast::Checker;
use crate::fix::edits::fits_or_shrinks;

use crate::rules::pyflakes::format::FormatSummary;
use crate::rules::pyupgrade::helpers::curly_escape;
use crate::rules::pyupgrade::helpers::{curly_escape, curly_unescape};

/// ## What it does
/// Checks for `str.format` calls that can be replaced with f-strings.
Expand Down Expand Up @@ -357,9 +357,11 @@ pub(crate) fn f_strings(
Some((Tok::String { .. }, range)) => {
match try_convert_to_f_string(range, &mut summary, checker.locator()) {
Ok(Some(fstring)) => patches.push((range, fstring)),
// Skip any strings that don't require conversion (e.g., literal segments of an
// implicit concatenation).
Ok(None) => continue,
// Convert escaped curly brackets e.g. `{{` to `{` in literal string parts
Ok(None) => patches.push((
range,
curly_unescape(checker.locator().slice(range)).to_string(),
)),
// If any of the segments fail to convert, then we can't convert the entire
// expression.
Err(_) => return,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -995,10 +995,11 @@ UP032_0.py:212:18: UP032 [*] Use f-string instead of `format` call
211 211 | # When fixing, trim the trailing empty string.
212 |-raise ValueError("Conflicting configuration dicts: {!r} {!r}"
213 |- "".format(new_dict, d))
212 |+raise ValueError(f"Conflicting configuration dicts: {new_dict!r} {d!r}")
214 213 |
215 214 | # When fixing, trim the trailing empty string.
216 215 | raise ValueError("Conflicting configuration dicts: {!r} {!r}"
212 |+raise ValueError(f"Conflicting configuration dicts: {new_dict!r} {d!r}"
213 |+ "")
214 214 |
215 215 | # When fixing, trim the trailing empty string.
216 216 | raise ValueError("Conflicting configuration dicts: {!r} {!r}"

UP032_0.py:216:18: UP032 [*] Use f-string instead of `format` call
|
Expand Down Expand Up @@ -1041,9 +1042,10 @@ UP032_0.py:220:5: UP032 [*] Use f-string instead of `format` call
220 |- "Conflicting configuration dicts: {!r} {!r}"
221 |- "".format(new_dict, d)
220 |+ f"Conflicting configuration dicts: {new_dict!r} {d!r}"
222 221 | )
223 222 |
224 223 | raise ValueError(
221 |+ ""
222 222 | )
223 223 |
224 224 | raise ValueError(

UP032_0.py:225:5: UP032 [*] Use f-string instead of `format` call
|
Expand All @@ -1064,7 +1066,96 @@ UP032_0.py:225:5: UP032 [*] Use f-string instead of `format` call
225 |- "Conflicting configuration dicts: {!r} {!r}"
226 |- "".format(new_dict, d)
225 |+ f"Conflicting configuration dicts: {new_dict!r} {d!r}"
227 226 |
228 227 | )
226 |+ ""
227 227 |
228 228 | )
229 229 |

UP032_0.py:231:1: UP032 [*] Use f-string instead of `format` call
|
230 | # The first string will be converted to an f-string and the curly braces in the second should be converted to be unescaped
231 | / (
232 | | "{}"
233 | | "{{}}"
234 | | ).format(a)
| |___________^ UP032
235 |
236 | ("{}" "{{}}").format(a)
|
= help: Convert to f-string

Safe fix
229 229 |
230 230 | # The first string will be converted to an f-string and the curly braces in the second should be converted to be unescaped
231 231 | (
232 |+ f"{a}"
232 233 | "{}"
233 |- "{{}}"
234 |-).format(a)
234 |+)
235 235 |
236 236 | ("{}" "{{}}").format(a)
237 237 |

UP032_0.py:236:1: UP032 [*] Use f-string instead of `format` call
|
234 | ).format(a)
235 |
236 | ("{}" "{{}}").format(a)
| ^^^^^^^^^^^^^^^^^^^^^^^ UP032
|
= help: Convert to f-string

Safe fix
233 233 | "{{}}"
234 234 | ).format(a)
235 235 |
236 |-("{}" "{{}}").format(a)
236 |+(f"{a}" "{}")
237 237 |
238 238 |
239 239 | # Both strings will be converted to an f-string and the curly braces in the second should left escaped

UP032_0.py:240:1: UP032 [*] Use f-string instead of `format` call
|
239 | # Both strings will be converted to an f-string and the curly braces in the second should left escaped
240 | / (
241 | | "{}"
242 | | "{{{}}}"
243 | | ).format(a, b)
| |______________^ UP032
244 |
245 | ("{}" "{{{}}}").format(a, b)
|
= help: Convert to f-string

Safe fix
238 238 |
239 239 | # Both strings will be converted to an f-string and the curly braces in the second should left escaped
240 240 | (
241 |- "{}"
242 |- "{{{}}}"
243 |-).format(a, b)
241 |+ f"{a}"
242 |+ f"{{{b}}}"
243 |+)
244 244 |
245 245 | ("{}" "{{{}}}").format(a, b)

UP032_0.py:245:1: UP032 [*] Use f-string instead of `format` call
|
243 | ).format(a, b)
244 |
245 | ("{}" "{{{}}}").format(a, b)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP032
|
= help: Convert to f-string

Safe fix
242 242 | "{{{}}}"
243 243 | ).format(a, b)
244 244 |
245 |-("{}" "{{{}}}").format(a, b)
245 |+(f"{a}" f"{{{b}}}")


0 comments on commit d1e88dc

Please sign in to comment.