Skip to content

Commit

Permalink
Merge pull request #2065 from parafoxia/task/fix-sort-reexports
Browse files Browse the repository at this point in the history
Fix re-export sorter
  • Loading branch information
timothycrosley committed Jan 20, 2023
2 parents 793cac2 + 5116f90 commit f31884b
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 18 deletions.
13 changes: 13 additions & 0 deletions docs/configuration/options.md
Expand Up @@ -232,6 +232,19 @@ Forces line endings to the specified value. If not set, values will be guessed p
- --le
- --line-ending

## Sort Re-exports

Specifies whether to sort re-exports (`__all__` collections) automatically.

**Type:** Bool
**Default:** `False`
**Config default:** `false`
**Python & Config File Name:** sort_reexports
**CLI Flags:**

- --srx
- --sort-reexports

## Sections

What sections isort should display imports for and in what order
Expand Down
31 changes: 15 additions & 16 deletions isort/core.py
Expand Up @@ -24,11 +24,7 @@
"# isort: unique-tuple",
"# isort: assignments",
)
LITERAL_TYPE_MAPPING = {
"(": "tuple",
"[": "list",
"{": "dict",
}
LITERAL_TYPE_MAPPING = {"(": "tuple", "[": "list", "{": "set"}


def process(
Expand Down Expand Up @@ -78,8 +74,8 @@ def process(
end_of_file: bool = False
verbose_output: List[str] = []
lines_before: List[str] = []
auto_reexporting: bool = False
line_index: int = 0
is_reexport: bool = False
sort_section_pointer: int = 0

if config.float_to_top:
new_input = ""
Expand Down Expand Up @@ -137,6 +133,8 @@ def process(
line_separator = "\n"

if code_sorting and code_sorting_section:
if is_reexport:
output_stream.seek(sort_section_pointer, 0)
sorted_code = textwrap.indent(
isort.literal.assignment(
code_sorting_section,
Expand All @@ -152,7 +150,7 @@ def process(
line_separator=line_separator,
ignore_whitespace=config.ignore_whitespace,
)
line_index += output_stream.write(sorted_code)
sort_section_pointer += output_stream.write(sorted_code)
else:
stripped_line = line.strip()
if stripped_line and not line_separator:
Expand Down Expand Up @@ -233,12 +231,13 @@ def process(
code_sorting_indent = line[: -len(line.lstrip())]
not_imports = True
elif config.sort_reexports and stripped_line.startswith("__all__"):
code_sorting = LITERAL_TYPE_MAPPING[stripped_line.split(" = ")[1][0]]
_, rhs = stripped_line.split("=")
code_sorting = LITERAL_TYPE_MAPPING.get(rhs.lstrip()[0], "tuple")
code_sorting_indent = line[: -len(line.lstrip())]
not_imports = True
code_sorting_section += line
auto_reexporting = True
line_index -= len(line) - 1
is_reexport = True
sort_section_pointer -= len(line)
elif code_sorting:
if not stripped_line:
sorted_code = textwrap.indent(
Expand All @@ -256,14 +255,14 @@ def process(
line_separator=line_separator,
ignore_whitespace=config.ignore_whitespace,
)
if auto_reexporting:
output_stream.seek(line_index, 0)
line_index += output_stream.write(sorted_code)
if is_reexport:
output_stream.seek(sort_section_pointer, 0)
sort_section_pointer += output_stream.write(sorted_code)
not_imports = True
code_sorting = False
code_sorting_section = ""
code_sorting_indent = ""
auto_reexporting = False
is_reexport = False
else:
code_sorting_section += line
line = ""
Expand Down Expand Up @@ -357,7 +356,7 @@ def process(
else:
not_imports = True

line_index += len(line)
sort_section_pointer += len(line)

if not_imports:

Expand Down
5 changes: 3 additions & 2 deletions isort/literal.py
Expand Up @@ -47,8 +47,9 @@ def assignment(code: str, sort_type: str, extension: str, config: Config = DEFAU
f"Defined sort types are {', '.join(type_mapping.keys())}."
)

variable_name, literal = code.split(" = ")
variable_name = variable_name.lstrip()
variable_name, literal = code.split("=")
variable_name = variable_name.strip()
literal = literal.lstrip()
try:
value = ast.literal_eval(literal)
except Exception as error:
Expand Down
1 change: 1 addition & 0 deletions isort/main.py
Expand Up @@ -323,6 +323,7 @@ def _build_arg_parser() -> argparse.ArgumentParser:
help="Override the format used to print success.",
)
general_group.add_argument(
"--srx",
"--sort-reexports",
dest="sort_reexports",
action="store_true",
Expand Down
81 changes: 81 additions & 0 deletions tests/unit/test_isort.py
Expand Up @@ -5591,3 +5591,84 @@ def test_infinite_loop_in_unmatched_parenthesis() -> None:

# ensure other cases are handled correctly
assert isort.code(test_input) == "from os import path, walk\n"


def test_reexport() -> None:
test_input = """__all__ = ('foo', 'bar')
"""
expd_output = """__all__ = ('bar', 'foo')
"""
assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output


def test_reexport_leave_alone_if_not_enabled() -> None:
test_input = """__all__ = ('foo', 'bar')
"""
assert isort.code(test_input) == test_input


def test_reexport_multiline() -> None:
test_input = """__all__ = (
'foo',
'bar',
)
"""
expd_output = """__all__ = ('bar', 'foo')
"""
assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output


def test_reexport_list() -> None:
test_input = """__all__ = ['foo', 'bar']
"""
expd_output = """__all__ = ['bar', 'foo']
"""
assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output


def test_reexport_set() -> None:
test_input = """__all__ = {'foo', 'bar'}
"""
expd_output = """__all__ = {'bar', 'foo'}
"""
assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output


def test_reexport_bare() -> None:
test_input = """__all__ = 'foo', 'bar'
"""
expd_output = """__all__ = ('bar', 'foo')
"""
assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output


def test_reexport_no_spaces() -> None:
test_input = """__all__=('foo', 'bar')
"""
expd_output = """__all__ = ('bar', 'foo')
"""
assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output


def test_reexport_not_first_line() -> None:
test_input = """import random
__all__ = ('foo', 'bar')
"""
expd_output = """import random
__all__ = ('bar', 'foo')
"""
assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output


def test_reexport_not_last_line() -> None:
test_input = """__all__ = ('foo', 'bar')
meme = "rickroll"
"""
expd_output = """__all__ = ('bar', 'foo')
meme = "rickroll"
"""
assert isort.code(test_input, config=Config(sort_reexports=True)) == expd_output

0 comments on commit f31884b

Please sign in to comment.