-
Notifications
You must be signed in to change notification settings - Fork 979
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
Import-sorting (a ruff
approximation of isort
)
#465
Comments
(I continually dip my toe into Rust and even a little PyO3 as I work on pantsbuild) Perhaps one day I'll pick this up myself 😉 |
Yes! I want to do this. I've been a little intimidated by the sheer amount of functionality that's packaged with I'm not certain if this will fit into the same "autofixable lint error" pattern, or if it will be its own sub-command. |
A potential first approximation that delays dealing with the first vs. third party heuristic would be to sort only within import groups separated by existing blank lines. This would be |
I like this idea! In my case it's just Isort with the Black profile, that's pretty much what I would like. 🤓 |
👍 I'm thinking that this should be a separate subcommand ( |
Can you explain a little why you think it'd need its own subcommand and not be folded (optionally, config-enabled) into From my perspective seems like I'd need to run two commands when one would be sufficient. |
You're probably right that it could be folded into However, these are really deficiencies in the autofix API and not essential blockers to adding import-sorting as a "fixable lint error". And moving (One solution to this problem, which I don't love but is arguably already needed, is to iteratively re-check and autofix errors until there are no more fixable errors. So, e.g., you'd run (Another solution is to try and develop a more semantically-aware fix API, or maybe something based on CRDTs (???), that lets you apply both "Reorder these statements" and "Remove this one statement" in a single pass.) |
I have a suspicion that if you start with the slow-but-correct way, your bar-chart of performance will still hold very relevant. And then when you figure out the fast-and-correct, everyone rejoices 😄 |
(I've started work on this tonight.) |
Not that my opinion matters much. But I have to admit that I'm not a big fan of how isort sorts out-of-the-box. And prefer something like this: [tool.isort]
profile = "black"
force_sort_within_sections = true # Don't group `from` imports separately
order_by_type = true # Order by CONSTANT, CamelCase, snake_case Mostly because it reduces noise in diffs when going back and forth between It's also bit jarring to make such a change, save, and then the line moves 10 lines up/down when you have "format on save" enabled in your editor. But maybe that's just me 🤷♂️ People seems to like separating |
I noticed that That is, from .param_functions import Body as Body
from .param_functions import Cookie as Cookie Whereas, if you omit the from .param_functions import Body
from .param_functions import Cookie Then from .param_functions import Body, Cookie Here's an example from FastAPI (\cc @tiangolo): There are some issues in isort suggesting that they wanted to make this the default (PyCQA/isort#1305, PyCQA/isort#1812). I'm partial to making this Ruff's default, but curious if others feel strongly? |
As long as from .param_functions import Body as Body
from .param_functions import Cookie as Cookie became from .param_functions import (
Body as Body,
Cookie as Cookie,
) and that still is kosher from (I usually see in our codebase) from .param_functions import Body as Body # re-export
from .param_functions import Cookie which I'd kinda prefer: from .param_functions import (
Body as Body, # re-export
Cookie
) but I understand if that's prohibitively difficult to get right 😉 |
Yeah that's what happens with the current implementation I have going -- you get: from .param_functions import (
Body as Body,
Cookie as Cookie,
) |
The main unsolved thing with my current implementation is that it removes and disregards comments. Comment handling is tricky (by this, I mean it's hard to know what the best behavior is, not that it's hard to implement). You can see how # comment 1a
# comment 1b
from .param_functions import (
Body, # comment 1c
Header, # comment 1d
)
# comment 2a
# comment 2b
from .param_functions import Cookie
from .param_functions import Request ...it becomes: # comment 1a
# comment 1b
# comment 2a
# comment 2b
from .param_functions import Body # comment 1c
from .param_functions import Header # comment 1d
from .param_functions import Cookie, Request (So, they move any line-level comments to the top of the import block, retain any end-of-line comments, and avoid merging statements with end-of-line comments.) |
I don't feel strongly about this, what would matter to me the most would be compatibility with mypy, Black, and minimization of diffs, but apart from that, I'm fine with anything. 🚀 |
At the point now where I'm finishing all the test cases, so an initial version of this should go out in the next day or so. There will be a few limitations that I'll document and resolve later (e.g., doesn't preserve comments). |
The first version of this is going out in v0.0.110. Feedback and Issues welcome! There are two main limitations that I want to address in future PRs:
|
If you're just using Ruff to sort imports, it's about 30x faster than single-threaded isort, and 10x faster than multi-threaded isort. But... if you're already using Ruff for linting, the performance hit for adding import sorting on top of that existing workflow should be extremely negligible (in the 5s or 10s of milliseconds for CPython), since the majority of execution is spent on parsing the AST. In other words: if you already use Ruff for linting, then using Ruff for import sorting should be basically "free". |
Nice! I’ve opened these issues I ran into: |
Much appreciated! |
So awesome! Amazing to see you build these things! 🚀🎉 |
v0.0.122 (which just hit PyPI) contains a bunch of compatibility improvements, including comment preservation, respecting If you're able to test it out on your projects, would love feedback, issues, etc.! |
Fantastic thank you! I noticed ruff will insert an empty line before comments (unlike isort), eg: import yaml
# use importlib_resources backport for compatibility with python < 3.9
from importlib_resources import files I presume this is a deliberate change. |
It’s deliberate. |
I just integrated the zulip/zulip pull request to use this. |
That's awesome! Thanks @timabbott! Made my day :) |
I saw Ruff insert a line between import and from import statements:
How can this be avoided, I am on version v0.0.286 |
@MehulBatra perhaps you're looking for |
How does one enable ruff's isort support? I have a project in which Even import blocks as gross as import jax
import copy
from typing import Optional
import jax.numpy as jnp
import jax.dlpack
import torch
import math
import functools are immune to ruff's import formatting. OTOH, running |
@samuela - Import sorting is currently part of the linter ( [tool.ruff.lint]
# Enable the isort rules.
extend-select = ["I"] Or, e.g., (We're continuing to discuss whether import sorting should be part of the formatter ( |
Thanks so much @charliermarsh ! Using [tool.ruff]
# Enable the isort rules.
extend-select = ["I"] did the trick for me. Thanks for all your hard work on ruff! 🏄♀️ |
Oof, sorry, it's |
Sorry if I am asking a dumb question, but where in the documentation can I find an explanation of this behaviour and why it works? |
Not dumb at all. The generated documentation for that field is here: https://docs.astral.sh/ruff/settings/#extend-select. And a prose write-up on rule selection is here: https://docs.astral.sh/ruff/linter/#rule-selection. ( (You can use either |
This comment was marked as resolved.
This comment was marked as resolved.
Any chance we might implement isort's Auto-comment import sections?
Trying with ruff: [tool.ruff.lint.isort]
import_heading_stdlib = "Standard Library" ruff check . --fix
ruff failed
Cause: Failed to parse /Users/***/***/***/pyproject.toml
Cause: TOML parse error at line 61, column 1
|
61 | [tool.ruff.lint]
| ^^^^^^^^^^^^^^^^
unknown field `import_heading_stdlib`, expected one of ... |
If somebody is wondering how to add this in pre-commit, use this
|
This works for both ruff-pre-commit with example configuration and ruff-vscode. |
isort
does have some wacky heuristics to determine first v third party, but ultimately I'd love to elide the import-sorting it does withruff
⚡To me, doesn't have to be 1:1, so long as the behavior is there for import sorting/grouping I'm happy 😄
If we want to keep this in the realm of
flake8
, it'd be flake8-import-order with a fixer 😉The text was updated successfully, but these errors were encountered: