Isort: Interopability with black

Created on 13 Apr 2018  ·  28Comments  ·  Source: PyCQA/isort

Hey, Timothy! Thanks for isort, it's a very useful project.

I made an opinionated formatter, Black. Quite a few of my users are also isort users. That's great, I don't want to have to perform import sorting in Black. However, there's a small issue.

None of isort's multiline modes fits what Black is doing. Black wraps lines like this:

  • try to fit everything in one line; if you can't then
  • indent the contents of the parens one level and try to fit those in one line; if you can't then
  • explode one element per line.

The second step in Black's algorithm is like your mode 5, and the third is like mode 3. I'd like to create a pull request for isort to introduce mode 8 (5+3) that does what Black does in this scenario. This would enable Black and isort to co-exist and keep consistent import styling.

Would you accept such pull request?

(I initially wrote this on Twitter but this belongs here better.)

black enhancement ongoing

Most helpful comment

@ambv sounds good!

Mostly, I realized this came up during a period where I was not as attentive to this project as I aim to be - and have been in the past. For the next release of isort in a week or so, I would like to ensure that there is an included black profile so that users don't have to mess with a ton of settings to use the two tools together. I'll start with the settings listed on the black GitHub page. And please, feel empowered to let me know of any incompatibilities and I'll do my best to resolve them in short order.

Thanks!

~Timothy

P.S. black is very cool, and something I've wanted to exist in the Python world for some time. I've introduced it in my work environment with great success and plan on using it with my open source code bases as well. So interoperability is very practically important for me :).

All 28 comments

@timothycrosley if you would accept a PR for this it would be fantastic!

In fact, Black now does what "Mode 3 + trailing commas" is doing in isort. This is no longer necessary.

Unsure whether this is a black or isort thing, but I believe there's a bit of overlap about how many lines are added after imports when using both libraries.

When using VSCode with both libraries set to format on save, the following script will be formatted alternatively with one or two line after imports. The two outputs will be set alternatively:

from collections import Mapping
from typing import Optional

class Headers:
    def __init__(seq: Optional[Mapping] = None, encoding: str = "utf-8") -> None: ...

Will output alternatively (1 line after imports):

from collections import Mapping
from typing import Optional

class Headers:
    def __init__(seq: Optional[Mapping] = None, encoding: str = "utf-8") -> None: ...

and (2 lines after imports):

from collections import Mapping
from typing import Optional


class Headers:
    def __init__(seq: Optional[Mapping] = None, encoding: str = "utf-8") -> None: ...

... back and forth.

I don't want to hijack this issue, so maybe this should be better off discussed as a distinct issue. I'm unsure whether it should be created on black or isort repositories though :). Since the initial discuss interop between the two libraries, I think it's best to start here and move somewhere else.

Thanks for creating these nice libraries.

Thanks for reopening. Black has since adopted what is almost exactly mode 3 in isort. There are a few edge cases that we can work through to ensure the tools are perfectly matched but they should be pretty close already :-)

@ambv sounds good!

Mostly, I realized this came up during a period where I was not as attentive to this project as I aim to be - and have been in the past. For the next release of isort in a week or so, I would like to ensure that there is an included black profile so that users don't have to mess with a ton of settings to use the two tools together. I'll start with the settings listed on the black GitHub page. And please, feel empowered to let me know of any incompatibilities and I'll do my best to resolve them in short order.

Thanks!

~Timothy

P.S. black is very cool, and something I've wanted to exist in the Python world for some time. I've introduced it in my work environment with great success and plan on using it with my open source code bases as well. So interoperability is very practically important for me :).

black is very cool, and something I've wanted to exist in the Python world for some time. I've introduced it in my work environment with great success and plan on using it with my open source code bases as well. So interoperability is very practically important for me

That is so nice to hear! Infact we use both of the awesome tools. i.e. black and isort in our project https://github.com/einsteinpy/einsteinpy . And this incompatibility creates a lot of issues. Can we not add a CI check for every PR to check if compatibility with black is retained?

Not sure if this belongs here:

The following line line is formatted differently by black and isort even though I am using the isort config line_lenght=88, include_trailing_comma=true, multi_line_output=3.

Import:

from aaaaaaa.bbbbbb.ccccccccccccccccccccc import dddddddddd as eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee

Formatted by black:

from aaaaaaa.bbbbbb.ccccccccccccccccccccc import (
    dddddddddd as eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee,
)

Formatted by isort:

from aaaaaaa.bbbbbb.ccccccccccccccccccccc import \
    dddddddddd as eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee

Would be nice if this gets taken care of. Personally I think the black format is the way to go as I am not a big fan of backslashes (also pep8 is not either, right?)

@DavidS3141 you need to use use_parantheses=True option (and black docs also recommend force_grid_wrap=0)

hey, thanks for the fast reply, it works! Just a typo that took me a minute longer to figure it out: use_parentheses=True.
And for all the others, the docs mentioned by @jack1142 are here under A compatible .isort.cfg!

Hi all, was able to fix all the black and isort issues with the following settings:

[tool:isort]
line_length=88
multi_line_output=3
include_trailing_comma=True

As per black's README https://github.com/psf/black#how-black-wraps-lines:

A compatible .isort.cfg

[settings]
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=88

The equivalent command line is:

$ isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --use-parentheses --line-width=88 [ file.py ]

Edit: the above works but I ended up using https://github.com/asottile/reorder_python_imports instead of isort. Its output is black-compatible without any configuration and I found that it did a better job of figuring out what is and isn't a 3rd party dependency.

has anyone seen this issue pop up again recently?

Note that for a pyproject.toml to specify the isort settings instead of an isort.cfg, you want

[tool.isort]
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
line_length = 88

The main difference is that True is capitalized in the .cfg file but not in toml.

I believe I've found yet another incompatibility between isort (v4.3.21) and black (v19.10b0):

(venv) ➜  ~ cat test.py 
"""Something something something something

Revision ID: xxxxxxxxxxxx
Revises: xxxxxxxxxxxx
Create Date: 2020-02-02 02:02:02.020202

"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
from sqlalchemy_utils import UUIDType


(venv) ➜  ~ black test.py
reformatted test.py
All done! ✨ 🍰 ✨
1 file reformatted.


(venv) ➜  ~ cat test.py  
"""Something something something something

Revision ID: xxxxxxxxxxxx
Revises: xxxxxxxxxxxx
Create Date: 2020-02-02 02:02:02.020202

"""
import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
from sqlalchemy_utils import UUIDType


(venv) ➜  ~ isort test.py
Fixing /home/pp/test.py


(venv) ➜  ~ cat test.py  
"""Something something something something

Revision ID: xxxxxxxxxxxx
Revises: xxxxxxxxxxxx
Create Date: 2020-02-02 02:02:02.020202

"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
from sqlalchemy_utils import UUIDType

I believe I've found two new scenarios where black and isort compete on formatting changes.
black == 19.10b0
isort == 4.3.21

In the first case black, isort appear to compete over the whitespace preceding the comment and the above import. I think this is different from this black reported issue https://github.com/psf/black/issues/251 but presumably related.

import external
from external import something

from ...somewhere import something_else

# A comment  
from .elsewhere import stuff

In this next case black, isort compete over the whitespace between the import and the class decorator. Black doesn't want the whitespace, but isort does (I think probably black is in the wrong here, but I'm recording the issue here for now). This issue occurs in a stub file (.pyi). The regular .py file works fine.

from .. import something

@my_class_decorator1
@my_class_decorator2
@my_class_decorator3
class MyClass(MyParent):
    pass

Note that I have applied the pyproject.toml configuration recommended above for isort to improve compatibility with black.

[tool.isort]
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
line_length = 88

I found the same question as @blueskyjunkie mentioned. When I attemp to format my python file in VSCode, isort and black will compete over the wihtespace before comment.

I have encountered another scenario where they two are holding an exact opposite opinion.

isort and black is conflicting on this line:
from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import xxxxxxxxxxxxxxxxxxxx

the line length is 90.

isort would like to format it to

-from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import xxxxxxxxxxxxxxxxxxxx
+from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import (
+    xxxxxxxxxxxxxxxxxxxx,
+)

after that, black would like to format it back:

-from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import (
-    xxxxxxxxxxxxxxxxxxxx,
-)
+from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import xxxxxxxxxxxxxxxxxxxx

these are my isort settings:

[settings]
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=88

I have encountered another scenario where they two are holding an exact opposite opinion.

isort and black is conflicting on this line:
from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import xxxxxxxxxxxxxxxxxxxx

the line length is 90.

isort would like to format it to

-from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import xxxxxxxxxxxxxxxxxxxx
+from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import (
+    xxxxxxxxxxxxxxxxxxxx,
+)

after that, black would like to format it back:

-from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import (
-    xxxxxxxxxxxxxxxxxxxx,
-)
+from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import xxxxxxxxxxxxxxxxxxxx

these are my isort settings:

[settings]
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=88

I have the same problem. How did you solve it?

I have encountered another scenario where they two are holding an exact opposite opinion.
isort and black is conflicting on this line:
from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import xxxxxxxxxxxxxxxxxxxx
the line length is 90.
isort would like to format it to

-from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import xxxxxxxxxxxxxxxxxxxx
+from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import (
+    xxxxxxxxxxxxxxxxxxxx,
+)

after that, black would like to format it back:

-from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import (
-    xxxxxxxxxxxxxxxxxxxx,
-)
+from xxxx.xxxxxxxx.xxxxxxxxxxx.xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxx import xxxxxxxxxxxxxxxxxxxx

these are my isort settings:

[settings]
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=88

I have the same problem. How did you solve it?

The conflict is due to these two lines:
use_parentheses=True line_length=88
and the import statement is 90 characters long.

As a solution, I have removed the use_parentheses=True and increased the line_length=100. However, if you increase the line length alone, it should work as well.

The previously defined line length of 88 is an out-of-date number for me since I do have quite some longer python statements. And since we have larger screens now, this historical line-length of 88 would leave screen space unutilized.

use_parentheses

Just now, I also solved this problem with my isort Settings:

[settings]
multi_line_output=6
include_trailing_comma=False
force_grid_wrap=0
use_parentheses=True
line_length=120  

Maybe I increased the line_length that solved the problem.

I was facing this issue, and couldn't work out what was wrong with my config. It turned out to be https://github.com/timothycrosley/isort/issues/1185

Edit: It wasn't that issue after all, it was just related. I'll open a new issue.

Edit 2: https://github.com/timothycrosley/isort/issues/1237

@timothycrosley Congratulations on the isort 5 release with profiles!

https://timothycrosley.github.io/isort/docs/major_releases/introducing_isort_5/

Should the profile for Black include line_length: 88 to match the Black default?

https://timothycrosley.github.io/isort/docs/configuration/profiles/

@hugovk Thanks!

Good catch! Originally, profile where given the highest priority overriding any other settings - so I didn't include line_length as there would be no easy way to override it and it is configurable in black as well. However, testing showed that wasn't the best approach and now profile sit above the defaults but below custom settings. I've updated the black profile to be 88 line_length.

This thread is a little bit confusing. As a black user, can I enable isort now or if so what do I need to do it in a way that does not conflict with black?

@ssbarnea yes you can enable isort alongside black! isort has been compatible with black for a while (with a complex combination of settings). With the latest release isort is fully compatible with black simply by using the black profile isort --profile black .. This thread is meant to be a coordination thread between the two projects to ensure compatibility is never broken.

Sadly using black profile does not change the default line length from 79 to 88. Also isort seems not to be able to read the value configured in flake8 config either, so I was forced to repeat configuring the line length alongside black profile. There is an open change at https://github.com/ansible/ansible-lint/pull/887/files -- note that the project does not use black but using the black profile should have not mattered, my expectation was to default to the magic 88 value :D

Sadly using black profile does not change the default line length from 79 to 88.

It should, shouldn't it?
https://github.com/timothycrosley/isort/blob/b49aba99b919473b66f05b663588cfac2472c601/isort/profiles.py#L4-L11

I'm going to close this issue now, as there as been some misunderstanding that it means that isort isn't compatible with black. If in the future anyone notices an incompatibility please raise it as a new issue and it will have the new black label applied and be fixed with the highest priority. isort is committed to long term compatibility with the black project.

Was this page helpful?
0 / 5 - 0 ratings