-
-
Notifications
You must be signed in to change notification settings - Fork 590
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
6.0.0: isort
is not stable on a file which imports multiple members from the same module, one aliased and another imported directly, with --profile black
#2352
Comments
We also noticed this issue, but with an extra step. This doesn't always happen the first time depending on the input. For example if you start with code like this: from another_file_aaaaaaaaaaaaaaaaaa import \
SOME_THING_CONSTANT as SOME_THING_CONSTANT_RENAMED
from another_file_aaaaaaaaaaaaaaaaaa import some_func then you run from another_file_aaaaaaaaaaaaaaaaaa import (
SOME_THING_CONSTANT as SOME_THING_CONSTANT_RENAMED,
)
from another_file_aaaaaaaaaaaaaaaaaa import some_func then you run from another_file_aaaaaaaaaaaaaaaaaa import (
SOME_THING_CONSTANT as SOME_THING_CONSTANT_RENAMED,
)
from another_file_aaaaaaaaaaaaaaaaaa import (
some_func,
) |
This seems related to this breaking change (not documented as such!) in default behavior. #2236 In my case it breaks compatibility with diff --git a/pyproject.toml b/pyproject.toml
index c3dd914..2a98b33 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -284,8 +284,11 @@ skip-magic-trailing-comma = false
line-ending = "auto"
docstring-code-format = false
docstring-code-line-length = "dynamic"
+[tool.ruff.lint.isort]
+split-on-trailing-comma = false
[tool.isort]
profile = "black"
src_paths = ["src"]
skip_glob = ["src/*/typings", "src/*/build"]
+split_on_trailing_comma = false |
@aholmes technically it is not a breaking anything. It is just a regular improvement on sorting. Django is a projecto se have integration tests for QA, but we had to comment because of this single case. @sarahboyce I would advice to upgrade to 6.0.0 and keep the new sorting. It will work perfectly and everything is sorted as it should. |
@staticdev correct me if I'm mistaken, but this is an observable change in isort's default behavior, right? |
Even if this is an intended change, (which I think is still not correct since the second import did not have a trailing comma that would make it split lines) - I can live with that if you think it is correct. However the problem I described in my comment is still an issue in my opinion since it requires running isort twice to stabilize the sort. It already happened to us that a developer ran isort locally only once and pushed the changes which then failed on CI because the output changes when you run isort the second time. Initially I thought this was related, but if the change is intended I can split the "you have to run isort twice" in to to a separate issue if you like. |
@LepkoQQ in this case I would definitely agree. Isort must be stable. I would like to confirm it is really happening. |
I would agree that this is indeed a regression, even if the sorting is stable. I hope to devote some time tonight to look into this. I must confess when I looked at the original |
@LepkoQQ I just cloned the Django repository and actually the sorting is stable, it works on the first time (no additional changes on second run). |
The Django repo already had isort applied at least once. I can replicate with the instructions of this comment 👍 |
@staticdev Thanks for confirming. I think I should clarify my usage of "breaking change." I don't mean to say that this change is broken or wrong; I mean that the change alters previous behavior and, as a result, potentially creates downstream breakages. In my case, this change causes my CI pipeline to fail because Either way, the point is somewhat moot because this is in a major-version change, anyway. I appreciate the work that went into this release and to improve |
By the way I can also replicate #2352 (comment) #!/bin/bash
read -r -d '' UNFORMATTED <<EOF
from another_file_aaaaaaaaaaaaaaaaaa import \\
SOME_THING_CONSTANT as SOME_THING_CONSTANT_RENAMED
from another_file_aaaaaaaaaaaaaaaaaa import some_func
EOF
echo Unformatted:
echo "$UNFORMATTED"
echo '-------------------------------------------------'
FORMATTED1="`isort --profile black - <<<"$UNFORMATTED"`"
echo Formatted 1:
echo "$FORMATTED1"
echo '-------------------------------------------------'
FORMATTED2="`isort --profile black - <<<"$FORMATTED1"`"
echo Formatted 2:
echo "$FORMATTED2"
|
@sarahboyce What would the desired output be of the original code block? Wouldn't this be more correct? from django.db.models.sql.compiler import (
SQLAggregateCompiler,
SQLCompiler,
SQLDeleteCompiler,
SQLUpdateCompiler,
)
from django.db.models.sql.compiler import SQLInsertCompiler as BaseSQLInsertCompiler What about the Anyway, this is indeed a regression. What (I think) is happening is that we "correctly" identify that If @staticdev gives me some pointers I can likely try and come up with a fix that either keeps the code as is, or puts the import of |
A fix with test would be: diff --git a/isort/output.py b/isort/output.py
index ade4ad29..e098d858 100644
--- a/isort/output.py
+++ b/isort/output.py
@@ -513,7 +513,7 @@ def _with_from_imports(
do_multiline_reformat = True
if (
- import_statement
+ len(import_statement) > config.line_length
and config.split_on_trailing_comma
and module in parsed.trailing_commas
):
diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py
index 61f66e7e..01ce9835 100644
--- a/tests/integration/test_projects_using_isort.py
+++ b/tests/integration/test_projects_using_isort.py
@@ -28,10 +28,6 @@ def run_isort(arguments: Generator[str, None, None] | Sequence[str]):
main(["--check-only", "--diff", *arguments])
-@pytest.mark.xfail(
- reason="Project is incorrectly formatted after PR #2236, should be fixed "
- "after a release and the project formatting again."
-)
def test_django(tmpdir):
git_clone("https://github.com/django/django.git", tmpdir)
run_isort(
diff --git a/tests/unit/profiles/test_black.py b/tests/unit/profiles/test_black.py
index 209d83d8..19885d77 100644
--- a/tests/unit/profiles/test_black.py
+++ b/tests/unit/profiles/test_black.py
@@ -456,3 +456,24 @@ from x import (
)
""",
)
+
+
+def test_black_trailing_comma_below_line_length():
+ black_test(
+"""from django.db.models.sql.compiler import (
+ SQLAggregateCompiler,
+ SQLCompiler,
+ SQLDeleteCompiler,
+)
+from django.db.models.sql.compiler import SQLInsertCompiler as BaseSQLInsertCompiler
+from django.db.models.sql.compiler import SQLUpdateCompiler
+""",
+"""from django.db.models.sql.compiler import (
+ SQLAggregateCompiler,
+ SQLCompiler,
+ SQLDeleteCompiler,
+)
+from django.db.models.sql.compiler import SQLInsertCompiler as BaseSQLInsertCompiler
+from django.db.models.sql.compiler import SQLUpdateCompiler
+""",
+ )
However, this doesn't actually work as we would no longer respect actual trailing commas if the line length is below the max line length. The issue is that at the place where I make the change to This was originally implemented with |
@sarahboyce I need to know if after this discussion you still think we have an issue and what exactly is this issue. We need to be on the same page on this to see if a change is needed on isort or not. As mentioned before, we had a change that affect how black profile deals split_on_trailing_comma (#2340). When such changes happen, it is normal that isort will resort some file differently like it happened on Django repo. If you still think after upgrade, the output is not what is desired, I need to know exactly why and what would you expect as the output so we can further analyse. Otherwise, you can close this issue if you think this was clarified and we can continue with this output. |
Reading the discussion, personally I think it's clear that we have an issue, and it's illustrated beautifully by @aholmes comment #2352 (comment) The issue is not that isort has made a change going from "Unformatted" to "Formatted 1" in @aholmes's example. The issue is that "Formatted 1" and "Formatted 2" are different. This issue can be viewed in two ways: (A) something that has already been isorted shouldn't change just because we isort it again, and (B) the correct formatting of "Unformatted" is actually "Formatted 1", not "Formatted 2" — going straight from "Unformatted" to "Formatted 2" would solve (A) but not (B). We want "Formatted 1" to be the solution when formatting "Unformatted". Please take close look at #2352 (comment) and see if you agree. I think a change is needed on isort. (Thanks also to @LepkoQQ for #2352 (comment).) |
When I first opened the issue, I wanted to have this confirmed by isort whether this is a wanted changed. I don't have an opinion on whether the output on 5.13.2 is more correct than the new output on 6.0.0. If you believe this is how it should be @staticdev - that's fine. |
isort
is not stable on a file which imports multiple members from the same module, one aliased and another imported directly, with --profile black
I still believe this is all caused by the issue I wrote about above. The handling of trailing commas for modules that appear multiple times in the import statements is inconsistent and causes unstable behaviour. Input: from django.db.models.sql.compiler import (
SQLAggregateCompiler,
SQLCompiler,
SQLDeleteCompiler,
)
from django.db.models.sql.compiler import SQLInsertCompiler as BaseSQLInsertCompiler
from django.db.models.sql.compiler import SQLUpdateCompiler Output: from django.db.models.sql.compiler import (
SQLAggregateCompiler,
SQLCompiler,
SQLDeleteCompiler,
)
from django.db.models.sql.compiler import SQLInsertCompiler as BaseSQLInsertCompiler
from django.db.models.sql.compiler import (
SQLUpdateCompiler,
) Input: from django.db.models.sql.compiler import SQLInsertCompiler as BaseSQLInsertCompiler
from django.db.models.sql.compiler import SQLUpdateCompiler Output: from django.db.models.sql.compiler import SQLInsertCompiler as BaseSQLInsertCompiler
from django.db.models.sql.compiler import SQLUpdateCompiler As you can see, the existence of one import statement where This is exactly the same behaviour that we see in the report for unstable behaviour reported by @LepkoQQ. Input: from another_file_aaaaaaaaaaaaaaaaaa import \
SOME_THING_CONSTANT as SOME_THING_CONSTANT_RENAMED
from another_file_aaaaaaaaaaaaaaaaaa import some_func First output: from another_file_aaaaaaaaaaaaaaaaaa import (
SOME_THING_CONSTANT as SOME_THING_CONSTANT_RENAMED,
)
from another_file_aaaaaaaaaaaaaaaaaa import some_func See how this adds a trailing comma to Second output: from another_file_aaaaaaaaaaaaaaaaaa import (
SOME_THING_CONSTANT as SOME_THING_CONSTANT_RENAMED,
)
from another_file_aaaaaaaaaaaaaaaaaa import (
some_func,
) The first import statement now has a trailing comma. Because of the behaviour change and influence this now has on other import statements a second run of Fixing the issue where trailing commas in one import statement influence trailing commas for different import statements (but from the same module) will fix both the originally reported issue as well as remove the instability. See my post above for more context on why it is difficult to fix this with the current infrastructure/code. |
On Django, our
isort
config is:With
isort==5.13.2
, the following imports are sorted as:But on
isort==6.0.0
, they are now sorted as:We don't believe this is a desirable change and so are pinned at 5.13.2
(Please let me know if you need more details. Our pr for reference: django/django#19109)
The text was updated successfully, but these errors were encountered: