Repository: PyCQA/isort

Issue:
isort breaks the code with sort_reexports = true
Thanks for making this amazing package! 

I use isort to sort the following file with `isort --sort-reexports <file/path>`. The results break the file.

Original file:
```
from .agg import (
    Aggregation,
    AggregationRegistry,
    AttentiveAggregation,
    MeanAggregation,
    NormAggregation,
    SumAggregation,
)
from .loss import (
    BCELoss,
    BinaryDirichletLoss,
    BinaryMCCLoss,
    BoundedMSELoss,
    CrossEntropyLoss,
    DirichletMixin,
    EvidentialLoss,
    LossFunction,
    LossFunctionRegistry,
    MccMixin,
    MSELoss,
    MulticlassDirichletLoss,
    MulticlassMCCLoss,
    MVELoss,
    SIDLoss,
    WassersteinLoss,
)
from .message_passing import (
    AtomMessagePassing,
    BondMessagePassing,
    MessagePassing,
    MulticomponentMessagePassing,
)
from .metrics import (
    BCEMetric,
    BinaryAccuracyMetric,
    BinaryAUPRCMetric,
    BinaryAUROCMetric,
    BinaryF1Metric,
    BinaryMCCMetric,
    BoundedMAEMetric,
    BoundedMixin,
    BoundedMSEMetric,
    BoundedRMSEMetric,
    CrossEntropyMetric,
    MAEMetric,
    Metric,
    MetricRegistry,
    MSEMetric,
    MulticlassMCCMetric,
    R2Metric,
    RMSEMetric,
    SIDMetric,
    ThresholdedMixin,
    WassersteinMetric,
)
from .predictors import (
    BinaryClassificationFFN,
    BinaryClassificationFFNBase,
    BinaryDirichletFFN,
    EvidentialFFN,
    MulticlassClassificationFFN,
    MulticlassDirichletFFN,
    MveFFN,
    Predictor,
    PredictorRegistry,
    RegressionFFN,
    SpectralFFN,
)
from .transforms import UnscaleTransform
from .utils import Activation

__all__ = [
    "Aggregation",
    "AggregationRegistry",
    "MeanAggregation",
    "SumAggregation",
    "NormAggregation",
    "AttentiveAggregation",
    "LossFunction",
    "LossFunctionRegistry",
    "MSELoss",
    "BoundedMSELoss",
    "MVELoss",
    "EvidentialLoss",
    "BCELoss",
    "CrossEntropyLoss",
    "MccMixin",
    "BinaryMCCLoss",
    "MulticlassMCCLoss",
    "DirichletMixin",
    "BinaryDirichletLoss",
    "MulticlassDirichletLoss",
    "SIDLoss",
    "WassersteinLoss",
    "Metric",
    "MetricRegistry",
    "ThresholdedMixin",
    "MAEMetric",
    "MSEMetric",
    "RMSEMetric",
    "BoundedMixin",
    "BoundedMAEMetric",
    "BoundedMSEMetric",
    "BoundedRMSEMetric",
    "R2Metric",
    "BinaryAUROCMetric",
    "BinaryAUPRCMetric",
    "BinaryAccuracyMetric",
    "BinaryF1Metric",
    "BCEMetric",
    "CrossEntropyMetric",
    "BinaryMCCMetric",
    "MulticlassMCCMetric",
    "SIDMetric",
    "WassersteinMetric",
    "MessagePassing",
    "AtomMessagePassing",
    "BondMessagePassing",
    "MulticomponentMessagePassing",
    "Predictor",
    "PredictorRegistry",
    "RegressionFFN",
    "MveFFN",
    "EvidentialFFN",
    "BinaryClassificationFFNBase",
    "BinaryClassificationFFN",
    "BinaryDirichletFFN",
    "MulticlassClassificationFFN",
    "MulticlassDirichletFFN",
    "SpectralFFN",
    "Activation",
    "UnscaleTransform",
]
```

After sorting:
```
from .agg import (
    Aggregation,
    AggregationRegistry,
    AttentiveAggregat__all__ = ['Activation', 'Aggregation', 'AggregationRegistry', 'AtomMessagePassing', 'AttentiveAggregation',
 'BCELoss', 'BCEMetric', 'BinaryAUPRCMetric', 'BinaryAUROCMetric', 'BinaryAccuracyMetric',
 'BinaryClassificationFFN', 'BinaryClassificationFFNBase', 'BinaryDirichletFFN',
 'BinaryDirichletLoss', 'BinaryF1Metric', 'BinaryMCCLoss', 'BinaryMCCMetric', 'BondMessagePassing',
 'BoundedMAEMetric', 'BoundedMSELoss', 'BoundedMSEMetric', 'BoundedMixin', 'BoundedRMSEMetric',
 'CrossEntropyLoss', 'CrossEntropyMetric', 'DirichletMixin', 'EvidentialFFN', 'EvidentialLoss',
 'LossFunction', 'LossFunctionRegistry', 'MAEMetric', 'MSELoss', 'MSEMetric', 'MVELoss', 'MccMixin',
 'MeanAggregation', 'MessagePassing', 'Metric', 'MetricRegistry', 'MulticlassClassificationFFN',
 'MulticlassDirichletFFN', 'MulticlassDirichletLoss', 'MulticlassMCCLoss', 'MulticlassMCCMetric',
 'MulticomponentMessagePassing', 'MveFFN', 'NormAggregation', 'Predictor', 'PredictorRegistry',
 'R2Metric', 'RMSEMetric', 'RegressionFFN', 'SIDLoss', 'SIDMetric', 'SpectralFFN', 'SumAggregation',
 'ThresholdedMixin', 'UnscaleTransform', 'WassersteinLoss', 'WassersteinMetric']
classDirichletFFN,
    MveFFN,
    Predictor,
    PredictorRegistry,
    RegressionFFN,
    SpectralFFN,
)
from .transforms import UnscaleTransform
from .utils import Activation

__all__ = [
```

Any suggestions are appreciated!

Discussion:
@JacksonBurns: Probable duplicate of https://github.com/PyCQA/isort/issues/2193@hwpang: Closing this as it's duplicate of https://github.com/PyCQA/isort/issues/2193

Solution patch:
diff --git a/isort/core.py b/isort/core.py
index 10103854..472d914d 100644
--- a/isort/core.py
+++ b/isort/core.py
@@ -27,6 +27,8 @@
 LITERAL_TYPE_MAPPING = {"(": "tuple", "[": "list", "{": "set"}
 
 
+# Ignore DeepSource cyclomatic complexity check for this function.
+# skipcq: PY-R1000
 def process(
     input_stream: TextIO,
     output_stream: TextIO,
@@ -75,7 +77,7 @@ def process(
     verbose_output: List[str] = []
     lines_before: List[str] = []
     is_reexport: bool = False
-    sort_section_pointer: int = 0
+    reexport_rollback: int = 0
 
     if config.float_to_top:
         new_input = ""
@@ -134,7 +136,8 @@ def process(
 
             if code_sorting and code_sorting_section:
                 if is_reexport:
-                    output_stream.seek(sort_section_pointer, 0)
+                    output_stream.seek(output_stream.tell() - reexport_rollback)
+                    reexport_rollback = 0
                 sorted_code = textwrap.indent(
                     isort.literal.assignment(
                         code_sorting_section,
@@ -150,7 +153,9 @@ def process(
                     line_separator=line_separator,
                     ignore_whitespace=config.ignore_whitespace,
                 )
-                sort_section_pointer += output_stream.write(sorted_code)
+                output_stream.write(sorted_code)
+                if is_reexport:
+                    output_stream.truncate()
         else:
             stripped_line = line.strip()
             if stripped_line and not line_separator:
@@ -236,8 +241,8 @@ def process(
                     code_sorting_indent = line[: -len(line.lstrip())]
                     not_imports = True
                     code_sorting_section += line
+                    reexport_rollback = len(line)
                     is_reexport = True
-                    sort_section_pointer -= len(line)
                 elif code_sorting:
                     if not stripped_line:
                         sorted_code = textwrap.indent(
@@ -256,8 +261,11 @@ def process(
                             ignore_whitespace=config.ignore_whitespace,
                         )
                         if is_reexport:
-                            output_stream.seek(sort_section_pointer, 0)
-                        sort_section_pointer += output_stream.write(sorted_code)
+                            output_stream.seek(output_stream.tell() - reexport_rollback)
+                            reexport_rollback = 0
+                        output_stream.write(sorted_code)
+                        if is_reexport:
+                            output_stream.truncate()
                         not_imports = True
                         code_sorting = False
                         code_sorting_section = ""
@@ -356,8 +364,6 @@ def process(
                 else:
                     not_imports = True
 
-        sort_section_pointer += len(line)
-
         if not_imports:
             if not was_in_quote and config.lines_before_imports > -1:
                 if line.strip() == "":


Test patch:
diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py
index 7b6743c7..eff10f69 100644
--- a/tests/unit/test_isort.py
+++ b/tests/unit/test_isort.py
@@ -2,6 +2,7 @@
 
 Should be ran using py.test by simply running py.test in the isort project directory
 """
+
 import os
 import os.path
 import subprocess
@@ -5671,3 +5672,58 @@ def test_reexport_not_last_line() -> None:
     meme = "rickroll"
 """
     assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output
+
+
+def test_reexport_multiline_import() -> None:
+    test_input = """from m import (
+    bar,
+    foo,
+)
+__all__ = [
+    "foo",
+    "bar",
+]
+"""
+    expd_output = """from m import bar, foo
+
+__all__ = ['bar', 'foo']
+"""
+    assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output
+
+
+def test_reexport_multiline_in_center() -> None:
+    test_input = """from m import (
+    bar,
+    foo,
+) 
+__all__ = [
+    "foo",
+    "bar",
+]
+
+test
+"""
+    expd_output = """from m import bar, foo
+
+__all__ = ['bar', 'foo']
+
+test
+"""
+    assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output
+
+
+def test_reexport_multiline_long_rollback() -> None:
+    test_input = """from m import foo, bar 
+__all__ = [                            "foo",
+    "bar",
+]
+
+test
+"""
+    expd_output = """from m import bar, foo
+
+__all__ = ['bar', 'foo']
+
+test
+"""
+    assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output