Skip to content

Commit

Permalink
Implemented #1722: Improved behavior for running isort in atomic mod…
Browse files Browse the repository at this point in the history
…e over Cython source files.
  • Loading branch information
timothycrosley committed Jun 21, 2021
1 parent eada928 commit 4cb72fa
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/
- Implemented #1737: Support for using action comments to avoid adding imports to individual files.
- Implemented #1750: Ability to customize output format lines.
- Implemented #1732: Support for custom sort functions.
- Implemented #1722: Improved behavior for running isort in atomic mode over Cython source files.
- Fixed (https://github.com/PyCQA/isort/pull/1695): added imports being added to doc string in some cases.
- Fixed (https://github.com/PyCQA/isort/pull/1714): in rare cases line continuation combined with tabs can output invalid code.
- Fixed (https://github.com/PyCQA/isort/pull/1726): isort ignores reverse_sort when force_sort_within_sections is true.
Expand Down
22 changes: 16 additions & 6 deletions isort/api.py
Expand Up @@ -37,7 +37,7 @@
from .io import Empty, File
from .place import module as place_module # noqa: F401
from .place import module_with_reason as place_module_with_reason # noqa: F401
from .settings import DEFAULT_CONFIG, Config
from .settings import CYTHON_EXTENSIONS, DEFAULT_CONFIG, Config


class ImportKey(Enum):
Expand Down Expand Up @@ -193,9 +193,14 @@ def sort_stream(
try:
file_content = input_stream.read()
compile(file_content, content_source, "exec", 0, 1)
input_stream = StringIO(file_content)
except SyntaxError:
raise ExistingSyntaxErrors(content_source)
if extension not in CYTHON_EXTENSIONS:
raise ExistingSyntaxErrors(content_source)
elif config.verbose:
warn(
f"{content_source} Python AST errors found but ignored due to Cython extension"
)
input_stream = StringIO(file_content)

if not output_stream.readable():
_internal_output = StringIO()
Expand All @@ -216,10 +221,15 @@ def sort_stream(
try:
compile(_internal_output.read(), content_source, "exec", 0, 1)
_internal_output.seek(0)
if _internal_output != output_stream:
output_stream.write(_internal_output.read())
except SyntaxError: # pragma: no cover
raise IntroducedSyntaxErrors(content_source)
if extension not in CYTHON_EXTENSIONS:
raise IntroducedSyntaxErrors(content_source)
elif config.verbose:
warn(
f"{content_source} Python AST errors found but ignored due to Cython extension"
)
if _internal_output != output_stream:
output_stream.write(_internal_output.read())

return changed

Expand Down
3 changes: 2 additions & 1 deletion isort/settings.py
Expand Up @@ -47,7 +47,8 @@
from .wrap_modes import from_string as wrap_mode_from_string

_SHEBANG_RE = re.compile(br"^#!.*\bpython[23w]?\b")
SUPPORTED_EXTENSIONS = frozenset({"py", "pyi", "pyx", "pxd"})
CYTHON_EXTENSIONS = frozenset({"pyx", "pxd"})
SUPPORTED_EXTENSIONS = frozenset({"py", "pyi", *CYTHON_EXTENSIONS})
BLOCKED_EXTENSIONS = frozenset({"pex"})
FILE_SKIP_COMMENTS: Tuple[str, ...] = (
"isort:" + "skip_file",
Expand Down
11 changes: 11 additions & 0 deletions tests/unit/test_isort.py
Expand Up @@ -1408,6 +1408,17 @@ def test_atomic_mode() -> None:
with pytest.raises(ExistingSyntaxErrors):
isort.code(test_input, atomic=True)

# unless file is for Cython which doesn't yet provide a public AST parsing API
assert (
isort.code(test_input, extension="pyx", atomic=True, verbose=True)
== isort.code(test_input, extension="pyx", atomic=True)
== """from a import e, f
from b import c, d
while True print 'Hello world'
"""
)

# ensure atomic works with streams
test_input = as_stream("from b import d, c\nfrom a import f, e\n")
test_output = UnreadableStream()
Expand Down

0 comments on commit 4cb72fa

Please sign in to comment.