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 import insertion support to autofix capabilities #3787
Conversation
808cb2d
to
683a494
Compare
aed248b
to
848ff40
Compare
683a494
to
c456622
Compare
PR Check ResultsEcosystem✅ ecosystem check detected no changes. BenchmarkLinux
Windows
|
} | ||
|
||
/// Find the end of the last docstring. | ||
fn match_docstring_end(body: &[Stmt]) -> Option<Location> { |
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.
GitHub isn't picking it up, but everything from here down is moved from rules/isort/rules/add_required_import.rs
(which now uses this Importer
struct).
What happens if |
No, we avoid fixing if the name is already bound to something else. In practice, I think that's the least surprising behavior. |
848ff40
to
2a9728b
Compare
c456622
to
ac04df5
Compare
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.
Nice!
Can we add a test case that demonstrates what happens if we have
if test:
import path
exit(1)
or
import path
if test:
pass
import numpy
// ...
exit(1)
) | ||
} | ||
} else { | ||
// Case 2: No `functools` import is in scope; thus, we add `import functools`, and |
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.
why not from functools import lru_cache
? that's what i'd expect and afaik that's what pycharm generally does
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.
Yeah I wasn't sure what the default should be here. I was thinking that it should be rule-dependent (e.g., it makes sense to do from typing import List
rather than import typing; typing.List
if we need to inject from typing).
Where does PyCharm do this? When you have @cache
and cache isn't imported? (But in that case, the user has already expressed that they want the member, not the fully-qualified name.)
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.
yes, this:
@cache
def foo():
...
quck fix -> enter -> enter:
from functools import cache
@cache
def foo():
...
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.
Yeah but in that case, the user has already written @cache
.
Given:
@functools.cache
def f(x):
return x
Then the same action yields:
import functools
@functools.cache
def f(x):
return x
Here, we don't know which the user prefers ahead-of-time.
ac04df5
to
c29bbf2
Compare
[![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.260` -> `^0.0.261` | [![age](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/compatibility-slim/0.0.260)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/confidence-slim/0.0.260)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>charliermarsh/ruff</summary> ### [`v0.0.261`](https://togithub.com/charliermarsh/ruff/releases/tag/v0.0.261) [Compare Source](https://togithub.com/charliermarsh/ruff/compare/v0.0.260...v0.0.261) <!-- Release notes generated using configuration in .github/release.yml at main --> #### What's Changed ##### Rules - \[`flake8-simplify`] Ignore `collapsible-if` violations for `if False:` and `if True:` by [@​JonathanPlasse](https://togithub.com/JonathanPlasse) in [astral-sh/ruff#3732 - \[`flake8-pie`] Extend `unncessary-generator-any-all` to set comprehensions by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3824 - \[`flake8-simplify`] Implement `dict-get-with-none-default` (`SIM910`) by [@​kyoto7250](https://togithub.com/kyoto7250) in [astral-sh/ruff#3874 - \[`flake8-annotations`] Additional simple magic return types by [@​dhruvmanila](https://togithub.com/dhruvmanila) in [astral-sh/ruff#3805 - \[`flake8-pyi`]: fix PYI015 false positive on assignment of TypeVar & friends by [@​bluetech](https://togithub.com/bluetech) in [astral-sh/ruff#3861 ##### Bug Fixes - Improve robustness of argument removal for `encode` calls by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3802 - Fix SIM222 and SIM223 false positive by [@​JonathanPlasse](https://togithub.com/JonathanPlasse) in [astral-sh/ruff#3832 - When checking module visibility, don't check entire ancestry by [@​Hnasar](https://togithub.com/Hnasar) in [astral-sh/ruff#3835 - Improve top-of-file insertions for required imports by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3779 - Use multi-fix semantics for `inplace` removal by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3804 - Flag non-`Name` expressions in `duplicate-isinstance-call` by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3817 - Avoid `unnecessary-comprehension-any-all` for async generators by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3823 - Fix `is_module_name()` and improve perf of `is_identifier()` by [@​JonathanPlasse](https://togithub.com/JonathanPlasse) in [astral-sh/ruff#3795 - Allow starred arguments in B030 by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3871 - Support mutually exclusive branches for `B031` by [@​dhruvmanila](https://togithub.com/dhruvmanila) in [astral-sh/ruff#3844 - Consider logger candidate from `logging` module only by [@​dhruvmanila](https://togithub.com/dhruvmanila) in [astral-sh/ruff#3878 ##### Core - Add import insertion support to autofix capabilities by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3787 - Generate `ImportMap` from module path to imported dependencies by [@​chanman3388](https://togithub.com/chanman3388) in [astral-sh/ruff#3243 ##### Docs - Add documentation for `ruff-action` (GitHub Action!) by [@​brucearctor](https://togithub.com/brucearctor) in [astral-sh/ruff#3857 #### New Contributors - [@​AetherUnbound](https://togithub.com/AetherUnbound) made their first contribution in [astral-sh/ruff#3806 - [@​Hnasar](https://togithub.com/Hnasar) made their first contribution in [astral-sh/ruff#3835 - [@​nvuillam](https://togithub.com/nvuillam) made their first contribution in [astral-sh/ruff#3848 - [@​brucearctor](https://togithub.com/brucearctor) made their first contribution in [astral-sh/ruff#3857 **Full Changelog**: astral-sh/ruff@v0.0.260...v0.0.261 </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:eyJjcmVhdGVkSW5WZXIiOiIzNS4zMi4yIiwidXBkYXRlZEluVmVyIjoiMzUuMzIuMiJ9--> Signed-off-by: Renovate Bot <bot@renovateapp.com> 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://togithub.com/charliermarsh/ruff) | `==0.0.260` -> `==0.0.261` | [![age](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/compatibility-slim/0.0.260)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/confidence-slim/0.0.260)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>charliermarsh/ruff</summary> ### [`v0.0.261`](https://togithub.com/charliermarsh/ruff/releases/tag/v0.0.261) [Compare Source](https://togithub.com/charliermarsh/ruff/compare/v0.0.260...v0.0.261) <!-- Release notes generated using configuration in .github/release.yml at main --> #### What's Changed ##### Rules - \[`flake8-simplify`] Ignore `collapsible-if` violations for `if False:` and `if True:` by [@​JonathanPlasse](https://togithub.com/JonathanPlasse) in [astral-sh/ruff#3732 - \[`flake8-pie`] Extend `unncessary-generator-any-all` to set comprehensions by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3824 - \[`flake8-simplify`] Implement `dict-get-with-none-default` (`SIM910`) by [@​kyoto7250](https://togithub.com/kyoto7250) in [astral-sh/ruff#3874 - \[`flake8-annotations`] Additional simple magic return types by [@​dhruvmanila](https://togithub.com/dhruvmanila) in [astral-sh/ruff#3805 - \[`flake8-pyi`]: fix PYI015 false positive on assignment of TypeVar & friends by [@​bluetech](https://togithub.com/bluetech) in [astral-sh/ruff#3861 ##### Bug Fixes - Improve robustness of argument removal for `encode` calls by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3802 - Fix SIM222 and SIM223 false positive by [@​JonathanPlasse](https://togithub.com/JonathanPlasse) in [astral-sh/ruff#3832 - When checking module visibility, don't check entire ancestry by [@​Hnasar](https://togithub.com/Hnasar) in [astral-sh/ruff#3835 - Improve top-of-file insertions for required imports by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3779 - Use multi-fix semantics for `inplace` removal by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3804 - Flag non-`Name` expressions in `duplicate-isinstance-call` by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3817 - Avoid `unnecessary-comprehension-any-all` for async generators by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3823 - Fix `is_module_name()` and improve perf of `is_identifier()` by [@​JonathanPlasse](https://togithub.com/JonathanPlasse) in [astral-sh/ruff#3795 - Allow starred arguments in B030 by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3871 - Support mutually exclusive branches for `B031` by [@​dhruvmanila](https://togithub.com/dhruvmanila) in [astral-sh/ruff#3844 - Consider logger candidate from `logging` module only by [@​dhruvmanila](https://togithub.com/dhruvmanila) in [astral-sh/ruff#3878 ##### Core - Add import insertion support to autofix capabilities by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3787 - Generate `ImportMap` from module path to imported dependencies by [@​chanman3388](https://togithub.com/chanman3388) in [astral-sh/ruff#3243 ##### Docs - Add documentation for `ruff-action` (GitHub Action!) by [@​brucearctor](https://togithub.com/brucearctor) in [astral-sh/ruff#3857 #### New Contributors - [@​AetherUnbound](https://togithub.com/AetherUnbound) made their first contribution in [astral-sh/ruff#3806 - [@​Hnasar](https://togithub.com/Hnasar) made their first contribution in [astral-sh/ruff#3835 - [@​nvuillam](https://togithub.com/nvuillam) made their first contribution in [astral-sh/ruff#3848 - [@​brucearctor](https://togithub.com/brucearctor) made their first contribution in [astral-sh/ruff#3857 **Full Changelog**: astral-sh/ruff@v0.0.260...v0.0.261 </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/pyrainbird). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNS4zNC4xIiwidXBkYXRlZEluVmVyIjoiMzUuMzQuMSJ9--> 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://togithub.com/charliermarsh/ruff) | `==0.0.260` -> `==0.0.261` | [![age](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/compatibility-slim/0.0.260)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/pypi/ruff/0.0.261/confidence-slim/0.0.260)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>charliermarsh/ruff</summary> ### [`v0.0.261`](https://togithub.com/charliermarsh/ruff/releases/tag/v0.0.261) [Compare Source](https://togithub.com/charliermarsh/ruff/compare/v0.0.260...v0.0.261) <!-- Release notes generated using configuration in .github/release.yml at main --> #### What's Changed ##### Rules - \[`flake8-simplify`] Ignore `collapsible-if` violations for `if False:` and `if True:` by [@​JonathanPlasse](https://togithub.com/JonathanPlasse) in [astral-sh/ruff#3732 - \[`flake8-pie`] Extend `unncessary-generator-any-all` to set comprehensions by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3824 - \[`flake8-simplify`] Implement `dict-get-with-none-default` (`SIM910`) by [@​kyoto7250](https://togithub.com/kyoto7250) in [astral-sh/ruff#3874 - \[`flake8-annotations`] Additional simple magic return types by [@​dhruvmanila](https://togithub.com/dhruvmanila) in [astral-sh/ruff#3805 - \[`flake8-pyi`]: fix PYI015 false positive on assignment of TypeVar & friends by [@​bluetech](https://togithub.com/bluetech) in [astral-sh/ruff#3861 ##### Bug Fixes - Improve robustness of argument removal for `encode` calls by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3802 - Fix SIM222 and SIM223 false positive by [@​JonathanPlasse](https://togithub.com/JonathanPlasse) in [astral-sh/ruff#3832 - When checking module visibility, don't check entire ancestry by [@​Hnasar](https://togithub.com/Hnasar) in [astral-sh/ruff#3835 - Improve top-of-file insertions for required imports by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3779 - Use multi-fix semantics for `inplace` removal by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3804 - Flag non-`Name` expressions in `duplicate-isinstance-call` by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3817 - Avoid `unnecessary-comprehension-any-all` for async generators by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3823 - Fix `is_module_name()` and improve perf of `is_identifier()` by [@​JonathanPlasse](https://togithub.com/JonathanPlasse) in [astral-sh/ruff#3795 - Allow starred arguments in B030 by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3871 - Support mutually exclusive branches for `B031` by [@​dhruvmanila](https://togithub.com/dhruvmanila) in [astral-sh/ruff#3844 - Consider logger candidate from `logging` module only by [@​dhruvmanila](https://togithub.com/dhruvmanila) in [astral-sh/ruff#3878 ##### Core - Add import insertion support to autofix capabilities by [@​charliermarsh](https://togithub.com/charliermarsh) in [astral-sh/ruff#3787 - Generate `ImportMap` from module path to imported dependencies by [@​chanman3388](https://togithub.com/chanman3388) in [astral-sh/ruff#3243 ##### Docs - Add documentation for `ruff-action` (GitHub Action!) by [@​brucearctor](https://togithub.com/brucearctor) in [astral-sh/ruff#3857 #### New Contributors - [@​AetherUnbound](https://togithub.com/AetherUnbound) made their first contribution in [astral-sh/ruff#3806 - [@​Hnasar](https://togithub.com/Hnasar) made their first contribution in [astral-sh/ruff#3835 - [@​nvuillam](https://togithub.com/nvuillam) made their first contribution in [astral-sh/ruff#3848 - [@​brucearctor](https://togithub.com/brucearctor) made their first contribution in [astral-sh/ruff#3857 **Full Changelog**: astral-sh/ruff@v0.0.260...v0.0.261 </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:eyJjcmVhdGVkSW5WZXIiOiIzNS4zNC4xIiwidXBkYXRlZEluVmVyIjoiMzUuMzQuMSJ9--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Summary
We often have rules (and violations) that could in theory be autofixed, but that rely on the presence of an imported symbol that may or may not be available in scope.
The
sys-exit-alias
rule is a good example. It recommends replacing calls to the builtinexit
andquit
methods with calls tosys.exit
. That's "easy" to autofix... as long assys.exit
is in-scope.This PR enables rules to import symbols into the current scope in a fairly general way, enabling autofix support for rules like
sys-exit-alias
. Below, I've also extended autofix support forlru-cache-with-maxsize-none
, which relies on makingfunctools.cache
available.How it works
From a rule's perspective, the end-user API leans on a single
get_or_import_symbol
method, which returns (1) anEdit
(to "insert" the symbol into the scope) and (2) the bound name of the resolved symbol (e.g.,exit
, if we addedfrom sys import exit
, orsys.exit
, if we addedimport sys
).Under-the-hood, that method performs a series of lookups:
sys.exit
is already available in any form (e.g.,from sys import exit as foo
,import sys as foo
); if so, it returns a reference to it.import ... from
for the relevant module (e.g.,from sys import path
). If so, it modifies that import to include the necessary symbol (from sys import path, exit
). Ifexit
is already bound to something else, it aborts and logs an error.import sys
, and returnssys.exit
as the bound reference.The logic for inserting imports is crude and follows LibCST's own logic: if there are no imports, we insert at top-of-file; otherwise, we insert after the last-seen top-level import statement.
Limitations
There are a few limitations to the current approach:
We don't attempt to retain import order at all. We leave that up to the
isort
rules (orisort
itself, if you're usingisort
).We only respect top-level imports, so here, we'd still insert an
import sys
:Closes #835.