-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
[Feature Request] Optimize imports #333
Comments
Remove duplicate import also requested. |
IMO import sorting should be handled separate outside of Black. With ISort (and others) we already have great tools. |
#363 (comment) also serves as a decent indicator that this won't / shouldn't happen in black. from @ambv's comment:
adding / removing / sorting imports would change the ast |
I wish there was something like black but for linting and code review. Maybe name it white? 😋 |
Obviously we can use isort and autoflake (or some other tools) to achieve the same thing, but a big benefit of Black is that it's opinionated and covers everything in one tool and one command and one optional configuration. |
I'm warming up to the idea that Black should cover this use case too. |
Which one? |
@ambv I understand why you'd want to, but pretty please stick to formatting only. Linting is a different kind of beast from formatting. I'm really comfortable with letting black format my code, as it doesn't change the working of it. But you have no idea why my code was written as it is.
I would expect that black-the-linter would always put all the imports together, without an cop-out:
I love that black takes out all discussion on formatting, but please please do not make black do linting (for lack of a better word). Thank you for reading :) |
I would like to see Black auto-sort imports, actually. For my team the value of Black is in taking care of boring, unimportant code-related decisions in a consistent way, so that developers don't have to worry about these things. That value gets even bigger if we can have a single tool taking care of all of these decisions, instead of having one for formatting, one for sorting imports, etc. Otherwise, developers have to remember to run every one of those tools on their code, and it gets harder to get people to install editor plugins to run every tool automatically. It's true that import sorting is a bit trickier than what Black currently does, because it will change the AST. To @jaroel's concern, we should limit import sorting to contiguous blocks of imports: if there is any non-import code, we should not move imports around it. Similarly, we should be careful not to reorder imports that define the same name, including |
fwiw, pre-commit goes a long way towards attempting to solve this problem. It makes it easy to set up as many linters as you want (in a variety of languages) and runs them automatically (commit / push time and/or through CI). I myself am leaning towards @jaroel's sentiment. black should aim to do formatting well and leave import sorting to the battle tested import sorting tools. Reinventing |
@JelleZijlstra It looks to me that you want automation, not import-sorting-by-black per se :) In other words: I wouldn't want my car mechanic to clean my clothes, even if it would be handy to have that if I'm already waiting at the shop ;) |
One practical problem we're running into for large codebases is that |
@jaroel, you raise some valid points. The very reason I'm considering sorting imports in Black is that isort does it wrong:
Even if isort's author agreed with some of the above, backwards compatibility concerns pretty much force him to keep it as is. If I followed some of the advice here around "reinventing" things, Black wouldn't see the light of day at all. We've had autopep8 and YAPF the entire time, didn't we? Just as I didn't want to write an auto-formatter, I don't want to creep more scope into it. Sadly, sorting imports looks increasingly inevitable. Don't worry though. This feature requires careful planning so it won't appear out of the blue any time soon. |
yapf/autopep8 have much more timid goals than black, not sure it's fair to compare them :) (whereas an import sorter would hopefully have identical goals). That said, I also reinvented the isort wheel for exactly the reasons listed above (1 2 4) 🙃 (whoops I'm a hypocrite) I definitely think (1) is fixable upstream. I've proposed fixes to (2) but this seems to be a sticking point (worked around instead), and (4) I just don't have any good excuses for |
@ambv is there anything we can do to help move this part of black along? Having fallen into various pitfalls of isort due to an issue of it's configuration (it merges |
@bertjwregeer I think the main limiting factor is that somebody has to actually do the work. Here's a list of some things that have to be taken care of:
|
I've got a Zope/Grok project where we import the whole module and refer to that. |
In the @plone project we decided to sort imports alphabetical. Major reason is, if there are many imports it is easier to read and to find an import, without guessing what it is semantically. An we often have many imports. Also, the internal vs. third-party is difficult to define in a project with many packages. Is the application server code third party or not? Developing an Addon for Plone, is Plone then thirdparty? If it is taken into Plone-Core, do we then need to resort imports? There is no semantic true way to make sections nor is there a good way to automate that. Alphabetical imports are simple, easy to read and also do not need code inspection for sorting. It still breaks the AST diff. I usually do an isort and the run black. The Black compatible (and currently still on a line length of 79) Isort configuration. |
@asottile's https://github.com/asottile/reorder_python_imports "has a single aim: reduce merge conflicts" and favours: from typing import Dict
from typing import List over: from typing import Dict, List |
I much prefer grouping things together than a single import per line, I find the explosion of import lines to be distracting... but that all comes down to preference. |
I think you can call it more than preference because if there is no other difference, less lines is better. |
I have no stake in black's implementation, that said it's not just a stylistic preference. -- much like the rest of black there's a reason for its opinionated decisions. Many of them are similar to the rationale for the tool I've written: avoid merge conflicts, reduce diff churn.
|
Worth noting that
|
@gwax I would like to point you to: #333 (comment) where some issues with |
Can we just get an option to disable checking imports? Otherwise my configured isort and black keep fighting with each other over which way is correct. |
|
@zsol Have you considered something equivalent to isort's It dramatically reduces merge conflict issues, which kinda aligns to how black approaches a lot of formatting too. |
Black absolutely should not sort or touch imports whatsoever. Many times import order and convention are manually crafted to allow specific behavior (monkey patching, for example) and should never get mangled by a formatter. As noted above this is directly changing the program AST. This issue should get closed, buried, and never to be talked about again!😃 |
I've seen a couple of larger projects that use a style with brackets: from typing import (
Any,
Dict,
List
) This is still probably a easier to break during a merge than the style mentioned above:
|
@stuaxo This syntax is very noisy when you have a single import though: from typing import (
Any,
) You can special case the above and keep it to one line, but then the diff when adding a second import gets very noisy: -from typing import Any
+from typing import (
+ Any,
+ Dict,
+) Merge conflicts can also get annoying with this type of diff. |
An important note should be that perfomance is different for every approach In [4]: timeit('''
...: from typing import Any
...: from typing import Dict
...: from typing import List
...: ''', number=1_000_000)
Out[4]: 1.094976361026056
In [5]: timeit('''
...: from typing import Any, Dict, List
...: ''', number=1_000_000)
Out[5]: 0.4201086120447144
In [6]: timeit('''
...: from typing import (
...: Any,
...: Dict,
...: List
...: )
...: ''', number=1_000_000)
Out[6]: 0.4278764280024916 |
The difference in performance is pretty big, maybe the faster cpython project is interested in @markshannon @gvanrossum |
Black shouldn't sort imports by default for the reasons stated many times. But it should definitely be an option. Using isort separately can result in formatting conflicts between Black and isort. To the point that I find myself implementing some kind of loop like this:
That obviously sucks. |
On Thu, 7 Oct 2021, at 11:06, Alexey wrote:
> I've seen a couple of larger projects that use a style with brackets:
>
> from typing import (
> Any,
> Dict,
> List
> )
> This is still probably a easier to break during a merge than the style mentioned above:
>
> `from typing import Any
> from typing import Dict
> from typing import List
`
An important note should be that perfomance is different for every approach
In [4]: timeit('''
...: from typing import Any
...: from typing import Dict
...: from typing import List
...: ''', number=1_000_000)
Out[4]: 1.094976361026056
In [5]: timeit('''
...: from typing import Any, Dict, List
...: ''', number=1_000_000)
Out[5]: 0.4201086120447144
In [6]: timeit('''
...: from typing import (
...: Any,
...: Dict,
...: List
...: )
...: ''', number=1_000_000)
Out[6]: 0.4278764280024916
I was blissfully unaware. Thanks for pointing this out.
I wonder if this can be improved in any way -- it's weird that the difference would be so big. That's definitely out-of-scope though.
|
To my knowledge, the isort maintainer would consider any conflict with black that arises when using black profile as a bug or something that should be improved. If you have some issues with conflicts, I suggest reporting it there ;) |
For me, I run autoflake, isort, and black and if I run black last, it reorders some imports. For example, if I have these imports after running isort, and before running black: # Third Party
from jsonpath_ng.ext import parse
# Library
from myapp import utils as u
from myapp.custom_exceptions import (
...
)
from myapp.json_factory import JsonFactory but after running black I have this # Third Party
# Library
from myapp import utils as u
from myapp.custom_exceptions import (
...
)
from myapp.json_factory import JsonFactory
from jsonpath_ng.ext import parse The [tool.black]
line-length = 80
target-version = ['py38']
include = '''
(
\.pyi?$
| \.py?$
| ^/tests/
| ^/src/
)
'''
exclude = '''
(
/(
\.eggs # exclude a few common directories in the
| \.git # root of the project
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| __pycache__
| buck-out
| build
| dist
| .*/migrations
| \.github
| ci
| node_modules
| static
| staticfiles
)/
| \.html
| \.js
| \.css
| \.scss
)
'''
[tool.isort]
import_heading_firstparty = "Library"
import_heading_future = "Futures"
import_heading_local = "Local"
import_heading_stdlib = "Standard Library"
import_heading_thirdparty = "Third Party"
indent = 4
known_first_party = "myapp"
lines_after_imports = 2
lines_between_types = 1
profile = "black" If people feel we must sort imports (I am fairly against this because it is easy to use these tools automatically), then why are we bothering to try and find out how to fix imports, just detect if we could always install black like |
Black doesn't sort imports. I don't know what tool is reordering your imports, but it's not black. |
I know it shouldn't now, but it is. running it directly from the cli using that as the config, black is changing imports after running isort. I am working on filling out a possible bug request to try and find out why. |
I use this additional settings and have no problem with running
|
@rmax You might try just [isort]
profile = "black" |
I am running blackd from PyCharm via the BlackConnect plugin. It would be great if black handled the imports as well. There is no working equivalent for isort on PyCharm. In any case, running separate daemons for each seems like overkill. |
I'm going to close this issue as we don't have plans for introducing import sorting into Black. There are several existing tools that provide import sorting really well, like isort and usort. If you're interested in a single tool that combines both, there's ufmt. Edit: Check out the usort configuration options if you want to finetune import ordering to please your linter like @stefaneidelloth mentions below. |
It will be easier to develop if we could use a tool to organize / sort imports and not have to move them around by hand. This PR shows how we could do this with isort (black doesn't quite do this per psf/black#333) After this PR lands everyone will need to update their formatter to include isort if they don't have it already, i.e. pip install -r ./python/requirements_linters.txt All future file changes will go through isort and may introduce a slightly larger PR the first time as it will clean up the imports. The plan is to land this PR and also clean up the rest of the code in parallel by using this PR to format the codebase (so people won't get surprised by the formatter if the file hasn't been touched yet) Co-authored-by: Clarence Ng <[email protected]>
I tried ufmt but it does not resolve my issues with the import order found by pylint: third party import ... should be placed before ... |
Can we please have a feature, similar to PyCharm's "optimize imports" feature, which basically sorts and removes un-used import statements?
P.S. Great work!, Black works like a charm for me.
The text was updated successfully, but these errors were encountered: