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

Convert single-argument %-style format calls #3600

Merged
merged 2 commits into from Mar 21, 2023
Merged

Conversation

charliermarsh
Copy link
Member

@charliermarsh charliermarsh commented Mar 18, 2023

Summary

Today, we only convert %-style format calls if the right-hand side is a dictionary or tuple literal. However, I think we can support a larger set of expressions on the right-hand side.

There are three cases to consider...

  1. The template string contain at least one named value ("%(foo)s" % x). In that case, the right-hand side has to resolve to a map, so we treat it as kwargs (e.g., "{foo}".format(**x)).
  2. The template string contains multiple positional values ("%s %s" % x). In that case, the right-hand side has to resolve to a tuple, so we treat it as kwargs (e.g., "{} {}".format(*x)).
  3. The template string contains a single value ("%s" % x). In that case, we can't be certain whether the right-hand side is a single expression, or a tuple containing a single expression -- so unfortunately, we have to continue ignoring those.

There's one case that's not formally covered above, which is a template string that mixes named and positional values, like "%(foo)s %s". But I'm unable to come up with a right-hand side that's valid given mixed named and positional values. Does such a case exist? I believe it's impossible.

Closes #3549.

@charliermarsh charliermarsh added the rule Implementing or modifying a lint rule label Mar 18, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Mar 18, 2023

PR Check Results

Ecosystem

ℹ️ ecosystem check detected changes. (+5, -0, 0 error(s))

airflow (+1, -0)

+ airflow/providers/google/cloud/hooks/bigtable.py:252:32: UP031 [*] Use format specifiers instead of percent format

bokeh (+3, -0)

+ src/bokeh/plotting/_figure.py:738:41: UP031 [*] Use format specifiers instead of percent format
+ tests/unit/bokeh/models/test_plots.py:486:38: UP031 [*] Use format specifiers instead of percent format
+ tests/unit/bokeh/models/test_plots.py:492:38: UP031 [*] Use format specifiers instead of percent format

scikit-build (+1, -0)

+ skbuild/platform_specifics/cygwin.py:33:27: UP031 [*] Use format specifiers instead of percent format

Benchmark

Linux

group                                      main                                   pr
-----                                      ----                                   --
linter/all-rules/large/dataset.py          1.00     14.0±0.04ms     2.9 MB/sec    1.00     14.0±0.04ms     2.9 MB/sec
linter/all-rules/numpy/ctypeslib.py        1.00      3.7±0.01ms     4.5 MB/sec    1.00      3.7±0.00ms     4.5 MB/sec
linter/all-rules/numpy/globals.py          1.01    433.7±1.11µs     6.8 MB/sec    1.00    430.9±2.23µs     6.8 MB/sec
linter/all-rules/pydantic/types.py         1.00      6.2±0.01ms     4.1 MB/sec    1.00      6.2±0.01ms     4.1 MB/sec
linter/default-rules/large/dataset.py      1.00      7.7±0.02ms     5.3 MB/sec    1.01      7.7±0.03ms     5.3 MB/sec
linter/default-rules/numpy/ctypeslib.py    1.00   1697.9±5.06µs     9.8 MB/sec    1.00   1706.0±2.25µs     9.8 MB/sec
linter/default-rules/numpy/globals.py      1.00    175.2±0.29µs    16.8 MB/sec    1.01    176.9±2.56µs    16.7 MB/sec
linter/default-rules/pydantic/types.py     1.00      3.6±0.01ms     7.1 MB/sec    1.00      3.6±0.00ms     7.0 MB/sec

Windows

group                                      main                                   pr
-----                                      ----                                   --
linter/all-rules/large/dataset.py          1.04     17.6±0.64ms     2.3 MB/sec    1.00     17.0±0.48ms     2.4 MB/sec
linter/all-rules/numpy/ctypeslib.py        1.00      4.8±0.21ms     3.5 MB/sec    1.01      4.8±0.26ms     3.5 MB/sec
linter/all-rules/numpy/globals.py          1.07   645.5±44.15µs     4.6 MB/sec    1.00   600.7±46.57µs     4.9 MB/sec
linter/all-rules/pydantic/types.py         1.02      8.2±0.36ms     3.1 MB/sec    1.00      8.0±0.39ms     3.2 MB/sec
linter/default-rules/large/dataset.py      1.05      9.9±0.38ms     4.1 MB/sec    1.00      9.5±0.58ms     4.3 MB/sec
linter/default-rules/numpy/ctypeslib.py    1.01      2.2±0.13ms     7.7 MB/sec    1.00      2.1±0.10ms     7.8 MB/sec
linter/default-rules/numpy/globals.py      1.07   265.4±16.18µs    11.1 MB/sec    1.00   248.4±28.31µs    11.9 MB/sec
linter/default-rules/pydantic/types.py     1.12      5.0±0.16ms     5.1 MB/sec    1.00      4.4±0.22ms     5.7 MB/sec

@andersk
Copy link
Contributor

andersk commented Mar 18, 2023

Case 3 does not hold.

x = (1,)
print("%s" % x)
print("{}".format(x))

@charliermarsh
Copy link
Member Author

Ah that's a shame, I'd guess that's a small minority of cases.

@charliermarsh
Copy link
Member Author

I can't think of any way to avoid that, so I guess we have to continue omitting those cases.

@charliermarsh
Copy link
Member Author

(Removed, and amended the PR summary.)

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.

LGTM. I can't say much about the inference rules.

Comment on lines +390 to +398
// Otherwise, if we have a single field, we can't make any assumptions about the
// right-hand side. It _could_ be a tuple, but it could also be a single value,
// and we can't differentiate between them.
// For example:
// ```python
// x = (1,)
// print("%s" % x)
// print("{}".format(x))
// ```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the comments!

@@ -335,7 +335,9 @@ pub(crate) fn printf_string_formatting(
}

// Parse each string segment.
let mut format_strings = vec![];
let mut num_positional = 0;
let mut num_keyword = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: A boolean for keywords seems sufficient

Suggested change
let mut num_keyword = 0;
let mut has_keyword_arguments = false;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, we need to know if there's more than one positional argument. If we have exactly one positional argument, we can't fix it unfortunately 😭

@konstin
Copy link
Member

konstin commented Mar 20, 2023

I think the proper way to ensure the cases are correctly covered would be reading the cpython source code, but i couldn't find the percent formatting source code, only the tests: https://github.com/python/cpython/blob/5e6661bce968173fa45b74fa2111098645ff609c/Lib/test/test_format.py

@charliermarsh charliermarsh enabled auto-merge (squash) March 21, 2023 03:30
@charliermarsh charliermarsh merged commit e9f359a into main Mar 21, 2023
10 checks passed
@charliermarsh charliermarsh deleted the charlie/printf branch March 21, 2023 03:35
renovate bot added a commit to ixm-one/pytest-cmake-presets that referenced this pull request Mar 23, 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://togithub.com/charliermarsh/ruff) | `^0.0.257` ->
`^0.0.258` |
[![age](https://badges.renovateapi.com/packages/pypi/ruff/0.0.258/age-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://badges.renovateapi.com/packages/pypi/ruff/0.0.258/adoption-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://badges.renovateapi.com/packages/pypi/ruff/0.0.258/compatibility-slim/0.0.257)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://badges.renovateapi.com/packages/pypi/ruff/0.0.258/confidence-slim/0.0.257)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>charliermarsh/ruff</summary>

###
[`v0.0.258`](https://togithub.com/charliermarsh/ruff/releases/tag/v0.0.258)

[Compare
Source](https://togithub.com/charliermarsh/ruff/compare/v0.0.257...v0.0.258)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### What's Changed

##### Rules

- \[`flake8-comprehensions`] Update `C416` with dict comprehension
(autofixable) by [@&#8203;dhruvmanila](https://togithub.com/dhruvmanila)
in
[astral-sh/ruff#3605
- \[`pylint`]: Implement `assert-on-string-literal` (`W0129`) by
[@&#8203;latonis](https://togithub.com/latonis) in
[astral-sh/ruff#3610
- \[`pyupgrade`] Convert single-argument %-style format calls by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3600
- \[`pyupgrade`] Flag PEP 585 and PEP 604 violations in quoted
annotations by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3593
- \[`pyupgrade`] Enable autofix for annotations within 'simple' string
literals by [@&#8203;charliermarsh](https://togithub.com/charliermarsh)
in
[astral-sh/ruff#3657
- \[`pyflakes`] Add autofix functionality for `F523`
([#&#8203;3613](https://togithub.com/charliermarsh/ruff/issues/3613)) by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3613
- \[`flake8-bandit`]: Implement deny-list rules for suspicious member
calls by [@&#8203;colin99d](https://togithub.com/colin99d) in
[astral-sh/ruff#3239
- \[`flake8-annotations`] Add autofix for `ANN204` with magic methods by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3633
- \[`pylint`] Implement `binary-op-exception` (`PLW0711`) by
[@&#8203;latonis](https://togithub.com/latonis) in
[astral-sh/ruff#3639
- \[`flake8-django`]: Implement rule DJ012 by
[@&#8203;dhruvmanila](https://togithub.com/dhruvmanila) in
[astral-sh/ruff#3659

##### Bug Fixes

- Check exclusions prior to resolving `pyproject.toml` files by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3588
- Fix D417 false positive by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3596
- Avoid removing comment hash for noqa's with trailing content by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3589
- Avoid panics for implicitly-concatenated docstrings by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3584
- Fix infinite loop due to rules `D207` & `W605` by
[@&#8203;vlindhol](https://togithub.com/vlindhol) in
[astral-sh/ruff#3609
- Avoid trimming escaped whitespace in D210 by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3635
- Handle `UP032` autofix with adjacent keywords by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3636
- Consider same-site fixes to be overlapping by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3638
- Avoid `RUF007` fixes for more than two arguments by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3654
- Allow `pairwise` diagnostics for `zip(..., strict=True)` by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3669
- isort: fix bad interaction between `force-sort-within-sections` and
`force-to-top` by [@&#8203;bluetech](https://togithub.com/bluetech) in
[astral-sh/ruff#3645
- Gracefully handle lint panics by
[@&#8203;MichaReiser](https://togithub.com/MichaReiser) in
[astral-sh/ruff#3509
- Fix TRY300 false positive by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3634
- Avoid raising PEP 604 errors with forward-referenced members by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3640
- Avoid attempting infinite `open` fix with re-bound builtin by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3650
- Check indentation level when executing E231 by
[@&#8203;kyoto7250](https://togithub.com/kyoto7250) in
[astral-sh/ruff#3668
- Flag, but don't fix, unused imports (`F401`) in `ModuleNotFoundError`
blocks by [@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3658

#### New Contributors

- [@&#8203;Rogdham](https://togithub.com/Rogdham) made their first
contribution in
[astral-sh/ruff#3607
- [@&#8203;vlindhol](https://togithub.com/vlindhol) made their first
contribution in
[astral-sh/ruff#3609
- [@&#8203;dhruvmanila](https://togithub.com/dhruvmanila) made their
first contribution in
[astral-sh/ruff#3605
- [@&#8203;luke396](https://togithub.com/luke396) made their first
contribution in
[astral-sh/ruff#3604
- [@&#8203;fuziontech](https://togithub.com/fuziontech) made their first
contribution in
[astral-sh/ruff#3641

**Full Changelog**:
astral-sh/ruff@v0.0.257...v0.0.258

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, 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://app.renovatebot.com/dashboard#github/ixm-one/pytest-cmake-presets).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNS4xNy4xIiwidXBkYXRlZEluVmVyIjoiMzUuMTcuMSJ9-->

Signed-off-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
renovate bot added a commit to allenporter/flux-local that referenced this pull request Mar 25, 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://togithub.com/charliermarsh/ruff) | `==0.0.257` ->
`==0.0.259` |
[![age](https://badges.renovateapi.com/packages/pypi/ruff/0.0.259/age-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://badges.renovateapi.com/packages/pypi/ruff/0.0.259/adoption-slim)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://badges.renovateapi.com/packages/pypi/ruff/0.0.259/compatibility-slim/0.0.257)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://badges.renovateapi.com/packages/pypi/ruff/0.0.259/confidence-slim/0.0.257)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>charliermarsh/ruff</summary>

###
[`v0.0.259`](https://togithub.com/charliermarsh/ruff/releases/tag/v0.0.259)

[Compare
Source](https://togithub.com/charliermarsh/ruff/compare/v0.0.258...v0.0.259)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### Summary

Follow-up release to `v0.0.258` to fix an issue related to rule
resolution via `select` and `ignore`.

#### What's Changed

##### Bug Fixes

- Fix RuleSet.remove by
[@&#8203;MichaReiser](https://togithub.com/MichaReiser) in
[astral-sh/ruff#3685
- Respect all rule-exemption sources when suppressing parser errors by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3665
- Avoid nested loops in `missing_whitespace` by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3688

**Full Changelog**:
astral-sh/ruff@v0.0.258...v0.0.259

###
[`v0.0.258`](https://togithub.com/charliermarsh/ruff/releases/tag/v0.0.258)

[Compare
Source](https://togithub.com/charliermarsh/ruff/compare/v0.0.257...v0.0.258)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### What's Changed

##### Rules

- \[`flake8-comprehensions`] Update `C416` with dict comprehension
(autofixable) by [@&#8203;dhruvmanila](https://togithub.com/dhruvmanila)
in
[astral-sh/ruff#3605
- \[`pylint`]: Implement `assert-on-string-literal` (`W0129`) by
[@&#8203;latonis](https://togithub.com/latonis) in
[astral-sh/ruff#3610
- \[`pyupgrade`] Convert single-argument %-style format calls by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3600
- \[`pyupgrade`] Flag PEP 585 and PEP 604 violations in quoted
annotations by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3593
- \[`pyupgrade`] Enable autofix for annotations within 'simple' string
literals by [@&#8203;charliermarsh](https://togithub.com/charliermarsh)
in
[astral-sh/ruff#3657
- \[`pyflakes`] Add autofix functionality for `F523`
([#&#8203;3613](https://togithub.com/charliermarsh/ruff/issues/3613)) by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3613
- \[`flake8-bandit`]: Implement deny-list rules for suspicious member
calls by [@&#8203;colin99d](https://togithub.com/colin99d) in
[astral-sh/ruff#3239
- \[`flake8-annotations`] Add autofix for `ANN204` with magic methods by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3633
- \[`pylint`] Implement `binary-op-exception` (`PLW0711`) by
[@&#8203;latonis](https://togithub.com/latonis) in
[astral-sh/ruff#3639
- \[`flake8-django`]: Implement rule DJ012 by
[@&#8203;dhruvmanila](https://togithub.com/dhruvmanila) in
[astral-sh/ruff#3659

##### Bug Fixes

- Check exclusions prior to resolving `pyproject.toml` files by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3588
- Fix D417 false positive by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3596
- Avoid removing comment hash for noqa's with trailing content by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3589
- Avoid panics for implicitly-concatenated docstrings by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3584
- Fix infinite loop due to rules `D207` & `W605` by
[@&#8203;vlindhol](https://togithub.com/vlindhol) in
[astral-sh/ruff#3609
- Avoid trimming escaped whitespace in D210 by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3635
- Handle `UP032` autofix with adjacent keywords by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3636
- Consider same-site fixes to be overlapping by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3638
- Avoid `RUF007` fixes for more than two arguments by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3654
- Allow `pairwise` diagnostics for `zip(..., strict=True)` by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3669
- isort: fix bad interaction between `force-sort-within-sections` and
`force-to-top` by [@&#8203;bluetech](https://togithub.com/bluetech) in
[astral-sh/ruff#3645
- Gracefully handle lint panics by
[@&#8203;MichaReiser](https://togithub.com/MichaReiser) in
[astral-sh/ruff#3509
- Fix TRY300 false positive by
[@&#8203;JonathanPlasse](https://togithub.com/JonathanPlasse) in
[astral-sh/ruff#3634
- Avoid raising PEP 604 errors with forward-referenced members by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3640
- Avoid attempting infinite `open` fix with re-bound builtin by
[@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3650
- Check indentation level when executing E231 by
[@&#8203;kyoto7250](https://togithub.com/kyoto7250) in
[astral-sh/ruff#3668
- Flag, but don't fix, unused imports (`F401`) in `ModuleNotFoundError`
blocks by [@&#8203;charliermarsh](https://togithub.com/charliermarsh) in
[astral-sh/ruff#3658

#### New Contributors

- [@&#8203;Rogdham](https://togithub.com/Rogdham) made their first
contribution in
[astral-sh/ruff#3607
- [@&#8203;vlindhol](https://togithub.com/vlindhol) made their first
contribution in
[astral-sh/ruff#3609
- [@&#8203;dhruvmanila](https://togithub.com/dhruvmanila) made their
first contribution in
[astral-sh/ruff#3605
- [@&#8203;luke396](https://togithub.com/luke396) made their first
contribution in
[astral-sh/ruff#3604
- [@&#8203;fuziontech](https://togithub.com/fuziontech) made their first
contribution in
[astral-sh/ruff#3641

**Full Changelog**:
astral-sh/ruff@v0.0.257...v0.0.258

</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://app.renovatebot.com/dashboard#github/allenporter/flux-local).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNS4xNC4yIiwidXBkYXRlZEluVmVyIjoiMzUuMTQuMiJ9-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rule Implementing or modifying a lint rule
Projects
None yet
Development

Successfully merging this pull request may close these issues.

printf-style-formatting ignores direct-string interpolation
4 participants