Skip to content
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

Merged
merged 13 commits into from
Aug 17, 2023

Conversation

zanieb
Copy link
Member

@zanieb zanieb commented Aug 16, 2023

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 FormatParts each of which is either a Field (content in {...}) or a Literal (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 full FormatPart.

Test plan

Adding unit tests for FormatSpec parsing and snapshots for PLE1300

Copy link
Member

@MichaReiser MichaReiser left a 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

crates/ruff_python_literal/src/format.rs Outdated Show resolved Hide resolved
crates/ruff_python_literal/src/format.rs Outdated Show resolved Hide resolved
@zanieb
Copy link
Member Author

zanieb commented Aug 16, 2023

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.

@zanieb zanieb marked this pull request as draft August 16, 2023 14:28
@charliermarsh
Copy link
Member

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).

Comment on lines 353 to 355
let replacements = RefCell::new(vec![]);
// get_integer in CPython
let text = parse_nested_format_part(&replacements, text)?;
Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Member

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.

@zanieb
Copy link
Member Author

zanieb commented Aug 16, 2023

My decision to just extend FormatSpec may not work since all of these FormatSpec.format_... methods will drop replacements. I'm unsure of the best approach.

@charliermarsh
Copy link
Member

What do you mean by "drop replacements"? Can you give an example?

@zanieb
Copy link
Member Author

zanieb commented Aug 16, 2023

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 {x} could change the formatting and should not necessarily be ignored. Since all the format_... methods return results we could just return an error if replacements are present. I'm not sure where these methods are really used though.

@zanieb
Copy link
Member Author

zanieb commented Aug 16, 2023

With e5d6ec3 I've updated PLE1300 to lint nested replacements as well.

@charliermarsh
Copy link
Member

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.

@github-actions
Copy link
Contributor

github-actions bot commented Aug 16, 2023

PR Check Results

Ecosystem

✅ ecosystem check detected no changes.

Benchmark

Linux

group                                      main                                   pr
-----                                      ----                                   --
formatter/large/dataset.py                 1.01      3.6±0.04ms    11.3 MB/sec    1.00      3.6±0.03ms    11.4 MB/sec
formatter/numpy/ctypeslib.py               1.00    736.1±7.99µs    22.6 MB/sec    1.00    733.6±3.11µs    22.7 MB/sec
formatter/numpy/globals.py                 1.00     75.9±0.42µs    38.9 MB/sec    1.01     77.0±1.59µs    38.3 MB/sec
formatter/pydantic/types.py                1.00  1454.5±13.98µs    17.5 MB/sec    1.00  1459.2±33.25µs    17.5 MB/sec
linter/all-rules/large/dataset.py          1.00     10.5±0.06ms     3.9 MB/sec    1.00     10.5±0.06ms     3.9 MB/sec
linter/all-rules/numpy/ctypeslib.py        1.00      2.8±0.02ms     6.0 MB/sec    1.00      2.8±0.01ms     6.0 MB/sec
linter/all-rules/numpy/globals.py          1.01    397.9±0.94µs     7.4 MB/sec    1.00    394.5±2.81µs     7.5 MB/sec
linter/all-rules/pydantic/types.py         1.00      5.4±0.10ms     4.7 MB/sec    1.00      5.4±0.09ms     4.7 MB/sec
linter/default-rules/large/dataset.py      1.00      5.5±0.02ms     7.3 MB/sec    1.01      5.6±0.03ms     7.3 MB/sec
linter/default-rules/numpy/ctypeslib.py    1.01  1227.4±44.65µs    13.6 MB/sec    1.00  1217.0±13.47µs    13.7 MB/sec
linter/default-rules/numpy/globals.py      1.00    141.0±2.59µs    20.9 MB/sec    1.04    145.9±6.01µs    20.2 MB/sec
linter/default-rules/pydantic/types.py     1.00      2.5±0.01ms    10.3 MB/sec    1.00      2.5±0.02ms    10.2 MB/sec

Windows

group                                      main                                   pr
-----                                      ----                                   --
formatter/large/dataset.py                 1.00      4.2±0.10ms     9.7 MB/sec    1.00      4.2±0.08ms     9.7 MB/sec
formatter/numpy/ctypeslib.py               1.00   828.1±20.67µs    20.1 MB/sec    1.00   828.9±17.37µs    20.1 MB/sec
formatter/numpy/globals.py                 1.00     84.7±1.90µs    34.8 MB/sec    1.01     85.6±2.25µs    34.5 MB/sec
formatter/pydantic/types.py                1.00  1669.0±27.39µs    15.3 MB/sec    1.01  1685.2±25.52µs    15.1 MB/sec
linter/all-rules/large/dataset.py          1.00     12.7±0.13ms     3.2 MB/sec    1.05     13.3±0.21ms     3.0 MB/sec
linter/all-rules/numpy/ctypeslib.py        1.00      3.5±0.04ms     4.8 MB/sec    1.03      3.6±0.06ms     4.6 MB/sec
linter/all-rules/numpy/globals.py          1.00    440.2±9.70µs     6.7 MB/sec    1.03   454.8±14.80µs     6.5 MB/sec
linter/all-rules/pydantic/types.py         1.00      6.6±0.07ms     3.9 MB/sec    1.05      6.9±0.11ms     3.7 MB/sec
linter/default-rules/large/dataset.py      1.00      7.0±0.09ms     5.8 MB/sec    1.04      7.3±0.12ms     5.6 MB/sec
linter/default-rules/numpy/ctypeslib.py    1.00  1489.7±19.61µs    11.2 MB/sec    1.02  1523.9±27.75µs    10.9 MB/sec
linter/default-rules/numpy/globals.py      1.00    176.5±4.46µs    16.7 MB/sec    1.02    180.4±3.75µs    16.4 MB/sec
linter/default-rules/pydantic/types.py     1.00      3.2±0.06ms     8.1 MB/sec    1.01      3.2±0.04ms     8.0 MB/sec

@zanieb zanieb marked this pull request as ready for review August 16, 2023 17:24
@zanieb
Copy link
Member Author

zanieb commented Aug 16, 2023

Great that works for me #6624

Comment on lines +58 to +59
20 | "{:{s:y}}".format("hello", s="s") # [bad-format-character] (nested replacement format spec checked)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLE1300
Copy link
Member Author

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?

Copy link
Member

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...

zanieb added a commit that referenced this pull request Aug 16, 2023
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..])
Copy link
Member

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?

Copy link
Member Author

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);
Copy link
Member

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.

Copy link
Member Author

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.

Comment on lines -347 to +420
InvalidFormatSpecifier,
PlaceholderRecursionExceeded,
Copy link
Member Author

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.

Copy link
Member

@MichaReiser MichaReiser left a 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.

@zanieb zanieb merged commit d0f2a8e into main Aug 17, 2023
@zanieb zanieb deleted the fix/ple1300 branch August 17, 2023 14:07
renovate bot referenced this pull request in allenporter/pyrainbird Aug 19, 2023
[![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 [@&#8203;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
[@&#8203;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 [@&#8203;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 [@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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 [@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;MichaReiser](https://github.com/MichaReiser) in
[https://github.com/astral-sh/ruff/pull/6609](https://github.com/astral-sh/ruff/pull/6609)

#### New Contributors

- [@&#8203;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)
- [@&#8203;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)
- [@&#8203;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)
- [@&#8203;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)
- [@&#8203;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)
- [@&#8203;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>
renovate bot referenced this pull request in allenporter/flux-local Aug 20, 2023
[![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 [@&#8203;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
[@&#8203;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 [@&#8203;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 [@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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 [@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;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
[@&#8203;MichaReiser](https://github.com/MichaReiser) in
[https://github.com/astral-sh/ruff/pull/6609](https://github.com/astral-sh/ruff/pull/6609)

#### New Contributors

- [@&#8203;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)
- [@&#8203;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)
- [@&#8203;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)
- [@&#8203;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)
- [@&#8203;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)
- [@&#8203;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>
zanieb added a commit that referenced this pull request Aug 25, 2023
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Spurious PLE1300 error with 0.0.283
3 participants