-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for nested replacements inside format specifications #6616
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you extend the PR summary with what parsing logic you changed? It is difficult to me to review this PR because I'm unfamiliar with format specifications and format.rs
Ya'll are too fast! I'm still working on this — I should have opened it as a draft but was in the process of writing up the body. I added support for a replacement at the end of the specification (before the format type) but as Charlie has noted I've realized that we actually need to support any of the items in specification being a replacement. I'll need to consider a bit further the best way to accomplish this. |
We might need to just detect that the spec has nested parts, and return some other representation that isn't really analyzable by downstream rules (since we can't know how the spec will be resolved). |
let replacements = RefCell::new(vec![]); | ||
// get_integer in CPython | ||
let text = parse_nested_format_part(&replacements, text)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would love suggestions here as this feels awkward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternative, I guess, is to traverse the entire string twice. Once to collect and remove all of the replacements and a second pass on the cleaned text.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit tedious but I don't know that I have much better.
My decision to just extend |
What do you mean by "drop replacements"? Can you give an example? |
Sure! The following test will pass assert_eq!(
FormatSpec::parse("{x}d")
.unwrap()
.format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
Ok("16".to_owned())
); but |
With e5d6ec3 I've updated PLE1300 to lint nested replacements as well. |
Ahhh I see, you're referring to the methods that actually use this to format things. Can we... remove those? I suspect they came from RustPython which needs to support these at runtime. Otherwise, I'd suggest returning an error in those cases. |
PR Check ResultsEcosystem✅ ecosystem check detected no changes. BenchmarkLinux
Windows
|
Great that works for me #6624 |
20 | "{:{s:y}}".format("hello", s="s") # [bad-format-character] (nested replacement format spec checked) | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLE1300 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd really like better range information here. Would that be hard for me to add to FormatSpec
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be challenging because the thing we get on the left-hand side is the parsed representation which doesn't always match the literal representation. For example, the string on the left could be an implicit concatenation, or it could contain escaped characters, etc...
In #6616 we are adding support for nested replacements in format specifiers which makes actually formatting strings infeasible without a great deal of complexity. Since we're not using these functions (they just exist for runtime use in RustPython), we can just remove them.
let format_part = FormatString::parse_part_in_brackets(&left) | ||
.map_err(FormatSpecError::InvalidReplacement)?; | ||
parts.push(format_part); | ||
Ok(&right[1..]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right
is guaranteed to be non-empty because the first character is the closing bracket?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess? Is split_at(idx + 1)
clearer? I added a couple test cases and can't make it fail.
], | ||
}); | ||
|
||
assert_eq!(FormatString::from_str("abcd{1:{a}}"), expected); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not for this PR, but I feel like these are good candidates for insta::assert_debug_snapshot
, so that we don't have to write out and maintain the expectations manually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, I tried to match the existing pattern but I agree that's more maintainable.
InvalidFormatSpecifier, | ||
PlaceholderRecursionExceeded, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The former was only used to represent the latter which I found confusing and unnecessarily vague.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to your question about better ranges. I have the impression that using Cursor
instead of manually constructing strings and iterating over chars could simplify the implementation significantly (and speed it up!). We could even decide to build a small lexer that tokenized the strings, simplifying the "parsing" part.
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [ruff](https://beta.ruff.rs/docs) ([source](https://github.com/astral-sh/ruff), [changelog](https://github.com/astral-sh/ruff/releases)) | `==0.0.284` -> `==0.0.285` | [![age](https://developer.mend.io/api/mc/badges/age/pypi/ruff/0.0.285?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/ruff/0.0.285?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/ruff/0.0.284/0.0.285?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/ruff/0.0.284/0.0.285?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>astral-sh/ruff (ruff)</summary> ### [`v0.0.285`](https://github.com/astral-sh/ruff/releases/tag/v0.0.285) [Compare Source](https://github.com/astral-sh/ruff/compare/v0.0.284...v0.0.285) #### What's Changed ##### New rules - \[`flake8-pytest-style`] Implement `pytest-unittest-raises-assertion` (`PT027`) by [@​harupy](https://github.com/harupy) in [https://github.com/astral-sh/ruff/pull/6554](https://github.com/astral-sh/ruff/pull/6554) - \[`flake8-pytest-style`] Implement `pytest-duplicate-parametrize-test-cases` (`PT014`) by [@​harupy](https://github.com/harupy) in [https://github.com/astral-sh/ruff/pull/6598](https://github.com/astral-sh/ruff/pull/6598) - \[`flake8-tidy-imports`] Implement `banned-module-level-imports` (`TID253`) by [@​durumu](https://github.com/durumu) in [https://github.com/astral-sh/ruff/pull/6378](https://github.com/astral-sh/ruff/pull/6378) - \[`pylint`] Implement `bad-dunder-name` (`W3201`) (in the Ruff nursery) by [@​LaBatata101](https://github.com/LaBatata101) in [https://github.com/astral-sh/ruff/pull/6486](https://github.com/astral-sh/ruff/pull/6486) - \[`pylint`] Implement `subprocess-run-check` (`W1510`) by [@​tjkuson](https://github.com/tjkuson) in [https://github.com/astral-sh/ruff/pull/6487](https://github.com/astral-sh/ruff/pull/6487) - \[`ruff`] Implement `quadratic-list-summation` (`RUF017`) by [@​evanrittenhouse](https://github.com/evanrittenhouse) in [https://github.com/astral-sh/ruff/pull/6489](https://github.com/astral-sh/ruff/pull/6489) ##### Rule changes - \[`flake8-bugbear`] Add autofix for `B006` by [@​qdegraaf](https://github.com/qdegraaf) in [https://github.com/astral-sh/ruff/pull/6131](https://github.com/astral-sh/ruff/pull/6131) - \[`flake8-pyi`] Avoid applying `PYI055` to runtime-evaluated annotations by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6457](https://github.com/astral-sh/ruff/pull/6457) - \[`flake8-self`] Allow `os._exit` accesses in `SLF001` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6490](https://github.com/astral-sh/ruff/pull/6490) - \[`perflint`] Ignore `PERF203` if `try` contains loop control flow statements by [@​evanrittenhouse](https://github.com/evanrittenhouse) in [https://github.com/astral-sh/ruff/pull/6536](https://github.com/astral-sh/ruff/pull/6536) - \[`pylint`] Check for invalid format type specifiers in nested replacements for `PLE1300` by [@​zanieb](https://github.com/zanieb) in [https://github.com/astral-sh/ruff/pull/6616](https://github.com/astral-sh/ruff/pull/6616) - \[`tryceratops`] Omit `NotImplementedError` from `TRY003` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6568](https://github.com/astral-sh/ruff/pull/6568) ##### Settings - Respect `.ipynb` and `.pyi` sources when linting from stdin by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6628](https://github.com/astral-sh/ruff/pull/6628) - Support glob patterns for `raises_require_match_for` and `raises_require_match_for` by [@​harupy](https://github.com/harupy) in [https://github.com/astral-sh/ruff/pull/6635](https://github.com/astral-sh/ruff/pull/6635) ##### Bug Fixes - Make `lambda-assignment` fix always-manual in class bodies by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6626](https://github.com/astral-sh/ruff/pull/6626) - Fix counting of message arguments when msg is provided as a keyword by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6456](https://github.com/astral-sh/ruff/pull/6456) - Add container types to `E721` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6469](https://github.com/astral-sh/ruff/pull/6469) - Respect scoping rules when identifying builtins by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6468](https://github.com/astral-sh/ruff/pull/6468) - Respect tab width in line-length heuristic by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6491](https://github.com/astral-sh/ruff/pull/6491) - Respect dummy-variable-rgx for unused bound exceptions by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6492](https://github.com/astral-sh/ruff/pull/6492) - Fix detection of top-level imports with newlines in `E402` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6526](https://github.com/astral-sh/ruff/pull/6526) - Allow if-expression with dual string arms in `invalid-envvar-value` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6538](https://github.com/astral-sh/ruff/pull/6538) - Add deprecated unittest assertions to PT009 by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6572](https://github.com/astral-sh/ruff/pull/6572) - Avoid unused argument rules when functions call `locals()` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6578](https://github.com/astral-sh/ruff/pull/6578) - Allow top-level `await` in Jupyter notebooks by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6607](https://github.com/astral-sh/ruff/pull/6607) - Don't detect `pandas#values` for stores, deletes, or class accesses by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6631](https://github.com/astral-sh/ruff/pull/6631) - Avoid removing parentheses in `E712` fix by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6575](https://github.com/astral-sh/ruff/pull/6575) - Skip whitespace between comments at start of file e.g. for `I002` by [@​durumu](https://github.com/durumu) in [https://github.com/astral-sh/ruff/pull/6523](https://github.com/astral-sh/ruff/pull/6523) - Add support for nested replacements inside format specifications e.g. for `PLE1300` by [@​zanieb](https://github.com/zanieb) in [https://github.com/astral-sh/ruff/pull/6616](https://github.com/astral-sh/ruff/pull/6616) ##### Playground - Shared playground links now use short URLs by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6383](https://github.com/astral-sh/ruff/pull/6383) - Fix possible JSON parse error on playground load by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6519](https://github.com/astral-sh/ruff/pull/6519) - Fix unreachable panic in playground by [@​MichaReiser](https://github.com/MichaReiser) in [https://github.com/astral-sh/ruff/pull/6623](https://github.com/astral-sh/ruff/pull/6623) ##### Performance - Improve tokenizer performance for ASCII only identifiers by [@​MichaReiser](https://github.com/MichaReiser) in [https://github.com/astral-sh/ruff/pull/6609](https://github.com/astral-sh/ruff/pull/6609) #### New Contributors - [@​magic-akari](https://github.com/magic-akari) made their first contribution in [https://github.com/astral-sh/ruff/pull/6472](https://github.com/astral-sh/ruff/pull/6472) - [@​durumu](https://github.com/durumu) made their first contribution in [https://github.com/astral-sh/ruff/pull/6378](https://github.com/astral-sh/ruff/pull/6378) - [@​jamesbraza](https://github.com/jamesbraza) made their first contribution in [https://github.com/astral-sh/ruff/pull/6520](https://github.com/astral-sh/ruff/pull/6520) - [@​takumaw](https://github.com/takumaw) made their first contribution in [https://github.com/astral-sh/ruff/pull/6533](https://github.com/astral-sh/ruff/pull/6533) - [@​noklam](https://github.com/noklam) made their first contribution in [https://github.com/astral-sh/ruff/pull/6573](https://github.com/astral-sh/ruff/pull/6573) - [@​Teraskull](https://github.com/Teraskull) made their first contribution in [https://github.com/astral-sh/ruff/pull/6605](https://github.com/astral-sh/ruff/pull/6605) **Full Changelog**: astral-sh/ruff@v0.0.284...v0.0.285 </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/allenporter/pyrainbird). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNi40My4yIiwidXBkYXRlZEluVmVyIjoiMzYuNDMuMiIsInRhcmdldEJyYW5jaCI6Im1haW4ifQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [ruff](https://beta.ruff.rs/docs) ([source](https://github.com/astral-sh/ruff), [changelog](https://github.com/astral-sh/ruff/releases)) | `==0.0.284` -> `==0.0.285` | [![age](https://developer.mend.io/api/mc/badges/age/pypi/ruff/0.0.285?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/pypi/ruff/0.0.285?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/pypi/ruff/0.0.284/0.0.285?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/ruff/0.0.284/0.0.285?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>astral-sh/ruff (ruff)</summary> ### [`v0.0.285`](https://github.com/astral-sh/ruff/releases/tag/v0.0.285) [Compare Source](https://github.com/astral-sh/ruff/compare/v0.0.284...v0.0.285) #### What's Changed ##### New rules - \[`flake8-pytest-style`] Implement `pytest-unittest-raises-assertion` (`PT027`) by [@​harupy](https://github.com/harupy) in [https://github.com/astral-sh/ruff/pull/6554](https://github.com/astral-sh/ruff/pull/6554) - \[`flake8-pytest-style`] Implement `pytest-duplicate-parametrize-test-cases` (`PT014`) by [@​harupy](https://github.com/harupy) in [https://github.com/astral-sh/ruff/pull/6598](https://github.com/astral-sh/ruff/pull/6598) - \[`flake8-tidy-imports`] Implement `banned-module-level-imports` (`TID253`) by [@​durumu](https://github.com/durumu) in [https://github.com/astral-sh/ruff/pull/6378](https://github.com/astral-sh/ruff/pull/6378) - \[`pylint`] Implement `bad-dunder-name` (`W3201`) (in the Ruff nursery) by [@​LaBatata101](https://github.com/LaBatata101) in [https://github.com/astral-sh/ruff/pull/6486](https://github.com/astral-sh/ruff/pull/6486) - \[`pylint`] Implement `subprocess-run-check` (`W1510`) by [@​tjkuson](https://github.com/tjkuson) in [https://github.com/astral-sh/ruff/pull/6487](https://github.com/astral-sh/ruff/pull/6487) - \[`ruff`] Implement `quadratic-list-summation` (`RUF017`) by [@​evanrittenhouse](https://github.com/evanrittenhouse) in [https://github.com/astral-sh/ruff/pull/6489](https://github.com/astral-sh/ruff/pull/6489) ##### Rule changes - \[`flake8-bugbear`] Add autofix for `B006` by [@​qdegraaf](https://github.com/qdegraaf) in [https://github.com/astral-sh/ruff/pull/6131](https://github.com/astral-sh/ruff/pull/6131) - \[`flake8-pyi`] Avoid applying `PYI055` to runtime-evaluated annotations by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6457](https://github.com/astral-sh/ruff/pull/6457) - \[`flake8-self`] Allow `os._exit` accesses in `SLF001` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6490](https://github.com/astral-sh/ruff/pull/6490) - \[`perflint`] Ignore `PERF203` if `try` contains loop control flow statements by [@​evanrittenhouse](https://github.com/evanrittenhouse) in [https://github.com/astral-sh/ruff/pull/6536](https://github.com/astral-sh/ruff/pull/6536) - \[`pylint`] Check for invalid format type specifiers in nested replacements for `PLE1300` by [@​zanieb](https://github.com/zanieb) in [https://github.com/astral-sh/ruff/pull/6616](https://github.com/astral-sh/ruff/pull/6616) - \[`tryceratops`] Omit `NotImplementedError` from `TRY003` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6568](https://github.com/astral-sh/ruff/pull/6568) ##### Settings - Respect `.ipynb` and `.pyi` sources when linting from stdin by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6628](https://github.com/astral-sh/ruff/pull/6628) - Support glob patterns for `raises_require_match_for` and `raises_require_match_for` by [@​harupy](https://github.com/harupy) in [https://github.com/astral-sh/ruff/pull/6635](https://github.com/astral-sh/ruff/pull/6635) ##### Bug Fixes - Make `lambda-assignment` fix always-manual in class bodies by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6626](https://github.com/astral-sh/ruff/pull/6626) - Fix counting of message arguments when msg is provided as a keyword by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6456](https://github.com/astral-sh/ruff/pull/6456) - Add container types to `E721` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6469](https://github.com/astral-sh/ruff/pull/6469) - Respect scoping rules when identifying builtins by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6468](https://github.com/astral-sh/ruff/pull/6468) - Respect tab width in line-length heuristic by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6491](https://github.com/astral-sh/ruff/pull/6491) - Respect dummy-variable-rgx for unused bound exceptions by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6492](https://github.com/astral-sh/ruff/pull/6492) - Fix detection of top-level imports with newlines in `E402` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6526](https://github.com/astral-sh/ruff/pull/6526) - Allow if-expression with dual string arms in `invalid-envvar-value` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6538](https://github.com/astral-sh/ruff/pull/6538) - Add deprecated unittest assertions to PT009 by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6572](https://github.com/astral-sh/ruff/pull/6572) - Avoid unused argument rules when functions call `locals()` by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6578](https://github.com/astral-sh/ruff/pull/6578) - Allow top-level `await` in Jupyter notebooks by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6607](https://github.com/astral-sh/ruff/pull/6607) - Don't detect `pandas#values` for stores, deletes, or class accesses by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6631](https://github.com/astral-sh/ruff/pull/6631) - Avoid removing parentheses in `E712` fix by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6575](https://github.com/astral-sh/ruff/pull/6575) - Skip whitespace between comments at start of file e.g. for `I002` by [@​durumu](https://github.com/durumu) in [https://github.com/astral-sh/ruff/pull/6523](https://github.com/astral-sh/ruff/pull/6523) - Add support for nested replacements inside format specifications e.g. for `PLE1300` by [@​zanieb](https://github.com/zanieb) in [https://github.com/astral-sh/ruff/pull/6616](https://github.com/astral-sh/ruff/pull/6616) ##### Playground - Shared playground links now use short URLs by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6383](https://github.com/astral-sh/ruff/pull/6383) - Fix possible JSON parse error on playground load by [@​charliermarsh](https://github.com/charliermarsh) in [https://github.com/astral-sh/ruff/pull/6519](https://github.com/astral-sh/ruff/pull/6519) - Fix unreachable panic in playground by [@​MichaReiser](https://github.com/MichaReiser) in [https://github.com/astral-sh/ruff/pull/6623](https://github.com/astral-sh/ruff/pull/6623) ##### Performance - Improve tokenizer performance for ASCII only identifiers by [@​MichaReiser](https://github.com/MichaReiser) in [https://github.com/astral-sh/ruff/pull/6609](https://github.com/astral-sh/ruff/pull/6609) #### New Contributors - [@​magic-akari](https://github.com/magic-akari) made their first contribution in [https://github.com/astral-sh/ruff/pull/6472](https://github.com/astral-sh/ruff/pull/6472) - [@​durumu](https://github.com/durumu) made their first contribution in [https://github.com/astral-sh/ruff/pull/6378](https://github.com/astral-sh/ruff/pull/6378) - [@​jamesbraza](https://github.com/jamesbraza) made their first contribution in [https://github.com/astral-sh/ruff/pull/6520](https://github.com/astral-sh/ruff/pull/6520) - [@​takumaw](https://github.com/takumaw) made their first contribution in [https://github.com/astral-sh/ruff/pull/6533](https://github.com/astral-sh/ruff/pull/6533) - [@​noklam](https://github.com/noklam) made their first contribution in [https://github.com/astral-sh/ruff/pull/6573](https://github.com/astral-sh/ruff/pull/6573) - [@​Teraskull](https://github.com/Teraskull) made their first contribution in [https://github.com/astral-sh/ruff/pull/6605](https://github.com/astral-sh/ruff/pull/6605) **Full Changelog**: astral-sh/ruff@v0.0.284...v0.0.285 </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/allenporter/flux-local). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNi40My4yIiwidXBkYXRlZEluVmVyIjoiMzYuNDMuMiIsInRhcmdldEJyYW5jaCI6Im1haW4ifQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…re present (#6858) Closes #6767 Replaces #6773 (this cherry-picks some parts from there) Alternative to the approach introduced in #6616 which added support for placeholders in format specifications while retaining parsing of other format specification parts. The idea is that if there are placeholders in a format specification we will not attempt to glean semantic meaning from the other parts of the format specification we'll just extract all of the placeholders ignoring other characters. The dynamic content of placeholders can drastically change the meaning of the format specification in ways unknowable by static analysis. This change prevents false analysis and will ensure safety if we build other rules on top of this at the cost of missing detection of some bad specifications. Minor note: I've use "replacements" and "placeholders" interchangeably but am trying to go with "placeholder" as I think it's a better term for the static analysis concept here
Closes #6442
Python string formatting like
"hello {place}".format(place="world")
supports format specifications for replaced content such as"hello {place:>10}".format(place="world")
which will align the text to the right in a container filled up to ten characters.Ruff parses formatted strings into
FormatPart
s each of which is either aField
(content in{...}
) or aLiteral
(the normal content). Fields are parsed into name and format specifier sections (we'll ignore conversion specifiers for now).There are a myriad of specifiers that can be used in a
FormatSpec
. Unfortunately for linters, the specifier values can be dynamically set. For example,"hello {place:{align}{width}}".format(place="world", align=">", width=10)
and"hello {place:{fmt}}".format(place="world", fmt=">10")
will yield the same string as before but variables can be used to determine the formatting. In this case, when parsing the format specifier we can't know what kind of specifier is being used as their meaning is determined by both position and value.Ruff does not support nested replacements and our current data model does not support the concept. Here the data model is updated to support this concept, although linting of specifications with replacements will be inherently limited. We could split format specifications into two types, one without any replacements that we can perform lints with and one with replacements that we cannot inspect. However, it seems excessive to drop all parsing of format specifiers due to the presence of a replacement. Instead, I've opted to parse replacements eagerly and ignore their possible effect on other format specifiers. This will allow us to retain a simple interface for
FormatSpec
and most syntax checks. We may need to add some handling to relax errors if a replacement was seen previously.It's worth noting that the nested replacement can also include a format specification although it may fail at runtime if you produce an invalid outer format specification. For example,
"hello {place:{fmt:<2}}".format(place="world", fmt=">10")
is valid so we need to represent each nested replacement as a fullFormatPart
.Test plan
Adding unit tests for
FormatSpec
parsing and snapshots for PLE1300