From a64712abdc197896a44f7c6ddea3acfc4f9735e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Noord?= <13665637+DanielNoord@users.noreply.github.com> Date: Sun, 27 Sep 2026 17:18:08 +0200 Subject: [PATCH] Fully type check `tests` and modernize code --- pyproject.toml | 10 - tests/benchmark/test_api.py | 25 +- tests/integration/test_hypothesmith.py | 4 +- .../integration/test_projects_using_isort.py | 65 ++-- .../integration/test_setting_combinations.py | 4 +- tests/unit/conftest.py | 10 +- tests/unit/profiles/test_attrs.py | 6 +- tests/unit/profiles/test_black.py | 16 +- tests/unit/profiles/test_django.py | 6 +- tests/unit/profiles/test_google.py | 8 +- tests/unit/profiles/test_hug.py | 6 +- tests/unit/profiles/test_open_stack.py | 6 +- tests/unit/profiles/test_plone.py | 10 +- tests/unit/profiles/test_pycharm.py | 2 +- tests/unit/profiles/test_wemake.py | 10 +- tests/unit/test_action_comments.py | 4 +- tests/unit/test_api.py | 41 +-- tests/unit/test_comments.py | 8 +- tests/unit/test_exceptions.py | 54 ++-- tests/unit/test_files.py | 8 +- tests/unit/test_format.py | 22 +- tests/unit/test_hooks.py | 6 +- tests/unit/test_identify.py | 15 +- tests/unit/test_importable.py | 2 +- tests/unit/test_io.py | 19 +- tests/unit/test_isort.py | 277 ++++++++++-------- tests/unit/test_lazy_imports.py | 30 +- tests/unit/test_literal.py | 60 ++-- tests/unit/test_main.py | 261 ++++++++--------- tests/unit/test_output.py | 4 +- tests/unit/test_parse.py | 16 +- tests/unit/test_place.py | 9 +- tests/unit/test_regressions.py | 199 ++++++------- tests/unit/test_settings.py | 119 ++++---- tests/unit/test_ticketed_features.py | 97 +++--- tests/unit/test_utils.py | 2 +- tests/unit/test_wrap.py | 6 +- tests/unit/test_wrap_modes.py | 256 ++++++++-------- tests/unit/utils.py | 7 +- 39 files changed, 869 insertions(+), 841 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f35808efd..8784d3c00 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -158,16 +158,6 @@ follow_imports = "silent" packages = ["isort", "tests"] exclude = "isort/_vendored|tests/unit/example_projects|tests/unit/example_crlf_file.py" -[[tool.mypy.overrides]] -module = "tests.*" -allow_untyped_defs = true -allow_incomplete_defs = true -allow_untyped_calls = true - -[[tool.mypy.overrides]] -module = "importlib_metadata.*" -ignore_missing_imports = true - [tool.ruff] target-version = "py310" line-length = 100 diff --git a/tests/benchmark/test_api.py b/tests/benchmark/test_api.py index a6091b724..e2483f1f9 100644 --- a/tests/benchmark/test_api.py +++ b/tests/benchmark/test_api.py @@ -1,31 +1,38 @@ -from typing import Any +from collections.abc import Callable +from pathlib import Path +from typing import Protocol import pytest from isort import api + +class BenchmarkFixture(Protocol): + def pedantic(self, target: Callable[[], object], *, iterations: int, rounds: int) -> None: ... + + imperfect_content = "import b\nimport a\n" fixed_content = "import a\nimport b\n" @pytest.fixture -def imperfect(tmpdir) -> Any: - imperfect_file = tmpdir.join("test_needs_changes.py") +def imperfect(tmp_path: Path) -> Path: + imperfect_file = tmp_path / "test_needs_changes.py" imperfect_file.write_text(imperfect_content, "utf8") return imperfect_file -def test_sort_file(benchmark, imperfect) -> None: - def sort_file(): +def test_sort_file(benchmark: BenchmarkFixture, imperfect: Path) -> None: + def sort_file() -> None: api.sort_file(imperfect) benchmark.pedantic(sort_file, iterations=10, rounds=100) - assert imperfect.read() == fixed_content + assert imperfect.read_text() == fixed_content -def test_sort_file_in_place(benchmark, imperfect) -> None: - def sort_file(): +def test_sort_file_in_place(benchmark: BenchmarkFixture, imperfect: Path) -> None: + def sort_file() -> None: api.sort_file(imperfect, overwrite_in_place=True) benchmark.pedantic(sort_file, iterations=10, rounds=100) - assert imperfect.read() == fixed_content + assert imperfect.read_text() == fixed_content diff --git a/tests/integration/test_hypothesmith.py b/tests/integration/test_hypothesmith.py index 5e824ae16..e1d62023c 100644 --- a/tests/integration/test_hypothesmith.py +++ b/tests/integration/test_hypothesmith.py @@ -1,5 +1,5 @@ import ast -from typing import get_type_hints +from typing import Any, get_type_hints import hypothesis import libcst @@ -9,7 +9,7 @@ import isort -def _as_config(kw) -> isort.Config: +def _as_config(kw: dict[str, Any]) -> isort.Config: if "wrap_length" in kw and "line_length" in kw: kw["wrap_length"], kw["line_length"] = sorted([kw["wrap_length"], kw["line_length"]]) try: diff --git a/tests/integration/test_projects_using_isort.py b/tests/integration/test_projects_using_isort.py index df5e98385..9c8581ed8 100644 --- a/tests/integration/test_projects_using_isort.py +++ b/tests/integration/test_projects_using_isort.py @@ -18,51 +18,52 @@ from isort.main import main -def git_clone(repository_url: str, directory: Path): +def git_clone(repository_url: str, directory: Path) -> None: """Clones the given repository into the given directory path""" check_call(["git", "clone", "--depth", "1", repository_url, str(directory)]) -def run_isort(arguments: Generator[str, None, None] | Sequence[str]): +def run_isort(arguments: Generator[str, None, None] | Sequence[str]) -> None: """Runs isort in diff and check mode with the given arguments""" main(["--check-only", "--diff", *arguments]) -def test_django(tmpdir): - git_clone("https://github.com/django/django.git", tmpdir) +def test_django(tmp_path: Path) -> None: + git_clone("https://github.com/django/django.git", tmp_path) run_isort( - str(target_dir) for target_dir in (tmpdir / "django", tmpdir / "tests", tmpdir / "scripts") + str(target_dir) + for target_dir in (tmp_path / "django", tmp_path / "tests", tmp_path / "scripts") ) -def test_plone(tmpdir): - git_clone("https://github.com/plone/plone.app.multilingualindexes.git", tmpdir) - run_isort([str(tmpdir / "src"), "--skip", "languagefallback.py"]) +def test_plone(tmp_path: Path) -> None: + git_clone("https://github.com/plone/plone.app.multilingualindexes.git", tmp_path) + run_isort([str(tmp_path / "src"), "--skip", "languagefallback.py"]) @pytest.mark.skip( "Skip for now as #2295 introduce a breaking change. Can be re-enabled after pandas has updated." ) -def test_pandas(tmpdir): - git_clone("https://github.com/pandas-dev/pandas.git", tmpdir) - run_isort((str(tmpdir / "pandas"), "--skip", "__init__.py")) +def test_pandas(tmp_path: Path) -> None: + git_clone("https://github.com/pandas-dev/pandas.git", tmp_path) + run_isort((str(tmp_path / "pandas"), "--skip", "__init__.py")) -def test_habitat_lab(tmpdir): - git_clone("https://github.com/facebookresearch/habitat-lab.git", tmpdir) - run_isort([str(tmpdir)]) +def test_habitat_lab(tmp_path: Path) -> None: + git_clone("https://github.com/facebookresearch/habitat-lab.git", tmp_path) + run_isort([str(tmp_path)]) -def test_pylint(tmpdir): - git_clone("https://github.com/PyCQA/pylint.git", tmpdir) - run_isort([str(tmpdir), "--skip", "bad.py"]) +def test_pylint(tmp_path: Path) -> None: + git_clone("https://github.com/PyCQA/pylint.git", tmp_path) + run_isort([str(tmp_path), "--skip", "bad.py"]) -def test_hypothesis(tmpdir): - git_clone("https://github.com/HypothesisWorks/hypothesis.git", tmpdir) +def test_hypothesis(tmp_path: Path) -> None: + git_clone("https://github.com/HypothesisWorks/hypothesis.git", tmp_path) run_isort( ( - str(tmpdir), + str(tmp_path), "--skip", "tests", "--profile", @@ -76,24 +77,24 @@ def test_hypothesis(tmpdir): ) -def test_pyramid(tmpdir): - git_clone("https://github.com/Pylons/pyramid.git", tmpdir) +def test_pyramid(tmp_path: Path) -> None: + git_clone("https://github.com/Pylons/pyramid.git", tmp_path) run_isort( str(target_dir) - for target_dir in (tmpdir / "src" / "pyramid", tmpdir / "tests", tmpdir / "setup.py") + for target_dir in (tmp_path / "src" / "pyramid", tmp_path / "tests", tmp_path / "setup.py") ) -def test_products_zopetree(tmpdir): - git_clone("https://github.com/jugmac00/Products.ZopeTree.git", tmpdir) - run_isort([str(tmpdir)]) +def test_products_zopetree(tmp_path: Path) -> None: + git_clone("https://github.com/jugmac00/Products.ZopeTree.git", tmp_path) + run_isort([str(tmp_path)]) -def test_dobby(tmpdir): - git_clone("https://github.com/rocketDuck/dobby.git", tmpdir) - run_isort([str(tmpdir / "tests"), str(tmpdir / "src")]) +def test_dobby(tmp_path: Path) -> None: + git_clone("https://github.com/rocketDuck/dobby.git", tmp_path) + run_isort([str(tmp_path / "tests"), str(tmp_path / "src")]) -def test_zope(tmpdir): - git_clone("https://github.com/zopefoundation/Zope.git", tmpdir) - run_isort([str(tmpdir), "--skip", "util.py"]) +def test_zope(tmp_path: Path) -> None: + git_clone("https://github.com/zopefoundation/Zope.git", tmp_path) + run_isort([str(tmp_path), "--skip", "util.py"]) diff --git a/tests/integration/test_setting_combinations.py b/tests/integration/test_setting_combinations.py index 0badd0a8b..92ba861f8 100644 --- a/tests/integration/test_setting_combinations.py +++ b/tests/integration/test_setting_combinations.py @@ -1,4 +1,4 @@ -from typing import get_type_hints +from typing import Any, get_type_hints import hypothesis from hypothesis import strategies as st @@ -6,7 +6,7 @@ import isort -def _as_config(kw) -> isort.Config: +def _as_config(kw: dict[str, Any]) -> isort.Config: kw["atomic"] = False if "wrap_length" in kw and "line_length" in kw: kw["wrap_length"], kw["line_length"] = sorted([kw["wrap_length"], kw["line_length"]]) diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 4d8b87366..6518f8869 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -10,25 +10,25 @@ @pytest.fixture -def test_dir(): +def test_dir() -> str: return TEST_DIR @pytest.fixture -def src_dir(): +def src_dir() -> str: return SRC_DIR @pytest.fixture -def test_path(): +def test_path() -> Path: return Path(TEST_DIR).resolve() @pytest.fixture -def src_path(): +def src_path() -> Path: return Path(SRC_DIR).resolve() @pytest.fixture -def examples_path(): +def examples_path() -> Path: return Path(TEST_DIR).resolve() / "example_projects" diff --git a/tests/unit/profiles/test_attrs.py b/tests/unit/profiles/test_attrs.py index c08f184ef..26ea9bf3b 100644 --- a/tests/unit/profiles/test_attrs.py +++ b/tests/unit/profiles/test_attrs.py @@ -5,7 +5,7 @@ attrs_isort_test = partial(isort_test, profile="attrs") -def test_attrs_code_snippet_one(): +def test_attrs_code_snippet_one() -> None: attrs_isort_test( """from __future__ import absolute_import, division, print_function @@ -35,7 +35,7 @@ def test_attrs_code_snippet_one(): ) -def test_attrs_code_snippet_two(): +def test_attrs_code_snippet_two() -> None: attrs_isort_test( """from __future__ import absolute_import, division, print_function @@ -72,7 +72,7 @@ def test_attrs_code_snippet_two(): ) -def test_attrs_code_snippet_three(): +def test_attrs_code_snippet_three() -> None: attrs_isort_test( ''' """ diff --git a/tests/unit/profiles/test_black.py b/tests/unit/profiles/test_black.py index 209d83d82..ebd4f30b5 100644 --- a/tests/unit/profiles/test_black.py +++ b/tests/unit/profiles/test_black.py @@ -1,3 +1,5 @@ +from typing import Any + import black from black.report import NothingChanged @@ -19,7 +21,9 @@ def black_format(code: str, is_pyi: bool = False, line_length: int = 88) -> str: return code -def black_test(code: str, expected_output: str = "", *, is_pyi: bool = False, **config_kwargs): +def black_test( + code: str, expected_output: str = "", *, is_pyi: bool = False, **config_kwargs: Any +) -> None: """Tests that the given code: - Behaves the same when formatted multiple times with isort. - Agrees with black formatting. @@ -44,7 +48,7 @@ def black_test(code: str, expected_output: str = "", *, is_pyi: bool = False, ** assert output == expected_output -def test_black_snippet_one(): +def test_black_snippet_one() -> None: """Test consistent code formatting between isort and black for code snippet from black repository. See: https://github.com/psf/black/blob/master/tests/test_black.py """ @@ -158,7 +162,7 @@ def test_black_snippet_one(): ) -def test_black_snippet_two(): +def test_black_snippet_two() -> None: """Test consistent code formatting between isort and black for code snippet from black repository. See: https://github.com/psf/black/blob/master/tests/test_primer.py """ @@ -233,7 +237,7 @@ def test_black_snippet_two(): ) -def test_black_snippet_three(): +def test_black_snippet_three() -> None: """Test consistent code formatting between isort and black for code snippet from black repository. See: https://github.com/psf/black/blob/master/src/black/__init__.py """ @@ -376,7 +380,7 @@ def test_black_snippet_three(): ) -def test_black_pyi_file(): +def test_black_pyi_file() -> None: """Test consistent code formatting between isort and black for `.pyi` files. black only allows no more than two consecutive blank lines in a `.pyi` file. @@ -445,7 +449,7 @@ def sub(a: np.ndarray, b: np.ndarray) -> np.ndarray: ... ) -def test_black_trailing_comma(): +def test_black_trailing_comma() -> None: black_test( "from x import (a, b, c,)\n", """\ diff --git a/tests/unit/profiles/test_django.py b/tests/unit/profiles/test_django.py index c2202717f..72be5451e 100644 --- a/tests/unit/profiles/test_django.py +++ b/tests/unit/profiles/test_django.py @@ -5,7 +5,7 @@ django_isort_test = partial(isort_test, profile="django", known_first_party=["django"]) -def test_django_snippet_one(): +def test_django_snippet_one() -> None: django_isort_test( """import copy import inspect @@ -57,7 +57,7 @@ def __str__(self): ) -def test_django_snippet_two(): +def test_django_snippet_two() -> None: django_isort_test( '''from django.utils.version import get_version @@ -86,7 +86,7 @@ def setup(set_prefix=True): ) -def test_django_snippet_three(): +def test_django_snippet_three() -> None: django_isort_test( """import cgi import codecs diff --git a/tests/unit/profiles/test_google.py b/tests/unit/profiles/test_google.py index 1017eaf0c..ba5d2630b 100644 --- a/tests/unit/profiles/test_google.py +++ b/tests/unit/profiles/test_google.py @@ -5,7 +5,7 @@ google_isort_test = partial(isort_test, profile="google") -def test_google_code_snippet_shared_example(): +def test_google_code_snippet_shared_example() -> None: """Tests snippet examples directly shared with the isort project. See: https://github.com/PyCQA/isort/issues/1486. """ @@ -22,7 +22,7 @@ def test_google_code_snippet_shared_example(): ) -def test_google_code_snippet_one(): +def test_google_code_snippet_one() -> None: google_isort_test( '''# coding=utf-8 # Copyright 2018 Google LLC @@ -232,7 +232,7 @@ def test_google_code_snippet_one(): ) -def test_google_code_snippet_two(): +def test_google_code_snippet_two() -> None: google_isort_test( """#!/usr/bin/env python # In[ ]: @@ -316,7 +316,7 @@ def test_google_code_snippet_two(): ) -def test_code_snippet_three(): +def test_code_snippet_three() -> None: google_isort_test( '''# Copyright 2019 Google LLC # diff --git a/tests/unit/profiles/test_hug.py b/tests/unit/profiles/test_hug.py index f10b3ee61..d009187e9 100644 --- a/tests/unit/profiles/test_hug.py +++ b/tests/unit/profiles/test_hug.py @@ -5,7 +5,7 @@ hug_isort_test = partial(isort_test, profile="hug", known_first_party=["hug"]) -def test_hug_code_snippet_one(): +def test_hug_code_snippet_one() -> None: hug_isort_test( ''' from __future__ import absolute_import @@ -53,7 +53,7 @@ def test_hug_code_snippet_one(): ) -def test_hug_code_snippet_two(): +def test_hug_code_snippet_two() -> None: hug_isort_test( """from __future__ import absolute_import @@ -76,7 +76,7 @@ def default_output_format( ) -def test_hug_code_snippet_three(): +def test_hug_code_snippet_three() -> None: hug_isort_test( """from __future__ import absolute_import diff --git a/tests/unit/profiles/test_open_stack.py b/tests/unit/profiles/test_open_stack.py index 4639dba2a..3b6ed4879 100644 --- a/tests/unit/profiles/test_open_stack.py +++ b/tests/unit/profiles/test_open_stack.py @@ -5,7 +5,7 @@ open_stack_isort_test = partial(isort_test, profile="open_stack") -def test_open_stack_code_snippet_one(): +def test_open_stack_code_snippet_one() -> None: open_stack_isort_test( """import httplib import logging @@ -33,7 +33,7 @@ def test_open_stack_code_snippet_one(): ) -def test_open_stack_code_snippet_two(): +def test_open_stack_code_snippet_two() -> None: open_stack_isort_test( """# Copyright 2011 VMware, Inc # All Rights Reserved. @@ -83,7 +83,7 @@ def test_open_stack_code_snippet_two(): ) -def test_open_stack_code_snippet_three(): +def test_open_stack_code_snippet_three() -> None: open_stack_isort_test( """ # Copyright 2013 Red Hat, Inc. diff --git a/tests/unit/profiles/test_plone.py b/tests/unit/profiles/test_plone.py index ea0b68ddb..8dd62eed3 100644 --- a/tests/unit/profiles/test_plone.py +++ b/tests/unit/profiles/test_plone.py @@ -5,7 +5,7 @@ plone_isort_test = partial(isort_test, profile="plone") -def test_plone_code_snippet_one(): +def test_plone_code_snippet_one() -> None: plone_isort_test( """# -*- coding: utf-8 -*- from plone.app.multilingual.testing import PLONE_APP_MULTILINGUAL_PRESET_FIXTURE # noqa @@ -29,7 +29,7 @@ def test_plone_code_snippet_one(): ) -def test_plone_code_snippet_two(): +def test_plone_code_snippet_two() -> None: plone_isort_test( """# -*- coding: utf-8 -*- from Acquisition import aq_base @@ -58,7 +58,7 @@ def test_plone_code_snippet_two(): ) -def test_plone_code_snippet_three(): +def test_plone_code_snippet_three() -> None: plone_isort_test( """# -*- coding: utf-8 -*- from plone.app.querystring.interfaces import IQueryModifier @@ -72,7 +72,7 @@ def test_plone_code_snippet_three(): ) -def test_plone_code_snippet_four(): +def test_plone_code_snippet_four() -> None: plone_isort_test( """# -*- coding: utf-8 -*- from plone.app.querystring.interfaces import IQueryModifier @@ -88,7 +88,7 @@ def my_function(): ) -def test_plone_code_snippet_five(): +def test_plone_code_snippet_five() -> None: plone_isort_test( """# -*- coding: utf-8 -*- from plone.app.querystring.interfaces import IQueryModifier diff --git a/tests/unit/profiles/test_pycharm.py b/tests/unit/profiles/test_pycharm.py index f3ce1fd32..60e7e64e1 100644 --- a/tests/unit/profiles/test_pycharm.py +++ b/tests/unit/profiles/test_pycharm.py @@ -5,7 +5,7 @@ pycharm_isort_test = partial(isort_test, profile="pycharm") -def test_pycharm_snippet_one(): +def test_pycharm_snippet_one() -> None: pycharm_isort_test( """import shutil import sys diff --git a/tests/unit/profiles/test_wemake.py b/tests/unit/profiles/test_wemake.py index a2caa57af..bb9acc242 100644 --- a/tests/unit/profiles/test_wemake.py +++ b/tests/unit/profiles/test_wemake.py @@ -13,7 +13,7 @@ ) -def test_wemake_snippet_one(): +def test_wemake_snippet_one() -> None: wemake_isort_test( """ import ast @@ -40,7 +40,7 @@ def test_wemake_snippet_one(): ) -def test_wemake_snippet_two(): +def test_wemake_snippet_two() -> None: wemake_isort_test( """ from collections import defaultdict @@ -64,7 +64,7 @@ def test_wemake_snippet_two(): ) -def test_wemake_snippet_three(): +def test_wemake_snippet_three() -> None: wemake_isort_test( """ import ast @@ -88,7 +88,7 @@ class _ClassVisitor(ast.NodeVisitor): ... ) -def test_wemake_snippet_four(): +def test_wemake_snippet_four() -> None: """80 line length should not be fixed""" wemake_isort_test( """ @@ -100,7 +100,7 @@ def test_wemake_snippet_four(): ) -def test_wemake_snippet_five(): +def test_wemake_snippet_five() -> None: """81 line length should be fixed""" wemake_isort_test( """ diff --git a/tests/unit/test_action_comments.py b/tests/unit/test_action_comments.py index 266e30fe6..075158c3b 100644 --- a/tests/unit/test_action_comments.py +++ b/tests/unit/test_action_comments.py @@ -3,7 +3,7 @@ import isort -def test_isort_off_and_on(): +def test_isort_off_and_on() -> None: """Test so ensure isort: off action comment and associated on action comment work together""" # as top of file comment @@ -48,7 +48,7 @@ def test_isort_off_and_on(): ) -def test_skip_sort_reexports(): +def test_skip_sort_reexports() -> None: code = """my_list = [\"bee\", \"Albatross\", \"Dinosaur\", \"cat\"] __all__ = my_list # isort: skip """ diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index c6e65f8c4..2561df1de 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -2,6 +2,7 @@ import os from io import StringIO +from pathlib import Path from unittest.mock import MagicMock, patch import pytest @@ -15,14 +16,14 @@ @pytest.fixture -def imperfect(tmpdir): - imperfect_file = tmpdir.join("test_needs_changes.py") +def imperfect(tmp_path: Path) -> Path: + imperfect_file = tmp_path / "test_needs_changes.py" imperfect_file.write_text(imperfect_content, "utf8") return imperfect_file -def test_sort_file_with_bad_syntax(tmpdir) -> None: - tmp_file = tmpdir.join("test_bad_syntax.py") +def test_sort_file_with_bad_syntax(tmp_path: Path) -> None: + tmp_file = tmp_path / "test_bad_syntax.py" tmp_file.write_text("""print('mismatching quotes")""", "utf8") with pytest.warns(UserWarning): api.sort_file(tmp_file, atomic=True) @@ -30,44 +31,44 @@ def test_sort_file_with_bad_syntax(tmpdir) -> None: api.sort_file(tmp_file, atomic=True, write_to_stdout=True) -def test_sort_file(imperfect) -> None: +def test_sort_file(imperfect: Path) -> None: assert api.sort_file(imperfect) - assert imperfect.read() == fixed_content + assert imperfect.read_text() == fixed_content -def test_sort_file_in_place(imperfect) -> None: +def test_sort_file_in_place(imperfect: Path) -> None: assert api.sort_file(imperfect, overwrite_in_place=True) - assert imperfect.read() == fixed_content + assert imperfect.read_text() == fixed_content -def test_sort_file_to_stdout(capsys, imperfect) -> None: +def test_sort_file_to_stdout(capsys: pytest.CaptureFixture[str], imperfect: Path) -> None: assert api.sort_file(imperfect, write_to_stdout=True) out, _ = capsys.readouterr() assert out == fixed_content.replace("\n", os.linesep) -def test_other_ask_to_apply(imperfect) -> None: +def test_other_ask_to_apply(imperfect: Path) -> None: # First show diff, but ensure change won't get written by asking to apply # and ensuring answer is no. with patch("builtins.input", MagicMock(return_value="n")): assert not api.sort_file(imperfect, ask_to_apply=True) - assert imperfect.read() == imperfect_content + assert imperfect.read_text() == imperfect_content # Then run again, but apply the change (answer is yes) with patch("builtins.input", MagicMock(return_value="y")): assert api.sort_file(imperfect, ask_to_apply=True) - assert imperfect.read() == fixed_content + assert imperfect.read_text() == fixed_content -def test_check_file_no_changes(capsys, tmpdir) -> None: - perfect = tmpdir.join("test_no_changes.py") +def test_check_file_no_changes(capsys: pytest.CaptureFixture[str], tmp_path: Path) -> None: + perfect = tmp_path / "test_no_changes.py" perfect.write_text("import a\nimport b\n", "utf8") assert api.check_file(perfect, show_diff=True) out, _ = capsys.readouterr() assert not out -def test_check_file_with_changes(capsys, imperfect) -> None: +def test_check_file_with_changes(capsys: pytest.CaptureFixture[str], imperfect: Path) -> None: assert not api.check_file(imperfect, show_diff=True) out, _ = capsys.readouterr() assert fixed_diff.replace("\n", os.linesep) in out @@ -85,23 +86,23 @@ def test_diff_stream() -> None: assert fixed_diff in output.read() -def test_sort_code_string_mixed_newlines(): +def test_sort_code_string_mixed_newlines() -> None: assert api.sort_code_string("import A\n\r\nimportA\n\n") == "import A\r\n\r\nimportA\r\n\n" -def test_find_imports_in_file(imperfect): +def test_find_imports_in_file(imperfect: Path) -> None: found_imports = list(api.find_imports_in_file(imperfect)) assert "b" in [found_import.module for found_import in found_imports] -def test_find_imports_in_file_error(tmpdir): - test_path = tmpdir.join("test_path.py") +def test_find_imports_in_file_error(tmp_path: Path) -> None: + test_path = tmp_path / "test_path.py" test_path.mkdir() with pytest.warns(UserWarning): assert not list(api.find_imports_in_file(test_path)) -def test_find_imports_in_code(): +def test_find_imports_in_code() -> None: code = """ from x.y import z as a from x.y import z as a diff --git a/tests/unit/test_comments.py b/tests/unit/test_comments.py index 31ae8a6c4..0c98fce5f 100644 --- a/tests/unit/test_comments.py +++ b/tests/unit/test_comments.py @@ -4,7 +4,7 @@ import isort.comments -def test_add_to_line(): +def test_add_to_line() -> None: assert ( isort.comments.add_to_line([], "import os # comment", removed=True).strip() == "import os" ) @@ -20,7 +20,9 @@ def test_add_to_line(): removed=st.booleans(), comment_prefix=st.text(), ) -def test_fuzz_add_to_line(comments, original_string, removed, comment_prefix): +def test_fuzz_add_to_line( + comments: list[str] | None, original_string: str, removed: bool, comment_prefix: str +) -> None: isort.comments.add_to_line( comments=comments, original_string=original_string, @@ -30,5 +32,5 @@ def test_fuzz_add_to_line(comments, original_string, removed, comment_prefix): @given(line=st.text()) -def test_fuzz_parse(line): +def test_fuzz_parse(line: str) -> None: isort.comments.parse(line=line) diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py index 7878148e5..502e26c0a 100644 --- a/tests/unit/test_exceptions.py +++ b/tests/unit/test_exceptions.py @@ -4,125 +4,125 @@ class TestISortError: - def setup_class(self): + def setup_class(self) -> None: self.instance = exceptions.ISortError() - def test_init(self): + def test_init(self) -> None: assert isinstance(self.instance, exceptions.ISortError) - def test_pickleable(self): + def test_pickleable(self) -> None: assert isinstance(pickle.loads(pickle.dumps(self.instance)), exceptions.ISortError) class TestExistingSyntaxErrors(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.ExistingSyntaxErrors = exceptions.ExistingSyntaxErrors( "file_path" ) - def test_variables(self): + def test_variables(self) -> None: assert self.instance.file_path == "file_path" class TestIntroducedSyntaxErrors(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.IntroducedSyntaxErrors = exceptions.IntroducedSyntaxErrors( "file_path" ) - def test_variables(self): + def test_variables(self) -> None: assert self.instance.file_path == "file_path" class TestFileSkipped(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.FileSkipped = exceptions.FileSkipped("message", "file_path") - def test_variables(self): + def test_variables(self) -> None: assert self.instance.file_path == "file_path" assert str(self.instance) == "message" class TestFileSkipComment(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.FileSkipComment = exceptions.FileSkipComment("file_path") - def test_variables(self): + def test_variables(self) -> None: assert self.instance.file_path == "file_path" class TestFileSkipSetting(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.FileSkipSetting = exceptions.FileSkipSetting("file_path") - def test_variables(self): + def test_variables(self) -> None: assert self.instance.file_path == "file_path" class TestProfileDoesNotExist(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.ProfileDoesNotExist = exceptions.ProfileDoesNotExist("profile") - def test_variables(self): + def test_variables(self) -> None: assert self.instance.profile == "profile" class TestSortingFunctionDoesNotExist(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.SortingFunctionDoesNotExist = ( exceptions.SortingFunctionDoesNotExist("round", ["square", "peg"]) ) - def test_variables(self): + def test_variables(self) -> None: assert self.instance.sort_order == "round" assert self.instance.available_sort_orders == ["square", "peg"] class TestLiteralParsingFailure(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.LiteralParsingFailure = exceptions.LiteralParsingFailure( "x = [", SyntaxError ) - def test_variables(self): + def test_variables(self) -> None: assert self.instance.code == "x = [" assert self.instance.original_error is SyntaxError class TestLiteralSortTypeMismatch(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.LiteralSortTypeMismatch = exceptions.LiteralSortTypeMismatch( tuple, list ) - def test_variables(self): + def test_variables(self) -> None: assert self.instance.kind is tuple assert self.instance.expected_kind is list class TestAssignmentsFormatMismatch(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.AssignmentsFormatMismatch = exceptions.AssignmentsFormatMismatch( "print x" ) - def test_variables(self): + def test_variables(self) -> None: assert self.instance.code == "print x" class TestUnsupportedSettings(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.UnsupportedSettings = exceptions.UnsupportedSettings( {"apply": {"value": "true", "source": "/"}} ) - def test_variables(self): + def test_variables(self) -> None: assert self.instance.unsupported_settings == {"apply": {"value": "true", "source": "/"}} class TestUnsupportedEncoding(TestISortError): - def setup_class(self): + def setup_class(self) -> None: self.instance: exceptions.UnsupportedEncoding = exceptions.UnsupportedEncoding("file.py") - def test_variables(self): + def test_variables(self) -> None: assert self.instance.filename == "file.py" diff --git a/tests/unit/test_files.py b/tests/unit/test_files.py index 7ee6acf40..b120d0daf 100644 --- a/tests/unit/test_files.py +++ b/tests/unit/test_files.py @@ -1,8 +1,10 @@ +from pathlib import Path + from isort import files from isort.settings import DEFAULT_CONFIG -def test_find(tmpdir): - tmp_file = tmpdir.join("file.py") - tmp_file.write("import os, sys\n") +def test_find(tmp_path: Path) -> None: + tmp_file = tmp_path / "file.py" + tmp_file.write_text("import os, sys\n") assert tuple(files.find((tmp_file,), DEFAULT_CONFIG, [], [])) == (tmp_file,) diff --git a/tests/unit/test_format.py b/tests/unit/test_format.py index e441c4135..2d87aa7ec 100644 --- a/tests/unit/test_format.py +++ b/tests/unit/test_format.py @@ -10,7 +10,7 @@ import isort.format -def test_ask_whether_to_apply_changes_to_file(): +def test_ask_whether_to_apply_changes_to_file() -> None: with patch("builtins.input", MagicMock(return_value="y")): assert isort.format.ask_whether_to_apply_changes_to_file("") with patch("builtins.input", MagicMock(return_value="n")): @@ -20,7 +20,7 @@ def test_ask_whether_to_apply_changes_to_file(): assert isort.format.ask_whether_to_apply_changes_to_file("") -def test_ask_whether_to_apply_changes_to_file_eof_is_treated_as_quit(): +def test_ask_whether_to_apply_changes_to_file_eof_is_treated_as_quit() -> None: # Regression test for #1897: a closed/exhausted stdin (input() raising EOFError) # must exit cleanly like an explicit "quit" answer, not propagate as an # unhandled exception that main.py reports as an "unrecoverable exception". @@ -29,7 +29,7 @@ def test_ask_whether_to_apply_changes_to_file_eof_is_treated_as_quit(): isort.format.ask_whether_to_apply_changes_to_file("") -def test_basic_printer(capsys): +def test_basic_printer(capsys: pytest.CaptureFixture[str]) -> None: printer = isort.format.create_terminal_printer( color=False, success="{success}: {message}", error="{error}: {message}" ) @@ -51,7 +51,7 @@ def test_basic_printer(capsys): assert err == "error: Some error: ERROR\n" -def test_basic_printer_diff(capsys): +def test_basic_printer_diff(capsys: pytest.CaptureFixture[str]) -> None: printer = isort.format.create_terminal_printer(color=False) printer.diff_line("+ added line\n") printer.diff_line("- removed line\n") @@ -60,7 +60,7 @@ def test_basic_printer_diff(capsys): assert out == "+ added line\n- removed line\n" -def test_colored_printer_success(capsys): +def test_colored_printer_success(capsys: pytest.CaptureFixture[str]) -> None: printer = isort.format.create_terminal_printer(color=True, success="{success}: {message}") printer.success("All good!") out, _ = capsys.readouterr() @@ -69,7 +69,7 @@ def test_colored_printer_success(capsys): assert colorama.Fore.GREEN in out -def test_colored_printer_error(capsys): +def test_colored_printer_error(capsys: pytest.CaptureFixture[str]) -> None: printer = isort.format.create_terminal_printer(color=True, error="{error}: {message}") printer.error("Some error") _, err = capsys.readouterr() @@ -78,7 +78,7 @@ def test_colored_printer_error(capsys): assert colorama.Fore.RED in err -def test_colored_printer_diff(capsys): +def test_colored_printer_diff(capsys: pytest.CaptureFixture[str]) -> None: printer = isort.format.create_terminal_printer(color=True) printer.diff_line("+++ file1\n") printer.diff_line("--- file2\n") @@ -98,7 +98,7 @@ def test_colored_printer_diff(capsys): assert colorama.Style.RESET_ALL + "normal line" in out -def test_colored_printer_diff_output(capsys): +def test_colored_printer_diff_output(capsys: pytest.CaptureFixture[str]) -> None: output = StringIO() printer = isort.format.create_terminal_printer(color=True, output=output) printer.diff_line("a line\n") @@ -111,7 +111,7 @@ def test_colored_printer_diff_output(capsys): @patch("isort.format.colorama_unavailable", True) -def test_colorama_not_available_handled_gracefully(capsys): +def test_colorama_not_available_handled_gracefully(capsys: pytest.CaptureFixture[str]) -> None: with pytest.raises(SystemExit) as system_exit: _ = isort.format.create_terminal_printer(color=True) assert system_exit.value.code @@ -131,7 +131,9 @@ def test_colorama_not_available_handled_gracefully(capsys): file_path=st.one_of(st.none(), st.builds(Path)), output=st.one_of(st.none(), st.builds(StringIO, st.text())), ) -def test_fuzz_show_unified_diff(file_input, file_output, file_path, output): +def test_fuzz_show_unified_diff( + file_input: str, file_output: str, file_path: Path | None, output: StringIO | None +) -> None: try: isort.format.show_unified_diff( file_input=file_input, diff --git a/tests/unit/test_hooks.py b/tests/unit/test_hooks.py index 6f22f22a0..06e816df1 100644 --- a/tests/unit/test_hooks.py +++ b/tests/unit/test_hooks.py @@ -9,7 +9,7 @@ from isort._version import _IS_COMPILED -def test_git_hook(src_dir): +def test_git_hook(src_dir: str) -> None: """Simple smoke level testing of git hooks""" # Ensure correct subprocess command is called @@ -92,10 +92,10 @@ class FakeProcessResponse: hooks.git_hook(modify=True) -def test_git_hook_lazy(tmpdir): +def test_git_hook_lazy(tmp_path: Path) -> None: # Write an actual unsorted file to disk & check that `lazy=True` spots it - has_problems = tmpdir.join("test_has_problems.py") + has_problems = tmp_path / "test_has_problems.py" has_problems.write_text("import b\nimport a\n", "utf8") with patch( diff --git a/tests/unit/test_identify.py b/tests/unit/test_identify.py index 9e60261ba..0b58dda3b 100644 --- a/tests/unit/test_identify.py +++ b/tests/unit/test_identify.py @@ -1,14 +1,15 @@ from io import StringIO +from typing import Any from isort import Config, identify from isort.identify import Import -def imports_in_code(code: str, **kwargs) -> list[identify.Import]: +def imports_in_code(code: str, **kwargs: Any) -> list[identify.Import]: return list(identify.imports(StringIO(code), **kwargs)) -def test_top_only(): +def test_top_only() -> None: imports_in_function = """ import abc @@ -30,7 +31,7 @@ class MyObject: assert len(imports_in_code(imports_after_class, top_only=True)) == 1 -def test_top_doc_string(): +def test_top_doc_string() -> None: assert ( len( imports_in_code( @@ -47,7 +48,7 @@ def test_top_doc_string(): ) -def test_yield_and_raise_edge_cases(): +def test_yield_and_raise_edge_cases() -> None: assert not imports_in_code( """ raise SomeException("Blah") \\ @@ -200,7 +201,7 @@ def generator_function(): ) -def test_complex_examples(): +def test_complex_examples() -> None: assert ( len( imports_in_code( @@ -265,7 +266,7 @@ def test_complex_examples(): ] -def test_aliases(): +def test_aliases() -> None: assert imports_in_code("import os as os")[0].alias == "os" assert not imports_in_code( "import os as os", @@ -280,7 +281,7 @@ def test_aliases(): )[0].alias -def test_indented(): +def test_indented() -> None: assert not imports_in_code("import os")[0].indented assert imports_in_code(" import os")[0].indented assert imports_in_code("\timport os")[0].indented diff --git a/tests/unit/test_importable.py b/tests/unit/test_importable.py index 146f7b035..08df755b7 100644 --- a/tests/unit/test_importable.py +++ b/tests/unit/test_importable.py @@ -8,7 +8,7 @@ from pathlib import Path -def test_importable(): +def test_importable() -> None: """Simple smoketest to ensure all isort modules are importable""" import isort diff --git a/tests/unit/test_io.py b/tests/unit/test_io.py index c41ce4ecd..6762c6841 100644 --- a/tests/unit/test_io.py +++ b/tests/unit/test_io.py @@ -1,4 +1,5 @@ import sys +from pathlib import Path from unittest.mock import patch import pytest @@ -9,33 +10,33 @@ class TestFile: @pytest.mark.skipif(sys.platform == "win32", reason="Can't run file encoding test in AppVeyor") - def test_read(self, tmpdir): + def test_read(self, tmp_path: Path) -> None: test_file_content = """# -*- encoding: ascii -*- import Ὡ """ - test_file = tmpdir.join("file.py") - test_file.write(test_file_content) + test_file = tmp_path / "file.py" + test_file.write_text(test_file_content) with pytest.raises(UnicodeDecodeError): with io.File.read(str(test_file)) as file_handler: file_handler.stream.read() - def test_from_content(self, tmpdir): - test_file = tmpdir.join("file.py") + def test_from_content(self, tmp_path: Path) -> None: + test_file = tmp_path / "file.py" test_file.write_text("import os", "utf8") file_obj = io.File.from_contents("import os", filename=str(test_file)) assert file_obj assert file_obj.extension == "py" - def test_open(self, tmpdir): + def test_open(self, tmp_path: Path) -> None: with pytest.raises(FileNotFoundError): io.File._open("THISCANTBEAREALFILEὩὩὩὩὩὩὩὩὩὩὩὩ.ὩὩὩὩὩ") - def raise_arbitrary_exception(*args, **kwargs): + def raise_arbitrary_exception(*args: object, **kwargs: object) -> None: raise RuntimeError("test") - test_file = tmpdir.join("file.py") - test_file.write("import os") + test_file = tmp_path / "file.py" + test_file.write_text("import os") assert io.File._open(str(test_file)) # correctly responds to error determining encoding diff --git a/tests/unit/test_isort.py b/tests/unit/test_isort.py index 1e29fab15..f339175c3 100644 --- a/tests/unit/test_isort.py +++ b/tests/unit/test_isort.py @@ -58,17 +58,19 @@ @pytest.fixture(scope="session", autouse=True) -def default_settings_path(tmpdir_factory) -> Iterator[str]: - config_dir = tmpdir_factory.mktemp("config") - config_file = config_dir.join(".editorconfig").strpath +def default_settings_path(tmp_path_factory: pytest.TempPathFactory) -> Iterator[str]: + config_dir = tmp_path_factory.mktemp("config") + config_file = config_dir / ".editorconfig" + config_file.write_text(TEST_DEFAULT_CONFIG) - with open(config_file, "w") as editorconfig: - editorconfig.write(TEST_DEFAULT_CONFIG) + assert Config(str(config_file)).known_other - assert Config(config_file).known_other - - with config_dir.as_cwd(): - yield config_dir.strpath + original_cwd = os.getcwd() + os.chdir(config_dir) + try: + yield str(config_dir) + finally: + os.chdir(original_cwd) def test_happy_path() -> None: @@ -953,7 +955,7 @@ def test_remove_imports() -> None: assert test_output == "" -def test_comments_above(): +def test_comments_above() -> None: """Test to ensure comments above an import will stay in place""" test_input = "import os\n\nfrom x import y\n\n# comment\nfrom z import __version__, api\n" assert isort.code(test_input, ensure_newline_before_comments=True) == test_input @@ -988,7 +990,7 @@ def test_quotes_in_file() -> None: assert isort.code(test_input) == test_input -def test_check_newline_in_imports(capsys) -> None: +def test_check_newline_in_imports(capsys: pytest.CaptureFixture[str]) -> None: """Ensure tests works correctly when new lines are in imports.""" test_input = "from lib1 import (\n sub1,\n sub2,\n sub3\n)\n" @@ -1120,13 +1122,14 @@ def test_thirdy_party_overrides_standard_section() -> None: assert test_output == "import os\nimport sys\n\nimport profile.test\n" -def test_known_pattern_path_expansion(tmpdir) -> None: +def test_known_pattern_path_expansion(tmp_path: Path) -> None: """Test to ensure patterns ending with path sep gets expanded and nested packages treated as known patterns. """ - src_dir = tmpdir.mkdir("src") - src_dir.mkdir("foo") - src_dir.mkdir("bar") + src_dir = tmp_path / "src" + src_dir.mkdir() + (src_dir / "foo").mkdir() + (src_dir / "bar").mkdir() test_input = ( "from kate_plugin import isort_plugin\n" "import sys\n" @@ -1139,7 +1142,7 @@ def test_known_pattern_path_expansion(tmpdir) -> None: code=test_input, default_section="THIRDPARTY", known_first_party=["src/", "this", "kate_plugin"], - directory=str(tmpdir), + directory=str(tmp_path), ) assert test_output == ( "import os\n" @@ -2496,53 +2499,49 @@ def test_import_split_is_word_boundary_aware() -> None: ) -def test_other_file_encodings(tmpdir) -> None: +def test_other_file_encodings(tmp_path: Path) -> None: """Test to ensure file encoding is respected""" for encoding in ("latin1", "utf8"): - tmp_fname = tmpdir.join(f"test_{encoding}.py") + tmp_fname = tmp_path / f"test_{encoding}.py" file_contents = f"# coding: {encoding}\n\ns = u'ã'\n" - tmp_fname.write_binary(file_contents.encode(encoding)) - api.sort_file(Path(tmp_fname), file_path=Path(tmp_fname), settings_path=os.getcwd()) + tmp_fname.write_bytes(file_contents.encode(encoding)) + api.sort_file(tmp_fname, file_path=tmp_fname, settings_path=os.getcwd()) assert tmp_fname.read_text(encoding) == file_contents -def test_other_file_encodings_in_place(tmpdir) -> None: +def test_other_file_encodings_in_place(tmp_path: Path) -> None: """Test to ensure file encoding is respected when overwritten in place.""" for encoding in ("latin1", "utf8"): - tmp_fname = tmpdir.join(f"test_{encoding}.py") + tmp_fname = tmp_path / f"test_{encoding}.py" file_contents = f"# coding: {encoding}\n\ns = u'ã'\n" - tmp_fname.write_binary(file_contents.encode(encoding)) + tmp_fname.write_bytes(file_contents.encode(encoding)) api.sort_file( - Path(tmp_fname), - file_path=Path(tmp_fname), + tmp_fname, + file_path=tmp_fname, settings_path=os.getcwd(), overwrite_in_place=True, ) assert tmp_fname.read_text(encoding) == file_contents -def test_encoding_not_in_comment(tmpdir) -> None: +def test_encoding_not_in_comment(tmp_path: Path) -> None: """Test that 'encoding' not in a comment is ignored""" - tmp_fname = tmpdir.join("test_encoding.py") + tmp_fname = tmp_path / "test_encoding.py" file_contents = "class Foo\n coding: latin1\n\ns = u'ã'\n" - tmp_fname.write_binary(file_contents.encode("utf8")) + tmp_fname.write_bytes(file_contents.encode("utf8")) assert ( - isort.code( - Path(tmp_fname).read_text("utf8"), file_path=Path(tmp_fname), settings_path=os.getcwd() - ) + isort.code(tmp_fname.read_text("utf8"), file_path=tmp_fname, settings_path=os.getcwd()) == file_contents ) -def test_encoding_not_in_first_two_lines(tmpdir) -> None: +def test_encoding_not_in_first_two_lines(tmp_path: Path) -> None: """Test that 'encoding' not in the first two lines is ignored""" - tmp_fname = tmpdir.join("test_encoding.py") + tmp_fname = tmp_path / "test_encoding.py" file_contents = "\n\n# -*- coding: latin1\n\ns = u'ã'\n" - tmp_fname.write_binary(file_contents.encode("utf8")) + tmp_fname.write_bytes(file_contents.encode("utf8")) assert ( - isort.code( - Path(tmp_fname).read_text("utf8"), file_path=Path(tmp_fname), settings_path=os.getcwd() - ) + isort.code(tmp_fname.read_text("utf8"), file_path=tmp_fname, settings_path=os.getcwd()) == file_contents ) @@ -2640,7 +2639,7 @@ def test_shouldnt_add_lines() -> None: assert isort.code(test_input) == test_input -def test_sections_parsed_correct(tmpdir) -> None: +def test_sections_parsed_correct(tmp_path: Path) -> None: """Ensure that modules for custom sections parsed as list from config file and isort result is correct """ @@ -2661,11 +2660,11 @@ def test_sections_parsed_correct(tmpdir) -> None: "import nose\n" "from nose import *\n" ) - tmpdir.join(".isort.cfg").write(conf_file_data) - assert isort.code(test_input, settings_path=str(tmpdir)) == correct_output + (tmp_path / ".isort.cfg").write_text(conf_file_data) + assert isort.code(test_input, settings_path=str(tmp_path)) == correct_output -def test_pyproject_conf_file(tmpdir) -> None: +def test_pyproject_conf_file(tmp_path: Path) -> None: """Ensure that modules for custom sections parsed as list from config file and isort result is correct """ @@ -2699,8 +2698,8 @@ def test_pyproject_conf_file(tmpdir) -> None: "\n" "from nose import *\n" ) - tmpdir.join("pyproject.toml").write(conf_file_data) - assert isort.code(test_input, settings_path=str(tmpdir)) == correct_output + (tmp_path / "pyproject.toml").write_text(conf_file_data) + assert isort.code(test_input, settings_path=str(tmp_path)) == correct_output def test_alphabetic_sorting_no_newlines() -> None: @@ -3005,29 +3004,28 @@ def test_third_party_case_sensitive() -> None: assert isort.code(test_input) == expected_output -def test_exists_case_sensitive_file(tmpdir) -> None: +def test_exists_case_sensitive_file(tmp_path: Path) -> None: """Test exists_case_sensitive function for a file.""" exists_case_sensitive.cache_clear() - tmpdir.join("module.py").ensure(file=1) - assert exists_case_sensitive(str(tmpdir.join("module.py"))) - assert not exists_case_sensitive(str(tmpdir.join("MODULE.py"))) + (tmp_path / "module.py").touch() + assert exists_case_sensitive(str(tmp_path / "module.py")) + assert not exists_case_sensitive(str(tmp_path / "MODULE.py")) -def test_exists_case_sensitive_directory(tmpdir) -> None: +def test_exists_case_sensitive_directory(tmp_path: Path) -> None: """Test exists_case_sensitive function for a directory.""" exists_case_sensitive.cache_clear() - tmpdir.join("pkg").ensure(dir=1) - assert exists_case_sensitive(str(tmpdir.join("pkg"))) - assert not exists_case_sensitive(str(tmpdir.join("PKG"))) + (tmp_path / "pkg").mkdir() + assert exists_case_sensitive(str(tmp_path / "pkg")) + assert not exists_case_sensitive(str(tmp_path / "PKG")) -def test_sys_path_mutation(tmpdir) -> None: +def test_sys_path_mutation(tmp_path: Path) -> None: """Test to ensure sys.path is not modified""" - tmpdir.mkdir("src").mkdir("a") + (tmp_path / "src" / "a").mkdir(parents=True) test_input = "from myproject import test" - options = {"virtual_env": str(tmpdir)} # type: dict[str, Any] expected_length = len(sys.path) - isort.code(test_input, **options) + isort.code(test_input, virtual_env=str(tmp_path)) assert len(sys.path) == expected_length @@ -3174,7 +3172,7 @@ def test_long_alias_using_paren_issue_957() -> None: assert out == expected_output -def test_strict_whitespace_by_default(capsys) -> None: +def test_strict_whitespace_by_default(capsys: pytest.CaptureFixture[str]) -> None: test_input = "import os\nfrom django.conf import settings\n" assert not api.check_code_string(test_input) _, err = capsys.readouterr() @@ -3182,14 +3180,16 @@ def test_strict_whitespace_by_default(capsys) -> None: assert err.endswith("Imports are incorrectly sorted and/or formatted.\n") -def test_strict_whitespace_no_closing_newline_issue_676(capsys) -> None: +def test_strict_whitespace_no_closing_newline_issue_676( + capsys: pytest.CaptureFixture[str], +) -> None: test_input = "import os\n\nfrom django.conf import settings\n\nprint(1)" assert api.check_code_string(test_input) out, _ = capsys.readouterr() assert out == "" -def test_ignore_whitespace(capsys) -> None: +def test_ignore_whitespace(capsys: pytest.CaptureFixture[str]) -> None: test_input = "import os\nfrom django.conf import settings\n" assert api.check_code_string(test_input, ignore_whitespace=True) out, _ = capsys.readouterr() @@ -3678,9 +3678,9 @@ def test_new_lines_are_preserved() -> None: os.remove(n_newline.name) -def test_forced_separate_is_deterministic_issue_774(tmpdir) -> None: - config_file = tmpdir.join("setup.cfg") - config_file.write( +def test_forced_separate_is_deterministic_issue_774(tmp_path: Path) -> None: + config_file = tmp_path / "setup.cfg" + config_file.write_text( "[isort]\nforced_separate:\n separate1\n separate2\n separate3\n separate4\n" ) @@ -3696,7 +3696,7 @@ def test_forced_separate_is_deterministic_issue_774(tmpdir) -> None: "from separate4 import quux\n" ) - assert isort.code(test_input, settings_file=config_file.strpath) == test_input + assert isort.code(test_input, settings_file=str(config_file)) == test_input def test_monkey_patched_urllib() -> None: @@ -3716,37 +3716,41 @@ def test_argument_parsing() -> None: @pytest.mark.parametrize("multiprocess", [False, True]) -def test_command_line(tmpdir, capfd, multiprocess: bool) -> None: - tmpdir.join("file1.py").write("import re\nimport os\n\nimport contextlib\n\n\nimport isort") - tmpdir.join("file2.py").write("import collections\nimport time\n\nimport abc\n\n\nimport isort") - arguments = [str(tmpdir), "--settings-path", os.getcwd()] +def test_command_line( + tmp_path: Path, capfd: pytest.CaptureFixture[str], multiprocess: bool +) -> None: + (tmp_path / "file1.py").write_text( + "import re\nimport os\n\nimport contextlib\n\n\nimport isort" + ) + (tmp_path / "file2.py").write_text( + "import collections\nimport time\n\nimport abc\n\n\nimport isort" + ) + arguments = [str(tmp_path), "--settings-path", os.getcwd()] if multiprocess: arguments.extend(["--jobs", "2"]) main(arguments) assert ( - tmpdir.join("file1.py").read() - == "import contextlib\nimport os\nimport re\n\nimport isort\n" - ) + tmp_path / "file1.py" + ).read_text() == "import contextlib\nimport os\nimport re\n\nimport isort\n" assert ( - tmpdir.join("file2.py").read() - == "import abc\nimport collections\nimport time\n\nimport isort\n" - ) + tmp_path / "file2.py" + ).read_text() == "import abc\nimport collections\nimport time\n\nimport isort\n" if not (sys.platform.startswith("win") or sys.platform.startswith("darwin")): out, err = capfd.readouterr() assert not [error for error in err.split("\n") if error and "warning:" not in error] # it informs us about fixing the files: - assert str(tmpdir.join("file1.py")) in out - assert str(tmpdir.join("file2.py")) in out + assert str(tmp_path / "file1.py") in out + assert str(tmp_path / "file2.py") in out @pytest.mark.parametrize("quiet", [False, True]) -def test_quiet(tmpdir, capfd, quiet: bool) -> None: +def test_quiet(tmp_path: Path, capfd: pytest.CaptureFixture[str], quiet: bool) -> None: if sys.platform.startswith("win"): return - tmpdir.join("file1.py").write("import re\nimport os") - tmpdir.join("file2.py").write("") - arguments = [str(tmpdir)] + (tmp_path / "file1.py").write_text("import re\nimport os") + (tmp_path / "file2.py").write_text("") + arguments = [str(tmp_path)] if quiet: arguments.append("-q") main(arguments) @@ -3756,24 +3760,30 @@ def test_quiet(tmpdir, capfd, quiet: bool) -> None: @pytest.mark.parametrize("enabled", [False, True]) -def test_safety_skips(tmpdir, enabled: bool) -> None: - tmpdir.join("victim.py").write("# ...") - toxdir = tmpdir.mkdir(".tox") - toxdir.join("verysafe.py").write("# ...") - tmpdir.mkdir("_build").mkdir("python3.7").join("importantsystemlibrary.py").write("# ...") - tmpdir.mkdir(".pants.d").join("pants.py").write("import os") +def test_safety_skips(tmp_path: Path, enabled: bool) -> None: + (tmp_path / "victim.py").write_text("# ...") + toxdir = tmp_path / ".tox" + toxdir.mkdir() + (toxdir / "verysafe.py").write_text("# ...") + build_dir = tmp_path / "_build" / "python3.7" + build_dir.mkdir(parents=True) + (build_dir / "importantsystemlibrary.py").write_text("# ...") + pants_dir = tmp_path / ".pants.d" + pants_dir.mkdir() + (pants_dir / "pants.py").write_text("import os") if enabled: - config = Config(directory=str(tmpdir)) + config = Config(directory=str(tmp_path)) else: - config = Config(skip=[], directory=str(tmpdir)) + config = Config(skip=[], directory=str(tmp_path)) skipped: list[str] = [] broken: list[str] = [] - codes = [str(tmpdir)] + codes = [str(tmp_path)] files.find(codes, config, skipped, broken) # if enabled files within nested unsafe directories should be skipped file_names = { - os.path.relpath(f, str(tmpdir)) for f in files.find([str(tmpdir)], config, skipped, broken) + os.path.relpath(f, str(tmp_path)) + for f in files.find([str(tmp_path)], config, skipped, broken) } if enabled: assert file_names == {"victim.py"} @@ -3803,11 +3813,12 @@ def test_safety_skips(tmpdir, enabled: bool) -> None: (["*/code/*.py"], 1, set()), ], ) -def test_skip_glob(tmpdir, skip_glob_assert: tuple[list[str], int, set[str]]) -> None: +def test_skip_glob(tmp_path: Path, skip_glob_assert: tuple[list[str], int, set[str]]) -> None: skip_glob, skipped_count, file_names_expected = skip_glob_assert - base_dir = tmpdir.mkdir("build") - code_dir = base_dir.mkdir("code") - code_dir.join("file.py").write("import os") + base_dir = tmp_path / "build" + code_dir = base_dir / "code" + code_dir.mkdir(parents=True) + (code_dir / "file.py").write_text("import os") config = Config(skip_glob=skip_glob, directory=str(base_dir)) skipped: list[str] = [] @@ -3820,8 +3831,9 @@ def test_skip_glob(tmpdir, skip_glob_assert: tuple[list[str], int, set[str]]) -> assert file_names == file_names_expected -def test_broken(tmpdir) -> None: - base_dir = tmpdir.mkdir("broken") +def test_broken(tmp_path: Path) -> None: + base_dir = tmp_path / "broken" + base_dir.mkdir() config = Config(directory=str(base_dir)) skipped: list[str] = [] @@ -4238,17 +4250,19 @@ def test_standard_library_deprecates_user_issue_778() -> None: @pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") -def test_settings_path_skip_issue_909(tmpdir) -> None: - base_dir = tmpdir.mkdir("project") - config_dir = base_dir.mkdir("conf") - config_dir.join(".isort.cfg").write( +def test_settings_path_skip_issue_909(tmp_path: Path) -> None: + base_dir = tmp_path / "project" + base_dir.mkdir() + config_dir = base_dir / "conf" + config_dir.mkdir() + (config_dir / ".isort.cfg").write_text( "[isort]\nskip =\n file_to_be_skipped.py\nskip_glob =\n *glob_skip*\n" ) - base_dir.join("file_glob_skip.py").write( + (base_dir / "file_glob_skip.py").write_text( 'import os\n\nprint("Hello World")\n\nimport sys\nimport os\n' ) - base_dir.join("file_to_be_skipped.py").write( + (base_dir / "file_to_be_skipped.py").write_text( 'import os\n\nprint("Hello World")\nimport sys\nimport os\n' ) @@ -4268,10 +4282,12 @@ def test_settings_path_skip_issue_909(tmpdir) -> None: @pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows") -def test_skip_paths_issue_938(tmpdir) -> None: - base_dir = tmpdir.mkdir("project") - config_dir = base_dir.mkdir("conf") - config_dir.join(".isort.cfg").write( +def test_skip_paths_issue_938(tmp_path: Path) -> None: + base_dir = tmp_path / "project" + base_dir.mkdir() + config_dir = base_dir / "conf" + config_dir.mkdir() + (config_dir / ".isort.cfg").write_text( "[isort]\n" "line_length = 88\n" "multi_line_output = 4\n" @@ -4279,10 +4295,11 @@ def test_skip_paths_issue_938(tmpdir) -> None: "skip_glob =\n" " migrations/**.py\n" ) - base_dir.join("dont_skip.py").write('import os\n\nprint("Hello World")\nimport sys\n') + (base_dir / "dont_skip.py").write_text('import os\n\nprint("Hello World")\nimport sys\n') - migrations_dir = base_dir.mkdir("migrations") - migrations_dir.join("file_glob_skip.py").write( + migrations_dir = base_dir / "migrations" + migrations_dir.mkdir() + (migrations_dir / "file_glob_skip.py").write_text( 'import os\n\nprint("Hello World")\n\nimport sys\n' ) @@ -4461,22 +4478,22 @@ def test_isort_ensures_blank_line_between_import_and_comment() -> None: assert isort.code(test_input, **config) == expected_output -def test_pyi_formatting_issue_942(tmpdir) -> None: +def test_pyi_formatting_issue_942(tmp_path: Path) -> None: test_input = "import os\n\n\ndef my_method():\n" expected_py_output = test_input.splitlines() expected_pyi_output = "import os\n\ndef my_method():\n".splitlines() assert isort.code(test_input).splitlines() == expected_py_output assert isort.code(test_input, extension="pyi").splitlines() == expected_pyi_output - source_py = tmpdir.join("source.py") - source_py.write(test_input) + source_py = tmp_path / "source.py" + source_py.write_text(test_input) assert ( isort.code(code=Path(source_py).read_text(), file_path=Path(source_py)).splitlines() == expected_py_output ) - source_pyi = tmpdir.join("source.pyi") - source_pyi.write(test_input) + source_pyi = tmp_path / "source.pyi" + source_pyi.write_text(test_input) assert ( isort.code( code=Path(source_pyi).read_text(), extension="pyi", file_path=Path(source_pyi) @@ -4485,11 +4502,11 @@ def test_pyi_formatting_issue_942(tmpdir) -> None: ) # Ensure it works for direct file API as well (see: issue #1284) - source_pyi = tmpdir.join("source.pyi") - source_pyi.write(test_input) + source_pyi = tmp_path / "source.pyi" + source_pyi.write_text(test_input) api.sort_file(Path(source_pyi)) - assert source_pyi.read().splitlines() == expected_pyi_output + assert source_pyi.read_text().splitlines() == expected_pyi_output def test_move_class_issue_751() -> None: @@ -4611,7 +4628,7 @@ def test_isort_split() -> None: assert isort.code(test_input) == test_input -def test_comment_look_alike(): +def test_comment_look_alike() -> None: """Test to ensure isort will handle what looks like a single line comment at the end of a multi-line comment. """ @@ -4636,7 +4653,7 @@ def test_comment_look_alike(): ) -def test_cimport_support(): +def test_cimport_support() -> None: """Test to ensure cimports (Cython style imports) work""" test_input = """ import os @@ -4994,7 +5011,7 @@ def test_cimport_support(): assert isort.code(test_input).strip() == expected_output.strip() -def test_cdef_support(): +def test_cdef_support() -> None: assert ( isort.code( code=""" @@ -5087,7 +5104,7 @@ def test_noqa_issue_1065() -> None: assert isort.code(test_input_2, line_length=100) == expected_output -def test_single_line_exclusions(): +def test_single_line_exclusions() -> None: test_input = """ # start comment from os import path, system @@ -5105,7 +5122,7 @@ def test_single_line_exclusions(): ) -def test_nested_comment_handling(): +def test_nested_comment_handling() -> None: test_input = """ if True: import foo @@ -5154,7 +5171,7 @@ def f(): assert isort.code(test_input) == test_input -def test_comments_top_of_file(): +def test_comments_top_of_file() -> None: """Test to ensure comments at top of file are correctly handled. See issue #1091.""" test_input = """# comment 1 @@ -5189,7 +5206,7 @@ def _process_item(self, item, spider): assert isort.code(test_input) == test_input -def test_multiple_aliases(): +def test_multiple_aliases() -> None: """Test to ensure isort will retain multiple aliases. See issue #1037""" test_input = """import datetime import datetime as datetime @@ -5199,7 +5216,7 @@ def test_multiple_aliases(): assert isort.code(code=test_input) == test_input -def test_parens_in_comment(): +def test_parens_in_comment() -> None: """Test to ensure isort can handle parens placed in comments. See issue #1103""" test_input = """from foo import ( # (some text in brackets) bar, @@ -5209,7 +5226,7 @@ def test_parens_in_comment(): assert isort.code(test_input) == expected_output -def test_as_imports_mixed(): +def test_as_imports_mixed() -> None: """Test to ensure as imports can be mixed with non as. See issue #908""" test_input = """from datetime import datetime import datetime.datetime as dt @@ -5220,7 +5237,7 @@ def test_as_imports_mixed(): assert isort.code(test_input) == expected_output -def test_no_sections_with_future(): +def test_no_sections_with_future() -> None: """Test to ensure no_sections works with future. See issue #807""" test_input = """from __future__ import print_function import os @@ -5232,7 +5249,7 @@ def test_no_sections_with_future(): assert isort.code(test_input, no_sections=True) == expected_output -def test_no_sections_with_as_import(): +def test_no_sections_with_as_import() -> None: """Test to ensure no_sections work with as import.""" test_input = """import oumpy as np import sympy @@ -5240,7 +5257,7 @@ def test_no_sections_with_as_import(): assert isort.code(test_input, no_sections=True) == test_input -def test_no_lines_too_long(): +def test_no_lines_too_long() -> None: """Test to ensure no lines end up too long. See issue: #1015""" test_input = """from package1 import first_package, \ second_package @@ -5256,7 +5273,7 @@ def test_no_lines_too_long(): assert isort.code(test_input, line_length=25, multi_line_output=2) == expected_output -def test_python_future_category(): +def test_python_future_category() -> None: """Test to ensure a manual python future category will work as needed to install aliases see: Issue #1005 @@ -5329,7 +5346,7 @@ def test_python_future_category(): ) -def test_combine_star_comments_above(): +def test_combine_star_comments_above() -> None: input_text = """from __future__ import absolute_import # my future comment @@ -5451,7 +5468,7 @@ class NonSeekableTestStream(StringIO): def seek(self, offset: int, whence: int = os.SEEK_SET, /) -> int: raise OSError("Stream is not seekable") - def seekable(self): + def seekable(self) -> bool: return False test_input = NonSeekableTestStream("import m2\nimport m1\nnot_import = 7") diff --git a/tests/unit/test_lazy_imports.py b/tests/unit/test_lazy_imports.py index fc7e7c421..e6491714e 100644 --- a/tests/unit/test_lazy_imports.py +++ b/tests/unit/test_lazy_imports.py @@ -6,22 +6,22 @@ class TestParsing: """Verify that ``parse.file_contents`` correctly identifies lazy imports.""" - def test_lazy_straight_import_is_stored_in_lazy_straight_bucket(self): + def test_lazy_straight_import_is_stored_in_lazy_straight_bucket(self) -> None: result = parse.file_contents("lazy import ast\n", Config()) assert "ast" in result.imports["STDLIB"]["lazy_straight"] assert "ast" not in result.imports["STDLIB"]["straight"] - def test_lazy_from_import_is_stored_in_lazy_from_bucket(self): + def test_lazy_from_import_is_stored_in_lazy_from_bucket(self) -> None: result = parse.file_contents("lazy from dataclasses import dataclass\n", Config()) assert "dataclasses" in result.imports["STDLIB"]["lazy_from"] assert "dataclasses" not in result.imports["STDLIB"]["from"] - def test_eager_imports_are_stored_in_regular_buckets(self): + def test_eager_imports_are_stored_in_regular_buckets(self) -> None: result = parse.file_contents("import os\nfrom pathlib import Path\n", Config()) assert "os" in result.imports["STDLIB"]["straight"] assert "pathlib" in result.imports["STDLIB"]["from"] - def test_lazy_imports_are_placed_in_correct_section(self): + def test_lazy_imports_are_placed_in_correct_section(self) -> None: """Lazy imports must be placed in the same section as their eager counterparts.""" result = parse.file_contents( "lazy import ast\nlazy import requests\n", @@ -31,12 +31,12 @@ def test_lazy_imports_are_placed_in_correct_section(self): assert "requests" in result.imports["THIRDPARTY"]["lazy_straight"] -def test_lazy_straight_imports_come_after_eager(): +def test_lazy_straight_imports_come_after_eager() -> None: """lazy import lines follow all eager import lines within the section.""" isort_test("lazy import ast\nimport os\n", "import os\nlazy import ast\n") -def test_lazy_from_imports_come_after_eager(): +def test_lazy_from_imports_come_after_eager() -> None: """lazy from ... import lines follow all eager import lines within the section.""" isort_test( "lazy from pathlib import Path\nfrom collections import defaultdict\n", @@ -44,12 +44,12 @@ def test_lazy_from_imports_come_after_eager(): ) -def test_lazy_straight_sorted_alphabetically(): +def test_lazy_straight_sorted_alphabetically() -> None: """Multiple lazy straight imports are sorted alphabetically.""" isort_test("lazy import shutil\nlazy import ast\n", "lazy import ast\nlazy import shutil\n") -def test_lazy_from_sorted_alphabetically(): +def test_lazy_from_sorted_alphabetically() -> None: """Multiple lazy from imports are sorted alphabetically by module name.""" isort_test( "lazy from pathlib import Path\nlazy from dataclasses import dataclass\n", @@ -57,7 +57,7 @@ def test_lazy_from_sorted_alphabetically(): ) -def test_ruff_reference_example(): +def test_ruff_reference_example() -> None: """Reproduce the canonical example from the ruff issue tracker. See https://github.com/astral-sh/ruff/issues/21305. @@ -87,14 +87,14 @@ def test_ruff_reference_example(): isort_test(unsorted, expected) -def test_lazy_imports_appear_after_eager_in_each_section_independently(): +def test_lazy_imports_appear_after_eager_in_each_section_independently() -> None: """Each section gets its own eager-first / lazy-last grouping.""" unsorted = "lazy import requests\nlazy import ast\nimport os\nimport requests\n" expected = "import os\nlazy import ast\n\nimport requests\nlazy import requests\n" isort_test(unsorted, expected, known_third_party=["requests"]) -def test_lazy_import_with_alias(): +def test_lazy_import_with_alias() -> None: """``lazy import X as Y`` is supported and sorted correctly.""" isort_test( "import os\nlazy import numpy as np\n", @@ -103,12 +103,12 @@ def test_lazy_import_with_alias(): ) -def test_lazy_from_import_multiple_names(): +def test_lazy_from_import_multiple_names() -> None: """``lazy from X import a, b`` is supported and names are sorted alphabetically.""" isort_test("lazy from typing import List, Dict\n", "lazy from typing import Dict, List\n") -def test_no_sections_mode_with_lazy_imports(): +def test_no_sections_mode_with_lazy_imports() -> None: """lazy imports are supported in no_sections mode.""" isort_test( "lazy import ast\nimport os\n", @@ -117,7 +117,7 @@ def test_no_sections_mode_with_lazy_imports(): ) -def test_from_first_option_respected_for_lazy(): +def test_from_first_option_respected_for_lazy() -> None: """When from_first=True, lazy from imports precede lazy straight but appear after eager.""" isort_test( "import pathlib\nlazy import ast\nlazy from dataclasses import dataclass\n", @@ -126,7 +126,7 @@ def test_from_first_option_respected_for_lazy(): ) -def test_force_sort_within_sections_applies_to_lazy(): +def test_force_sort_within_sections_applies_to_lazy() -> None: """force_sort_within_sections toggles lazy import ordering behavior.""" isort_test( "lazy import zlib\nlazy from ast import parse\n", diff --git a/tests/unit/test_literal.py b/tests/unit/test_literal.py index e31d9b772..1fe273c59 100644 --- a/tests/unit/test_literal.py +++ b/tests/unit/test_literal.py @@ -6,54 +6,54 @@ from isort.settings import Config -def test_value_mismatch(): +def test_value_mismatch() -> None: with pytest.raises(exceptions.LiteralSortTypeMismatch): isort.literal.assignment("x = [1, 2, 3]", "set", "py") -def test_invalid_syntax(): +def test_invalid_syntax() -> None: with pytest.raises(exceptions.LiteralParsingFailure): isort.literal.assignment("x = [1, 2, 3", "list", "py") -def test_invalid_sort_type(): +def test_invalid_sort_type() -> None: with pytest.raises(ValueError, match=r"Trying to sort using an undefined sort_type. Defined"): isort.literal.assignment("x = [1, 2, 3", "tuple-list-not-exist", "py") -def test_value_assignment_assignments(): +def test_value_assignment_assignments() -> None: assert isort.literal.assignment("b = 1\na = 2\n", "assignments", "py") == "a = 2\nb = 1\n" -def test_assignments_invalid_section(): +def test_assignments_invalid_section() -> None: with pytest.raises(exceptions.AssignmentsFormatMismatch): isort.literal.assignment("\n\nx = 1\nx++", "assignments", "py") -def test_list_uses_double_quotes(): +def test_list_uses_double_quotes() -> None: assert isort.literal.assignment("x = ['b', 'a']", "list", "py") == 'x = ["a", "b"]' -def test_list_preserves_bracket_type_tuple(): +def test_list_preserves_bracket_type_tuple() -> None: assert isort.literal.assignment("x = ('b', 'a')", "tuple", "py") == 'x = ("a", "b")' -def test_single_element_tuple_keeps_trailing_comma(): +def test_single_element_tuple_keeps_trailing_comma() -> None: assert isort.literal.assignment("x = ('a',)", "tuple", "py") == 'x = ("a",)' -def test_set_bracket_and_quotes(): +def test_set_bracket_and_quotes() -> None: assert isort.literal.assignment("x = {'b', 'a'}", "set", "py") == 'x = {"a", "b"}' -def test_long_list_wraps_vertical_hanging_indent(): +def test_long_list_wraps_vertical_hanging_indent() -> None: code = "__all__ = ['" + "', '".join(f"name_{i:02d}" for i in range(12)) + "']" result = isort.literal.assignment(code, "list", "py", config=Config(profile="black")) expected = "__all__ = [\n" + "".join(f' "name_{i:02d}",\n' for i in range(12)) + "]" assert result == expected -def test_wrap_without_trailing_comma(): +def test_wrap_without_trailing_comma() -> None: code = "__all__ = ['" + "', '".join(f"name_{i:02d}" for i in range(12)) + "']" result = isort.literal.assignment( code, "list", "py", config=Config(line_length=20, include_trailing_comma=False) @@ -77,42 +77,42 @@ def test_trailing_comma_detection_requires_matching_brackets() -> None: assert not isort.literal._has_trailing_comma("(\n 'a',\n]") -def test_quote_fallback_for_embedded_quote(): +def test_quote_fallback_for_embedded_quote() -> None: # value containing a double quote but no single quote -> single quotes (black rule) assert isort.literal.assignment("x = ['a\"b']", "list", "py") == "x = ['a\"b']" -def test_black_quote_prefers_double_quotes(): +def test_black_quote_prefers_double_quotes() -> None: assert isort.literal._black_quote("foo") == '"foo"' -def test_black_quote_uses_single_when_value_has_double_quote_only(): +def test_black_quote_uses_single_when_value_has_double_quote_only() -> None: assert isort.literal._black_quote('a"b') == "'a\"b'" -def test_black_quote_escapes_double_when_value_has_both_quotes(): +def test_black_quote_escapes_double_when_value_has_both_quotes() -> None: assert isort.literal._black_quote("a'b\"c") == '"a\'b\\"c"' -def test_black_quote_falls_back_to_repr_for_control_chars(): +def test_black_quote_falls_back_to_repr_for_control_chars() -> None: assert isort.literal._black_quote("a\tb") == repr("a\tb") -def test_list_of_non_strings_uses_repr(): +def test_list_of_non_strings_uses_repr() -> None: assert isort.literal.assignment("x = [3, 1, 2]", "list", "py") == "x = [1, 2, 3]" -def test_unique_tuple_dedupes_and_sorts(): +def test_unique_tuple_dedupes_and_sorts() -> None: assert isort.literal.assignment("x = ('b', 'a', 'a')", "unique-tuple", "py") == 'x = ("a", "b")' -def test_single_element_tuple_wraps_and_keeps_comma(): +def test_single_element_tuple_wraps_and_keeps_comma() -> None: long_name = "z" * 100 result = isort.literal.assignment(f"x = ('{long_name}',)", "tuple", "py") assert result == f'x = (\n "{long_name}",\n)' -def test_dict_sorts_by_value_with_double_quotes(): +def test_dict_sorts_by_value_with_double_quotes() -> None: # dict is now formatted config-aware too (no more pprint); sorted by value assert ( isort.literal.assignment("x = {'a': 'z', 'b': 'y'}", "dict", "py") @@ -120,11 +120,11 @@ def test_dict_sorts_by_value_with_double_quotes(): ) -def test_dict_non_string_values_use_repr(): +def test_dict_non_string_values_use_repr() -> None: assert isort.literal.assignment("x = {'b': 2, 'a': 1}", "dict", "py") == 'x = {"a": 1, "b": 2}' -def test_long_dict_wraps_vertical_hanging_indent(): +def test_long_dict_wraps_vertical_hanging_indent() -> None: pairs = {f"key_{i:02d}": f"value_{i:02d}" for i in range(10)} code = "d = {" + ", ".join(f"'{k}': '{v}'" for k, v in pairs.items()) + "}" result = isort.literal.assignment(code, "dict", "py", config=Config(profile="black")) @@ -134,46 +134,46 @@ def test_long_dict_wraps_vertical_hanging_indent(): assert result == expected -def test_value_assignment_dict(): +def test_value_assignment_dict() -> None: assert ( isort.literal.assignment("x = {3: 'c', 1: 'a', 2: 'b'}", "dict", "py") == 'x = {1: "a", 2: "b", 3: "c"}' ) -def test_value_assignment_unique_tuple(): +def test_value_assignment_unique_tuple() -> None: assert ( isort.literal.assignment("x = ('a', 'b', '1', '1')", "unique-tuple", "py") == 'x = ("1", "a", "b")' ) -def test_trailing_comment_is_preserved(): +def test_trailing_comment_is_preserved() -> None: assert ( isort.literal.assignment("x = ['b', 'a'] # exports", "list", "py") == 'x = ["a", "b"] # exports' ) -def test_trailing_comment_with_equals_is_preserved(): +def test_trailing_comment_with_equals_is_preserved() -> None: assert ( isort.literal.assignment("x = ['b', 'a'] # note = value", "list", "py") == 'x = ["a", "b"] # note = value' ) -def test_literal_with_equals_in_string_sorts(): +def test_literal_with_equals_in_string_sorts() -> None: assert isort.literal.assignment("x = ['c', 'a=b']", "list", "py") == 'x = ["a=b", "c"]' -def test_trailing_comment_on_multiline_literal_is_preserved(): +def test_trailing_comment_on_multiline_literal_is_preserved() -> None: assert ( isort.literal.assignment("x = (\n 'b',\n 'a',\n) # exports", "tuple", "py") == 'x = ("a", "b") # exports' ) -def test_wrapped_literal_honors_config_line_ending(): +def test_wrapped_literal_honors_config_line_ending() -> None: code = "__all__ = ['" + "', '".join(f"name_{i:02d}" for i in range(12)) + "']" result = isort.literal.assignment( code, "list", "py", config=Config(profile="black", line_ending="\r\n") @@ -182,7 +182,7 @@ def test_wrapped_literal_honors_config_line_ending(): assert result == expected -def test_wrapped_literal_infers_crlf_from_source(): +def test_wrapped_literal_infers_crlf_from_source() -> None: code = '__all__ = (\r\n "name_01",\r\n "name_00",\r\n)' result = isort.literal.assignment(code, "tuple", "py", config=Config(profile="black")) expected = '__all__ = (\r\n "name_00",\r\n "name_01",\r\n)' diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index a703935c0..1881f7dbb 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -1,10 +1,10 @@ -from pathlib import Path import json import os import pathlib import shutil import subprocess from datetime import datetime +from pathlib import Path import unittest.mock import pytest @@ -15,14 +15,9 @@ from isort._version import _VERSION_STRING, _IS_COMPILED from isort.exceptions import InvalidSettingsPath from isort.settings import DEFAULT_CONFIG, Config +from isort.wrap_modes import WrapModes from .utils import as_stream -from io import BytesIO, StringIO, TextIOWrapper -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - WrapModes: Any -else: - from isort.wrap_modes import WrapModes +from io import BytesIO, TextIOWrapper @given( @@ -32,7 +27,9 @@ ask_to_apply=st.booleans(), write_to_stdout=st.booleans(), ) -def test_fuzz_sort_imports(file_name, config, check, ask_to_apply, write_to_stdout): +def test_fuzz_sort_imports( + file_name: str, config: Config, check: bool, ask_to_apply: bool, write_to_stdout: bool +) -> None: main.sort_imports( file_name=file_name, config=config, @@ -42,9 +39,9 @@ def test_fuzz_sort_imports(file_name, config, check, ask_to_apply, write_to_stdo ) -def test_sort_imports(tmpdir): - tmp_file = tmpdir.join("file.py") - tmp_file.write("import os, sys\n") +def test_sort_imports(tmp_path: Path) -> None: + tmp_file = tmp_path / "file.py" + tmp_file.write_text("import os, sys\n") assert main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore # noqa main.sort_imports(str(tmp_file), DEFAULT_CONFIG) assert not main.sort_imports(str(tmp_file), DEFAULT_CONFIG, check=True).incorrectly_sorted # type: ignore # noqa @@ -57,9 +54,9 @@ def test_sort_imports(tmpdir): @pytest.mark.skipif(reason="Can't use these mocks in mypyc-compiled code.", condition=_IS_COMPILED) -def test_sort_imports_error_handling(tmpdir, capsys): - tmp_file = tmpdir.join("file.py") - tmp_file.write("import os, sys\n") +def test_sort_imports_error_handling(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: + tmp_file = tmp_path / "file.py" + tmp_file.write_text("import os, sys\n") with ( unittest.mock.patch( "isort.core.process", side_effect=IndexError("Example unhandled exception") @@ -72,7 +69,7 @@ def test_sort_imports_error_handling(tmpdir, capsys): assert "Unrecoverable exception thrown when parsing" in error -def test_parse_args(): +def test_parse_args() -> None: assert main.parse_args([]) == {} assert main.parse_args(["--multi-line", "1"]) == {"multi_line_output": WrapModes.VERTICAL} assert main.parse_args(["--multi-line", "GRID"]) == {"multi_line_output": WrapModes.GRID} @@ -90,7 +87,7 @@ def test_parse_args(): assert main.parse_args(["--resolve-all-configs"]) == {"resolve_all_configs": True} -def test_ascii_art(capsys): +def test_ascii_art(capsys: pytest.CaptureFixture[str]) -> None: main.main(["--version"]) out, error = capsys.readouterr() assert ( @@ -111,7 +108,7 @@ def test_ascii_art(capsys): assert error == "" -def test_preconvert(): +def test_preconvert() -> None: assert main._preconvert(frozenset([1, 1, 2])) == [1, 2] assert main._preconvert(WrapModes.GRID) == "GRID" assert main._preconvert(main._preconvert) == "_preconvert" @@ -119,12 +116,12 @@ def test_preconvert(): main._preconvert(datetime.now()) -def test_show_files(capsys, tmpdir): - tmpdir.join("a.py").write("import a") - tmpdir.join("b.py").write("import b") +def test_show_files(capsys: pytest.CaptureFixture[str], tmp_path: Path) -> None: + (tmp_path / "a.py").write_text("import a") + (tmp_path / "b.py").write_text("import b") # show files should list the files isort would sort - main.main([str(tmpdir), "--show-files"]) + main.main([str(tmp_path), "--show-files"]) out, error = capsys.readouterr() assert "a.py" in out assert "b.py" in out @@ -136,51 +133,51 @@ def test_show_files(capsys, tmpdir): # can not be used with show-config with pytest.raises(SystemExit): - main.main([str(tmpdir), "--show-files", "--show-config"]) + main.main([str(tmp_path), "--show-files", "--show-config"]) -def test_missing_default_section(tmpdir): - config_file = tmpdir.join(".isort.cfg") - config_file.write( +def test_missing_default_section(tmp_path: Path) -> None: + config_file = tmp_path / ".isort.cfg" + config_file.write_text( """ [settings] sections=MADEUP """ ) - python_file = tmpdir.join("file.py") - python_file.write("import os") + python_file = tmp_path / "file.py" + python_file.write_text("import os") with pytest.raises(SystemExit): main.main([str(python_file)]) -def test_ran_against_root(): +def test_ran_against_root() -> None: with pytest.raises(SystemExit): main.main(["/"]) -def test_config_root_interaction_with_resolve_all_configs(tmpdir): - python_file = tmpdir.join("file.py") - python_file.write("import os\n") +def test_config_root_interaction_with_resolve_all_configs(tmp_path: Path) -> None: + python_file = tmp_path / "file.py" + python_file.write_text("import os\n") with pytest.raises(SystemExit) as exc_info: - main.main([str(python_file), "--config-root", str(tmpdir)]) + main.main([str(python_file), "--config-root", str(tmp_path)]) assert "--resolve-all-configs" in str(exc_info.value) - main.main([str(python_file), "--config-root", str(tmpdir), "--resolve-all-configs"]) + main.main([str(python_file), "--config-root", str(tmp_path), "--resolve-all-configs"]) -def test_main(capsys, tmpdir): +def test_main(capsys: pytest.CaptureFixture[str], tmp_path: Path) -> None: base_args = [ "--sp", - str(tmpdir), + str(tmp_path), "--virtual-env", - str(tmpdir), + str(tmp_path), "--src-path", - str(tmpdir), + str(tmp_path), ] - tmpdir.mkdir(".git") + (tmp_path / ".git").mkdir() # If nothing is passed in the quick guide is returned without erroring main.main([]) @@ -199,12 +196,12 @@ def test_main(capsys, tmpdir): out, error = capsys.readouterr() returned_config = json.loads(out) assert returned_config - assert returned_config["virtual_env"] == str(tmpdir) + assert returned_config["virtual_env"] == str(tmp_path) # This should work even if settings path is not provided main.main([*base_args[2:], "--show-config"]) out, error = capsys.readouterr() - assert json.loads(out)["virtual_env"] == str(tmpdir) + assert json.loads(out)["virtual_env"] == str(tmp_path) # This should raise an error if an invalid settings path is provided with pytest.raises(InvalidSettingsPath): @@ -218,8 +215,8 @@ def test_main(capsys, tmpdir): ) # Should be able to set settings path to a file - config_file = tmpdir.join(".isort.cfg") - config_file.write( + config_file = tmp_path / ".isort.cfg" + config_file.write_text( """ [settings] profile=hug @@ -293,19 +290,19 @@ def test_main(capsys, tmpdir): assert error == "ERROR: Imports are incorrectly sorted and/or formatted.\n" # Should be able to run with just a file - python_file = tmpdir.join("has_imports.py") - python_file.write( + python_file = tmp_path / "has_imports.py" + python_file.write_text( """ import b import a """ ) main.main([str(python_file), "--filter-files", "--verbose"]) - assert python_file.read().lstrip() == "import a\nimport b\n" + assert python_file.read_text().lstrip() == "import a\nimport b\n" # Add a file to skip - should_skip = tmpdir.join("should_skip.py") - should_skip.write("import nothing") + should_skip = tmp_path / "should_skip.py" + should_skip.write_text("import nothing") main.main( [ str(python_file), @@ -318,7 +315,7 @@ def test_main(capsys, tmpdir): ) # Should raise a system exit if check only, with broken file - python_file.write( + python_file.write_text( """ import b import a @@ -340,19 +337,28 @@ def test_main(capsys, tmpdir): # Should have same behavior if full directory is skipped with pytest.raises(SystemExit): main.main( - [str(tmpdir), "--filter-files", "--verbose", "--check-only", "--skip", str(should_skip)] + [ + str(tmp_path), + "--filter-files", + "--verbose", + "--check-only", + "--skip", + str(should_skip), + ] ) # Nested files should be skipped without needing --filter-files - nested_file = tmpdir.mkdir("nested_dir").join("skip.py") - nested_file.write("import b;import a") - python_file.write( + nested_dir = tmp_path / "nested_dir" + nested_dir.mkdir() + nested_file = nested_dir / "skip.py" + nested_file.write_text("import b;import a") + python_file.write_text( """ import a import b """ ) - main.main([str(tmpdir), "--extend-skip", "skip.py", "--check"]) + main.main([str(tmp_path), "--extend-skip", "skip.py", "--check"]) # without filter options passed in should successfully sort files main.main([str(python_file), str(should_skip), "--verbose", "--atomic"]) @@ -367,7 +373,7 @@ def test_main(capsys, tmpdir): assert "Broken" in out -def test_isort_filename_overrides(tmpdir, capsys): +def test_isort_filename_overrides(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: """Tests isorts available approaches for overriding filename and extension based behavior""" input_text = """ import b @@ -377,7 +383,7 @@ def function(): pass """ - def build_input_content(): + def build_input_content() -> TextIOWrapper: return as_stream(input_text) main.main(["-"], stdin=build_input_content()) @@ -424,7 +430,7 @@ def function(): """ ) - tmp_file = tmpdir.join("tmp.pyi") + tmp_file = tmp_path / "tmp.pyi" tmp_file.write_text(input_text, encoding="utf8") main.main(["-", "--filename", str(tmp_file)], stdin=build_input_content()) out, error = capsys.readouterr() @@ -444,7 +450,7 @@ def function(): main.main([str(tmp_file), "--filename", str(tmp_file)], stdin=build_input_content()) -def test_isort_float_to_top_overrides(tmpdir, capsys): +def test_isort_float_to_top_overrides(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: """Tests isorts supports overriding float to top from CLI""" test_input = """ import b @@ -456,15 +462,15 @@ def function(): import a """ - config_file = tmpdir.join(".isort.cfg") - config_file.write( + config_file = tmp_path / ".isort.cfg" + config_file.write_text( """ [settings] float_to_top=True """ ) - python_file = tmpdir.join("file.py") - python_file.write(test_input) + python_file = tmp_path / "file.py" + python_file.write_text(test_input) main.main([str(python_file)]) out, error = capsys.readouterr() @@ -481,7 +487,7 @@ def function(): """ ) - python_file.write(test_input) + python_file.write_text(test_input) main.main([str(python_file), "--dont-float-to-top"]) _, error = capsys.readouterr() assert not error @@ -491,7 +497,7 @@ def function(): main.main([str(python_file), "--float-to-top", "--dont-float-to-top"]) -def test_isort_with_stdin(capsys): +def test_isort_with_stdin(capsys: pytest.CaptureFixture[str]) -> None: # ensures that isort sorts stdin without any flags input_content = as_stream( @@ -845,39 +851,8 @@ def test_isort_with_stdin(capsys): ) -def test_isort_with_stdin_preserves_lf_stdout(tmp_path: Path) -> None: - input_file = tmp_path / "in.py" - input_file.write_bytes(b"import re\nimport os\n") - output_file = tmp_path / "out.py" - - with input_file.open("r", newline=None) as stdin, output_file.open("w", newline=None) as stdout: - with unittest.mock.patch("sys.stdin", stdin), unittest.mock.patch("sys.stdout", stdout): - main.main(["-"]) - - assert output_file.read_bytes() == b"import os\nimport re\n" - - -def test_isort_with_stdin_preserves_crlf_stdout(tmp_path: Path) -> None: - input_file = tmp_path / "in.py" - input_file.write_bytes(b"import re\r\nimport os\r\n") - output_file = tmp_path / "out.py" - - with input_file.open("r", newline=None) as stdin, output_file.open("w", newline=None) as stdout: - with unittest.mock.patch("sys.stdin", stdin), unittest.mock.patch("sys.stdout", stdout): - main.main(["-"]) - - assert output_file.read_bytes() == b"import os\r\nimport re\r\n" - - -def test_preserve_newline_stream_keeps_non_textiowrapper() -> None: - input_content = StringIO("import re\nimport os\n") - - with main._stream_with_preserved_newlines(input_content, mode="r") as preserved_stream: - assert preserved_stream is input_content - - -def test_unsupported_encodings(tmpdir, capsys): - tmp_file = tmpdir.join("file.py") +def test_unsupported_encodings(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: + tmp_file = tmp_path / "file.py" # fmt: off tmp_file.write_text( ''' @@ -899,14 +874,14 @@ def test_unsupported_encodings(tmpdir, capsys): assert "No valid encodings." in error # should not throw an error if at least one valid encoding found - normal_file = tmpdir.join("file1.py") - normal_file.write("import os\nimport sys") + normal_file = tmp_path / "file1.py" + normal_file.write_text("import os\nimport sys") main.main([str(tmp_file), str(normal_file), "--verbose"]) _, error = capsys.readouterr() -def test_stream_skip_file(tmpdir, capsys): +def test_stream_skip_file(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: input_with_skip = """ # isort: skip_file import b @@ -944,19 +919,19 @@ def test_stream_skip_file(tmpdir, capsys): ) -def test_only_modified_flag(tmpdir, capsys): +def test_only_modified_flag(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: # ensures there is no verbose output for correct files with only-modified flag - file1 = tmpdir.join("file1.py") - file1.write( + file1 = tmp_path / "file1.py" + file1.write_text( """ import a import b """ ) - file2 = tmpdir.join("file2.py") - file2.write( + file2 = tmp_path / "file2.py" + file2.write_text( """ import math @@ -987,8 +962,8 @@ def test_only_modified_flag(tmpdir, capsys): # ensures that verbose output is only for modified file(s) with only-modified flag - file3 = tmpdir.join("file3.py") - file3.write( + file3 = tmp_path / "file3.py" + file3.write_text( """ import sys import os @@ -1028,8 +1003,8 @@ def test_only_modified_flag(tmpdir, capsys): assert not error - file4 = tmpdir.join("file4.py") - file4.write( + file4 = tmp_path / "file4.py" + file4.write_text( """ import sys import os @@ -1046,10 +1021,10 @@ def test_only_modified_flag(tmpdir, capsys): assert "else-type place_module for pandas returned THIRDPARTY" not in out -def test_identify_imports_main(tmpdir, capsys): +def test_identify_imports_main(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: file_content = "import mod2\nimport mod2\na = 1\nimport mod1\n" - some_file = tmpdir.join("some_file.py") - some_file.write(file_content) + some_file = tmp_path / "some_file.py" + some_file.write_text(file_content) file_imports = f"{some_file}:1 import mod2\n{some_file}:4 import mod1\n" file_imports_with_dupes = ( f"{some_file}:1 import mod2\n{some_file}:2 import mod2\n{some_file}:4 import mod1\n" @@ -1073,7 +1048,7 @@ def test_identify_imports_main(tmpdir, capsys): out, error = capsys.readouterr() assert out.replace("\r\n", "\n") == file_imports_with_dupes.replace(str(some_file), "") - main.identify_imports_main([str(tmpdir)]) + main.identify_imports_main([str(tmp_path)]) main.identify_imports_main(["-", "--packages"], stdin=as_stream(file_content)) out, error = capsys.readouterr() @@ -1088,13 +1063,13 @@ def test_identify_imports_main(tmpdir, capsys): assert len(out.split("\n")) == 3 -def test_gitignore(capsys, tmp_path: pathlib.Path): +def test_gitignore(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path) -> None: import_content = """ import b import a """ - def main_check(args): + def main_check(args: list[str]) -> tuple[str, str]: try: main.main(args) except SystemExit: @@ -1232,7 +1207,7 @@ def main_check(args): assert all(f"{tmp_path}{file}" in out for file in should_check) -def test_multiple_configs(capsys, tmpdir): +def test_multiple_configs(capsys: pytest.CaptureFixture[str], tmp_path: Path) -> None: # Ensure that --resolve-all-configs flag resolves multiple configs correctly # and sorts files corresponding to their nearest config @@ -1256,10 +1231,10 @@ def test_multiple_configs(capsys, tmpdir): force_single_line=True """ - dir1 = tmpdir / "subdir1" - dir2 = tmpdir / "subdir2" - dir3 = tmpdir / "subdir3" - dir4 = tmpdir / "subdir4" + dir1 = tmp_path / "subdir1" + dir2 = tmp_path / "subdir2" + dir3 = tmp_path / "subdir3" + dir4 = tmp_path / "subdir4" dir1.mkdir() dir2.mkdir() @@ -1295,10 +1270,10 @@ def test_multiple_configs(capsys, tmpdir): file4 = dir4 / "file4.py" file4.write_text(import_section, "utf-8") - file5 = tmpdir / "file5.py" + file5 = tmp_path / "file5.py" file5.write_text(import_section, "utf-8") - main.main([str(tmpdir), "--resolve-all-configs", "--cr", str(tmpdir), "--verbose"]) + main.main([str(tmp_path), "--resolve-all-configs", "--cr", str(tmp_path), "--verbose"]) out, _ = capsys.readouterr() assert f"{setup_cfg_file} used for file {file1}" in out @@ -1308,7 +1283,7 @@ def test_multiple_configs(capsys, tmpdir): assert f"default used for file {file5}" in out assert ( - file1.read() + file1.read_text() == """ from a import x, y, z import b @@ -1316,14 +1291,14 @@ def test_multiple_configs(capsys, tmpdir): ) assert ( - file2.read() + file2.read_text() == """ import b from a import y, z, x """ ) assert ( - file3.read() + file3.read_text() == """ import b from a import x @@ -1332,7 +1307,7 @@ def test_multiple_configs(capsys, tmpdir): """ ) assert ( - file4.read() + file4.read_text() == """ import b from a import x, y, z @@ -1340,7 +1315,7 @@ def test_multiple_configs(capsys, tmpdir): ) assert ( - file5.read() + file5.read_text() == """ import b from a import x, y, z @@ -1350,7 +1325,7 @@ def test_multiple_configs(capsys, tmpdir): # Ensure that --resolve-all-config flags works with --check file6 = dir1 / "file6.py" - file6.write( + file6.write_text( """ import b from a import x, y, z @@ -1358,25 +1333,25 @@ def test_multiple_configs(capsys, tmpdir): ) with pytest.raises(SystemExit): - main.main([str(tmpdir), "--resolve-all-configs", "--cr", str(tmpdir), "--check"]) + main.main([str(tmp_path), "--resolve-all-configs", "--cr", str(tmp_path), "--check"]) _, err = capsys.readouterr() assert f"{file6} Imports are incorrectly sorted and/or formatted" in err -def test_multiple_src_paths(tmpdir, capsys): +def test_multiple_src_paths(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: """ Ensure that isort has consistent behavior with multiple source paths """ - tests_module = tmpdir / "tests" - app_module = tmpdir / "app" + tests_module = tmp_path / "tests" + app_module = tmp_path / "app" tests_module.mkdir() app_module.mkdir() - pyproject_toml = tmpdir / "pyproject.toml" + pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( """ [tool.isort] @@ -1386,7 +1361,7 @@ def test_multiple_src_paths(tmpdir, capsys): """, "utf-8", ) - file = tmpdir / "file.py" + file = tmp_path / "file.py" file.write_text( """ from app.something import something @@ -1396,11 +1371,11 @@ def test_multiple_src_paths(tmpdir, capsys): ) for _ in range(10): # To ensure isort has consistent results in multiple runs - main.main([str(tmpdir), "--verbose"]) + main.main([str(tmp_path), "--verbose"]) out, _ = capsys.readouterr() assert ( - file.read() + file.read_text() == """ from app.something import something from tests.something import something_else @@ -1409,12 +1384,16 @@ def test_multiple_src_paths(tmpdir, capsys): assert "from-type place_module for tests.something returned FIRSTPARTY" in out -def test_cli_src_path_glob_pattern(tmpdir, capsys, monkeypatch): - service_a_src = tmpdir.mkdir("service_a").mkdir("src") - service_b_src = tmpdir.mkdir("service_b").mkdir("src") - (tmpdir / "file.py").write_text("import os\n", "utf-8") +def test_cli_src_path_glob_pattern( + tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch +) -> None: + service_a_src = tmp_path / "service_a" / "src" + service_a_src.mkdir(parents=True) + service_b_src = tmp_path / "service_b" / "src" + service_b_src.mkdir(parents=True) + (tmp_path / "file.py").write_text("import os\n", "utf-8") - monkeypatch.chdir(str(tmpdir)) + monkeypatch.chdir(tmp_path) main.main([".", "--src", "*/src/", "--show-config"]) out, _ = capsys.readouterr() diff --git a/tests/unit/test_output.py b/tests/unit/test_output.py index b4d8ed1a4..81fbde2f5 100644 --- a/tests/unit/test_output.py +++ b/tests/unit/test_output.py @@ -10,7 +10,9 @@ removed=st.booleans(), comment_prefix=st.text(), ) -def test_fuzz_add_to_line(comments, original_string, removed, comment_prefix): +def test_fuzz_add_to_line( + comments: list[str] | None, original_string: str, removed: bool, comment_prefix: str +) -> None: try: isort.comments.add_to_line( comments=comments, diff --git a/tests/unit/test_parse.py b/tests/unit/test_parse.py index b34800ee0..60e0995ab 100644 --- a/tests/unit/test_parse.py +++ b/tests/unit/test_parse.py @@ -24,7 +24,7 @@ def function(): """ -def test_file_contents(): +def test_file_contents() -> None: ( in_lines, out_lines, @@ -48,14 +48,14 @@ def test_file_contents(): assert original_line_count == len(in_lines) -def test_file_contents_empty(): +def test_file_contents_empty() -> None: parsed = parse.file_contents("", config=Config(default_section="")) assert parsed.in_lines == [] assert parsed.original_line_count == 0 @pytest.mark.parametrize("line_separator", ["\n", "\r\n", "\r"]) -def test_file_contents_splits_only_on_newlines(line_separator): +def test_file_contents_splits_only_on_newlines(line_separator: str) -> None: contents = line_separator.join(["import b", "import a", "\fpass"]) parsed = parse.file_contents(contents, config=Config(default_section="")) assert parsed.in_lines == ["import b", "import a", "\fpass"] @@ -66,22 +66,22 @@ def test_file_contents_splits_only_on_newlines(line_separator): @given(contents=st.text()) -def test_fuzz__infer_line_separator(contents): +def test_fuzz__infer_line_separator(contents: str) -> None: parse._infer_line_separator(contents=contents, configured="\n") @given(import_string=st.text()) -def test_fuzz__strip_syntax(import_string): +def test_fuzz__strip_syntax(import_string: str) -> None: _parse_utils.strip_syntax(import_string=import_string) @given(line=st.text(), config=st.builds(Config)) -def test_fuzz_import_type(line, config): +def test_fuzz_import_type(line: str, config: Config) -> None: _parse_utils.import_type(line=line, config=config) @given(line=st.text(), in_quote=st.text(), needs_import=st.booleans()) -def test_fuzz_skip_line(line, in_quote, needs_import): +def test_fuzz_skip_line(line: str, in_quote: str, needs_import: bool) -> None: _parse_utils.skip_line(line=line, in_quote=in_quote, needs_import=needs_import) @@ -105,7 +105,7 @@ def test_fuzz_skip_line(line, in_quote, needs_import): ("from\t.\timport a", "from . import a"), ], ) -def test_normalize_line(raw_line, expected): +def test_normalize_line(raw_line: str, expected: str) -> None: line, returned_raw_line = _parse_utils.normalize_line(raw_line) assert line == expected assert returned_raw_line == raw_line diff --git a/tests/unit/test_place.py b/tests/unit/test_place.py index c850c689a..dd35b3f86 100644 --- a/tests/unit/test_place.py +++ b/tests/unit/test_place.py @@ -1,12 +1,13 @@ """Tests for the isort import placement module""" from functools import partial +from pathlib import Path from isort import place, sections from isort.settings import Config -def test_module(src_path): +def test_module(src_path: Path) -> None: place_tester = partial(place.module, config=Config(src_paths=[src_path])) assert place_tester("isort") == sections.FIRSTPARTY assert place_tester("os") == sections.STDLIB @@ -15,7 +16,7 @@ def test_module(src_path): assert place_tester("hug") == sections.THIRDPARTY -def test_extra_standard_library(src_path): +def test_extra_standard_library(src_path: Path) -> None: place_tester = partial( place.module, config=Config(src_paths=[src_path], extra_standard_library=["hug"]) ) @@ -23,14 +24,14 @@ def test_extra_standard_library(src_path): assert place_tester("hug") == sections.STDLIB -def test_no_standard_library_placement(): +def test_no_standard_library_placement() -> None: assert place.module_with_reason( "pathlib", config=Config(sections=["THIRDPARTY"], default_section="THIRDPARTY") ) == ("THIRDPARTY", "Default option in Config or universal default.") assert place.module("pathlib") == "STDLIB" -def test_namespace_package_placement(examples_path): +def test_namespace_package_placement(examples_path: Path) -> None: namespace_examples = examples_path / "namespaces" implicit = namespace_examples / "implicit" diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 2605b6e2b..38c260737 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -13,7 +13,7 @@ from isort.wrap_modes import WrapModes -def test_isort_duplicating_comments_issue_1264(): +def test_isort_duplicating_comments_issue_1264() -> None: """Ensure isort doesn't duplicate comments when force_sort_within_sections is set to `True` as was the case in issue #1264: https://github.com/pycqa/isort/issues/1264 """ @@ -31,7 +31,7 @@ def test_isort_duplicating_comments_issue_1264(): ) -def test_moving_comments_issue_726(): +def test_moving_comments_issue_726() -> None: test_input = ( "from Blue import models as BlueModels\n" "# comment for PlaidModel\n" @@ -49,7 +49,7 @@ def test_moving_comments_issue_726(): assert isort.code(test_input, force_sort_within_sections=True) == test_input -def test_pylint_disable_next_stays_with_first_import_issue_2054(): +def test_pylint_disable_next_stays_with_first_import_issue_2054() -> None: test_input = ( "# pylint: disable-next=import-error\n" "from C import D\n" @@ -66,7 +66,7 @@ def test_pylint_disable_next_stays_with_first_import_issue_2054(): assert isort.code(test_input) == expected_output -def test_blank_lines_before_comments_issue_2156(): +def test_blank_lines_before_comments_issue_2156() -> None: test_input = """ def my_function(): def inner_function(): @@ -105,7 +105,7 @@ def test_force_sort_within_sections_uses_logical_import_lines_issue_1985() -> No ) -def test_blank_lined_removed_issue_1275(): +def test_blank_lined_removed_issue_1275() -> None: """Ensure isort doesn't accidentally remove blank lines after doc strings and before imports. See: https://github.com/pycqa/isort/issues/1275 """ @@ -149,7 +149,7 @@ def test_blank_lined_removed_issue_1275(): ) -def test_blank_lined_removed_issue_1283(): +def test_blank_lined_removed_issue_1283() -> None: """Ensure isort doesn't accidentally remove blank lines after __version__ identifiers. See: https://github.com/pycqa/isort/issues/1283 """ @@ -160,7 +160,7 @@ def test_blank_lined_removed_issue_1283(): assert isort.code(test_input) == test_input -def test_extra_blank_line_added_nested_imports_issue_1290(): +def test_extra_blank_line_added_nested_imports_issue_1290() -> None: """Ensure isort doesn't add unnecessary blank lines above nested imports. See: https://github.com/pycqa/isort/issues/1290 """ @@ -192,7 +192,7 @@ def func(): ) -def test_add_imports_shouldnt_make_isort_unusable_issue_1297(): +def test_add_imports_shouldnt_make_isort_unusable_issue_1297() -> None: """Test to ensure add imports doesn't cause any unexpected behaviour when combined with check See: https://github.com/pycqa/isort/issues/1297 """ @@ -205,7 +205,7 @@ def test_add_imports_shouldnt_make_isort_unusable_issue_1297(): ) -def test_no_extra_lines_for_imports_in_functions_issue_1277(): +def test_no_extra_lines_for_imports_in_functions_issue_1277() -> None: """Test to ensure isort doesn't introduce extra blank lines for imports within function. See: https://github.com/pycqa/isort/issues/1277 """ @@ -223,7 +223,7 @@ def main(): assert isort.code(isort.code(isort.code(test_input))) == expected_output -def test_no_extra_blank_lines_in_methods_issue_1293(): +def test_no_extra_blank_lines_in_methods_issue_1293() -> None: """Test to ensure isort isn't introducing extra lines in methods that contain imports See: https://github.com/pycqa/isort/issues/1293 """ @@ -241,7 +241,7 @@ def on_email_deleted(self, email): assert isort.code(test_input, lines_after_imports=2) == test_input -def test_form_feed_blank_line_not_removed_issue_2562(): +def test_form_feed_blank_line_not_removed_issue_2562() -> None: """Ensure isort preserves form feed as a valid blank line.""" test_input = 'import sys\n\n\f\nprint("!")\n' assert isort.code(test_input) == 'import sys\n\n\fprint("!")\n' @@ -252,7 +252,7 @@ def test_form_feed_blank_line_not_removed_issue_2562(): assert isort.code(test_input, lines_after_imports=2) == test_input -def test_force_single_line_shouldnt_remove_preceding_comment_lines_issue_1296(): +def test_force_single_line_shouldnt_remove_preceding_comment_lines_issue_1296() -> None: """Tests to ensure force_single_line setting doesn't result in lost comments. See: https://github.com/pycqa/isort/issues/1296 """ @@ -267,7 +267,7 @@ def test_force_single_line_shouldnt_remove_preceding_comment_lines_issue_1296(): assert isort.code(test_input, force_single_line=True) == test_input -def test_ensure_new_line_before_comments_mixed_with_ensure_newline_before_comments_1295(): +def test_ensure_new_line_before_comments_mixed_with_ensure_newline_before_comments_1295() -> None: """Tests to ensure that the black profile can be used in conjunction with force_sort_within_sections. @@ -284,7 +284,7 @@ def test_ensure_new_line_before_comments_mixed_with_ensure_newline_before_commen assert isort.code(test_input, profile="black", force_sort_within_sections=True) == test_input -def test_trailing_comma_doesnt_introduce_broken_code_with_comment_and_wrap_issue_1302(): +def test_trailing_comma_doesnt_introduce_broken_code_with_comment_and_wrap_issue_1302() -> None: """Tests to assert the combination of include_trailing_comma and a wrapped line doesn't break. See: https://github.com/pycqa/isort/issues/1302. """ @@ -303,14 +303,14 @@ def test_trailing_comma_doesnt_introduce_broken_code_with_comment_and_wrap_issue ) -def test_ensure_sre_parse_is_identified_as_stdlib_issue_1304(): +def test_ensure_sre_parse_is_identified_as_stdlib_issue_1304() -> None: """Ensure sre_parse is identified as STDLIB. See: https://github.com/pycqa/isort/issues/1304. """ assert isort.place_module("sre_parse") == isort.place_module("sre") == isort.sections.STDLIB -def test_add_imports_shouldnt_move_lower_comments_issue_1300(): +def test_add_imports_shouldnt_move_lower_comments_issue_1300() -> None: """Ensure add_imports doesn't move comments immediately below imports. See:: https://github.com/pycqa/isort/issues/1300. """ @@ -324,7 +324,7 @@ def test_add_imports_shouldnt_move_lower_comments_issue_1300(): assert isort.code(test_input, add_imports=["from os import path"]) == test_input -def test_windows_newline_issue_1277(): +def test_windows_newline_issue_1277() -> None: """Test to ensure windows new lines are correctly handled within indented scopes. See: https://github.com/pycqa/isort/issues/1277 """ @@ -334,7 +334,7 @@ def test_windows_newline_issue_1277(): ) -def test_windows_newline_issue_1278(): +def test_windows_newline_issue_1278() -> None: """Test to ensure windows new lines are correctly handled within indented scopes. See: https://github.com/pycqa/isort/issues/1278 """ @@ -345,7 +345,7 @@ def test_windows_newline_issue_1278(): ) -def test_check_never_passes_with_indented_headings_issue_1301(): +def test_check_never_passes_with_indented_headings_issue_1301() -> None: """Test to ensure that test can pass even when there are indented headings. See: https://github.com/pycqa/isort/issues/1301 """ @@ -362,7 +362,7 @@ def test_check_never_passes_with_indented_headings_issue_1301(): ) -def test_isort_shouldnt_fail_on_long_from_with_dot_issue_1190(): +def test_isort_shouldnt_fail_on_long_from_with_dot_issue_1190() -> None: """Test to ensure that isort will correctly handle formatting a long from import that contains a dot. See: https://github.com/pycqa/isort/issues/1190 @@ -389,7 +389,7 @@ def test_isort_shouldnt_fail_on_long_from_with_dot_issue_1190(): ) -def test_isort_shouldnt_add_extra_new_line_when_fass_and_n_issue_1315(): +def test_isort_shouldnt_add_extra_new_line_when_fass_and_n_issue_1315() -> None: """Test to ensure isort doesn't add a second extra new line when combining --fss and -n options. See: https://github.com/pycqa/isort/issues/1315 """ @@ -423,7 +423,7 @@ def test_isort_shouldnt_add_extra_new_line_when_fass_and_n_issue_1315(): ) -def test_isort_doesnt_rewrite_import_with_dot_to_from_import_issue_1280(): +def test_isort_doesnt_rewrite_import_with_dot_to_from_import_issue_1280() -> None: """Test to ensure isort doesn't rewrite imports in the from of import y.x into from y import x. This is because they are not technically fully equivalent to each other and can introduce broken behaviour. @@ -440,7 +440,7 @@ def test_isort_doesnt_rewrite_import_with_dot_to_from_import_issue_1280(): ) -def test_isort_shouldnt_introduce_extra_lines_with_fass_issue_1322(): +def test_isort_shouldnt_introduce_extra_lines_with_fass_issue_1322() -> None: """Tests to ensure isort doesn't introduce extra lines when used with fass option. See: https://github.com/pycqa/isort/issues/1322 """ @@ -466,7 +466,7 @@ def test_isort_shouldnt_introduce_extra_lines_with_fass_issue_1322(): ) -def test_comments_should_cause_wrapping_on_long_lines_black_mode_issue_1219(): +def test_comments_should_cause_wrapping_on_long_lines_black_mode_issue_1219() -> None: """Tests to ensure if isort encounters a single import line which is made too long with a comment it is wrapped when using black profile. See: https://github.com/pycqa/isort/issues/1219 @@ -482,7 +482,7 @@ def test_comments_should_cause_wrapping_on_long_lines_black_mode_issue_1219(): ) -def test_comment_blocks_should_stay_associated_without_extra_lines_issue_1156(): +def test_comment_blocks_should_stay_associated_without_extra_lines_issue_1156() -> None: """Tests to ensure isort doesn't add an extra line when there are large import blocks or otherwise warp the intent. See: https://github.com/pycqa/isort/issues/1156 @@ -507,7 +507,7 @@ def test_comment_blocks_should_stay_associated_without_extra_lines_issue_1156(): ) -def test_comment_shouldnt_be_duplicated_with_fass_enabled_issue_1329(): +def test_comment_shouldnt_be_duplicated_with_fass_enabled_issue_1329() -> None: """Tests to ensure isort doesn't duplicate comments when imports occur with comment on top, immediately after large comment blocks. See: https://github.com/pycqa/isort/pull/1329/files. @@ -526,7 +526,7 @@ def test_comment_shouldnt_be_duplicated_with_fass_enabled_issue_1329(): ) -def test_wrap_mode_equal_to_line_length_with_indendet_imports_issue_1333(): +def test_wrap_mode_equal_to_line_length_with_indendet_imports_issue_1333() -> None: assert isort.check_code( """ import a @@ -543,7 +543,7 @@ def function(): ) -def test_isort_skipped_nested_imports_issue_1339(): +def test_isort_skipped_nested_imports_issue_1339() -> None: """Ensure `isort:skip are honored in nested imports. See: https://github.com/pycqa/isort/issues/1339. """ @@ -558,7 +558,7 @@ def import_test(): ) -def test_windows_diff_too_large_misrepresentative_issue_1348(test_path): +def test_windows_diff_too_large_misrepresentative_issue_1348(test_path: Path) -> None: """Ensure isort handles windows files correctly when it come to producing a diff with --diff. See: https://github.com/pycqa/isort/issues/1348 """ @@ -570,7 +570,7 @@ def test_windows_diff_too_large_misrepresentative_issue_1348(test_path): ) -def test_combine_as_does_not_lose_comments_issue_1321(): +def test_combine_as_does_not_lose_comments_issue_1321() -> None: """Test to ensure isort doesn't lose comments when --combine-as is used. See: https://github.com/pycqa/isort/issues/1321 """ @@ -598,7 +598,7 @@ def test_combine_as_does_not_lose_comments_issue_1321(): assert isort.code(test_input, combine_as_imports=True) == expected_output -def test_combine_as_does_not_lose_comments_issue_1381(): +def test_combine_as_does_not_lose_comments_issue_1381() -> None: """Test to ensure isort doesn't lose comments when --combine-as is used. See: https://github.com/pycqa/isort/issues/1381 """ @@ -613,7 +613,7 @@ def test_combine_as_does_not_lose_comments_issue_1381(): assert "# type: ignore" in isort.code(test_input, combine_as_imports=True) -def test_incorrect_grouping_when_comments_issue_1396(): +def test_incorrect_grouping_when_comments_issue_1396() -> None: """Test to ensure isort groups import correct independent of the comments present. See: https://github.com/pycqa/isort/issues/1396 """ @@ -675,7 +675,7 @@ def test_incorrect_grouping_when_comments_issue_1396(): ) -def test_reverse_relative_combined_with_force_sort_within_sections_issue_1395(): +def test_reverse_relative_combined_with_force_sort_within_sections_issue_1395() -> None: """Test to ensure reverse relative combines well with other common isort settings. See: https://github.com/pycqa/isort/issues/1395. """ @@ -695,7 +695,7 @@ def test_reverse_relative_combined_with_force_sort_within_sections_issue_1395(): ) -def test_isort_should_be_able_to_add_independent_of_doc_string_placement_issue_1420(): +def test_isort_should_be_able_to_add_independent_of_doc_string_placement_issue_1420() -> None: """isort should be able to know when an import requested to be added is successfully added, independent of where the top doc string is located. See: https://github.com/PyCQA/isort/issues/1420 @@ -710,7 +710,7 @@ def test_isort_should_be_able_to_add_independent_of_doc_string_placement_issue_1 ) -def test_force_single_line_should_not_influence_order_of_star_import(): +def test_force_single_line_should_not_influence_order_of_star_import() -> None: """isort should never move comments to different import statement. Originally reported as an issue with moving comments across import statements, @@ -749,7 +749,7 @@ def test_force_single_line_should_not_influence_order_of_star_import(): ) -def test_isort_doesnt_misplace_comments_issue_1431(): +def test_isort_doesnt_misplace_comments_issue_1431() -> None: """Test to ensure isort won't misplace comments. See: https://github.com/PyCQA/isort/issues/1431 """ @@ -763,7 +763,7 @@ def test_isort_doesnt_misplace_comments_issue_1431(): assert isort.code(input_text, profile="black") == input_text -def test_isort_doesnt_misplace_add_import_issue_1445(): +def test_isort_doesnt_misplace_add_import_issue_1445() -> None: """Test to ensure isort won't misplace an added import depending on docstring position See: https://github.com/PyCQA/isort/issues/1445 """ @@ -795,7 +795,7 @@ def test_isort_doesnt_misplace_add_import_issue_1445(): ) -def test_isort_doesnt_mangle_code_when_adding_imports_issue_1444(): +def test_isort_doesnt_mangle_code_when_adding_imports_issue_1444() -> None: """isort should NEVER mangle code. This particularly nasty and easy to reproduce bug, caused isort to produce invalid code just by adding a single import statement depending on comment placement. @@ -818,7 +818,7 @@ def test_isort_doesnt_mangle_code_when_adding_imports_issue_1444(): ) -def test_isort_float_to_top_with_sort_on_off_tests(): +def test_isort_float_to_top_with_sort_on_off_tests() -> None: """Characterization test for current behaviour of float-to-top on isort: on/off sections. - imports in isort:off sections stay where they are - imports in isort:on sections float up, but to the top of the isort:on section (not the @@ -893,7 +893,7 @@ def bar(): assert isort.code(to_sort, float_to_top=True) == to_sort -def test_isort_float_to_top_respects_isort_off_with_crlf_issue_2528(): +def test_isort_float_to_top_respects_isort_off_with_crlf_issue_2528() -> None: to_sort = """ import b import a @@ -920,7 +920,7 @@ def test_isort_float_to_top_respects_isort_off_with_crlf_issue_2528(): ) -def test_isort_doesnt_float_to_top_correctly_when_imports_not_at_top_issue_1382(): +def test_isort_doesnt_float_to_top_correctly_when_imports_not_at_top_issue_1382() -> None: """isort should float existing imports to the top, if they are currently below the top. See: https://github.com/PyCQA/isort/issues/1382 """ @@ -1117,7 +1117,7 @@ def bar(): ) -def test_empty_float_to_top_shouldnt_error_issue_1453(): +def test_empty_float_to_top_shouldnt_error_issue_1453() -> None: """isort shouldn't error when float to top is set with a mostly empty file""" assert isort.check_code( """ @@ -1132,7 +1132,7 @@ def test_empty_float_to_top_shouldnt_error_issue_1453(): ) -def test_import_sorting_shouldnt_be_endless_with_headers_issue_1454(): +def test_import_sorting_shouldnt_be_endless_with_headers_issue_1454() -> None: """isort should never enter an endless sorting loop. See: https://github.com/PyCQA/isort/issues/1454 """ @@ -1155,7 +1155,7 @@ def test_import_sorting_shouldnt_be_endless_with_headers_issue_1454(): ) -def test_isort_should_leave_non_import_from_lines_alone_issue_1488(): +def test_isort_should_leave_non_import_from_lines_alone_issue_1488() -> None: """isort should never mangle non-import from statements. See: https://github.com/PyCQA/isort/issues/1488 """ @@ -1287,7 +1287,7 @@ def generator_function(): assert isort.check_code(raise_from_at_file_end_ignored, show_diff=True) -def test_isort_float_to_top_correctly_identifies_single_line_comments_1499(): +def test_isort_float_to_top_correctly_identifies_single_line_comments_1499() -> None: """Test to ensure isort correctly handles the case where float to top is used to push imports to the top and the top comment is a multiline type but only one line. @@ -1358,7 +1358,7 @@ def bar(): ) -def test_isort_shouldnt_mangle_from_multi_line_string_issue_1507(): +def test_isort_shouldnt_mangle_from_multi_line_string_issue_1507() -> None: """isort was seen mangling lines that happened to contain the word from after a yield happened to be in a file. Clearly this shouldn't happen. See: https://github.com/PyCQA/isort/issues/1507. @@ -1398,7 +1398,7 @@ def d(): ) -def test_isort_should_keep_all_as_and_non_as_imports_issue_1523(): +def test_isort_should_keep_all_as_and_non_as_imports_issue_1523() -> None: """isort should keep as and non-as imports of the same path that happen to exist within the same statement. See: https://github.com/PyCQA/isort/issues/1523. @@ -1412,7 +1412,7 @@ def test_isort_should_keep_all_as_and_non_as_imports_issue_1523(): ) -def test_isort_shouldnt_introduce_syntax_error_issue_1539(): +def test_isort_shouldnt_introduce_syntax_error_issue_1539() -> None: """isort should NEVER introduce syntax errors. In 5.5.4 some strings that contained a line starting with from could lead to no empty paren. See: https://github.com/PyCQA/isort/issues/1539. @@ -1461,7 +1461,7 @@ def test_isort_shouldnt_introduce_syntax_error_issue_1539(): ) -def test_isort_shouldnt_split_skip_issue_1548(): +def test_isort_shouldnt_split_skip_issue_1548() -> None: """Ensure isort doesn't add a spurious new line if isort: skip is combined with float to top. See: https://github.com/PyCQA/isort/issues/1548. """ @@ -1547,7 +1547,7 @@ def test_isort_shouldnt_split_skip_issue_1548(): ) -def test_isort_shouldnt_split_skip_issue_1556(): +def test_isort_shouldnt_split_skip_issue_1556() -> None: assert isort.check_code( """ from tools.dependency_pruning.prune_dependencies import ( # isort:skip @@ -1574,7 +1574,7 @@ def test_isort_shouldnt_split_skip_issue_1556(): ) -def test_isort_losing_imports_vertical_prefix_from_module_import_wrap_mode_issue_1542(): +def test_isort_losing_imports_vertical_prefix_from_module_import_wrap_mode_issue_1542() -> None: """Ensure isort doesn't lose imports when a comment is combined with an import and wrap mode VERTICAL_PREFIX_FROM_MODULE_IMPORT is used. See: https://github.com/PyCQA/isort/issues/1542. @@ -1610,7 +1610,7 @@ def test_isort_losing_imports_vertical_prefix_from_module_import_wrap_mode_issue ) -def test_isort_adding_second_comma_issue_1621(): +def test_isort_adding_second_comma_issue_1621() -> None: """Ensure isort doesn't add a second comma when very long comment is present See: https://github.com/PyCQA/isort/issues/1621. """ @@ -1640,7 +1640,7 @@ def test_isort_adding_second_comma_issue_1621(): ) -def test_isort_shouldnt_duplicate_comments_issue_1631(): +def test_isort_shouldnt_duplicate_comments_issue_1631() -> None: assert isort.check_code( """ import a # a comment @@ -1662,7 +1662,7 @@ def test_isort_shouldnt_duplicate_comments_issue_1631(): ) -def test_isort_shouldnt_add_extra_new_lines_with_import_heading_issue_1670(): +def test_isort_shouldnt_add_extra_new_lines_with_import_heading_issue_1670() -> None: snippet = """#!/usr/bin/python3 -ttu # Standard Library import argparse @@ -1693,7 +1693,7 @@ def spam(): ) -def test_isort_shouldnt_add_extra_line_float_to_top_issue_1667(): +def test_isort_shouldnt_add_extra_line_float_to_top_issue_1667() -> None: assert isort.check_code( """ import sys @@ -1710,7 +1710,7 @@ def test_isort_shouldnt_add_extra_line_float_to_top_issue_1667(): ) -def test_isort_shouldnt_move_noqa_comment_issue_1594(): +def test_isort_shouldnt_move_noqa_comment_issue_1594() -> None: assert ( isort.code( """ @@ -1732,7 +1732,7 @@ def test_isort_shouldnt_move_noqa_comment_issue_1594(): ) -def test_isort_correctly_handles_unix_vs_linux_newlines_issue_1566(): +def test_isort_correctly_handles_unix_vs_linux_newlines_issue_1566() -> None: import_statement = ( "from impacket.smb3structs import (\n" "SMB2_CREATE, SMB2_FLAGS_DFS_OPERATIONS, SMB2_IL_IMPERSONATION, " @@ -1744,7 +1744,7 @@ def test_isort_correctly_handles_unix_vs_linux_newlines_issue_1566(): ).replace("\r\n", "\n") -def test_isort_treats_src_paths_same_as_from_config_as_cli_issue_1711(tmpdir): +def test_isort_treats_src_paths_same_as_from_config_as_cli_issue_1711(tmp_path: Path) -> None: assert isort.check_code( """ import mymodule @@ -1753,16 +1753,17 @@ def test_isort_treats_src_paths_same_as_from_config_as_cli_issue_1711(tmpdir): show_diff=True, ) - config_file = tmpdir.join(".isort.cfg") - config_file.write( + config_file = tmp_path / ".isort.cfg" + config_file.write_text( """ [settings] src_paths= api """ ) - api_dir = tmpdir.mkdir("api") - api_dir.join("mymodule.py").write("# comment") + api_dir = tmp_path / "api" + api_dir.mkdir() + (api_dir / "mymodule.py").write_text("# comment") config = isort.settings.Config(str(config_file)) assert isort.check_code( @@ -1776,7 +1777,7 @@ def test_isort_treats_src_paths_same_as_from_config_as_cli_issue_1711(tmpdir): ) -def test_isort_should_never_quietly_remove_imports_in_hanging_line_mode_issue_1741(): +def test_isort_should_never_quietly_remove_imports_in_hanging_line_mode_issue_1741() -> None: assert ( isort.code( """ @@ -1834,7 +1835,7 @@ def test_isort_should_never_quietly_remove_imports_in_hanging_line_mode_issue_17 @pytest.mark.parametrize("multi_line_output", range(12)) def test_isort_should_never_quietly_remove_imports_in_any_hangin_mode_issue_1741( multi_line_output: int, -): +) -> None: sorted_code = isort.code( """ from src import abcd, qwerty, efg, xyz # some comment @@ -1848,7 +1849,7 @@ def test_isort_should_never_quietly_remove_imports_in_any_hangin_mode_issue_1741 assert "xyz" in sorted_code -def test_isort_should_keep_multi_noqa_with_star_issue_1744(): +def test_isort_should_keep_multi_noqa_with_star_issue_1744() -> None: assert isort.check_code( """ from typing import * # noqa @@ -1903,7 +1904,7 @@ def test_isort_should_keep_multi_noqa_with_star_issue_1744(): ) -def test_isort_should_keep_multiple_noqa_comments_force_single_line_mode_issue_1721(): +def test_isort_should_keep_multiple_noqa_comments_force_single_line_mode_issue_1721() -> None: assert isort.check_code( """ from some_very_long_filename_to_import_from_that_causes_a_too_long_import_row import ( # noqa: E501 @@ -1919,7 +1920,7 @@ def test_isort_should_keep_multiple_noqa_comments_force_single_line_mode_issue_1 ) -def test_isort_should_only_add_imports_to_valid_location_issue_1769(): +def test_isort_should_only_add_imports_to_valid_location_issue_1769() -> None: assert ( isort.code( '''v = """ @@ -1949,7 +1950,7 @@ def test_isort_should_only_add_imports_to_valid_location_issue_1769(): ) -def test_literal_sort_at_top_of_file_issue_1792(): +def test_literal_sort_at_top_of_file_issue_1792() -> None: assert ( isort.code( '''"""I'm a docstring! Look at me!""" @@ -1990,13 +1991,15 @@ class Bar: ) -def test_isort_should_produce_the_same_code_on_subsequent_runs_issue_1799(tmpdir): +def test_isort_should_produce_the_same_code_on_subsequent_runs_issue_1799( + tmp_path: Path, +) -> None: code = """import sys from importlib.metadata import PackageNotFoundError, version """ - config_file = tmpdir.join(".isort.cfg") - config_file.write( + config_file = tmp_path / ".isort.cfg" + config_file.write_text( """[isort] profile=black src_paths=isort,test @@ -2016,7 +2019,7 @@ def test_isort_should_produce_the_same_code_on_subsequent_runs_issue_1799(tmpdir ) -def test_check_code_should_not_false_positive_with_float_to_top_and_add_imports(): +def test_check_code_should_not_false_positive_with_float_to_top_and_add_imports() -> None: """isort check_code should not report incorrectly sorted imports when float_to_top and add_imports are used together and the import is already present. See: https://github.com/PyCQA/isort/issues/1971 @@ -2087,7 +2090,7 @@ def test_float_to_top_cli_check_accepts_crlf_issue_2681(tmp_path: Path) -> None: assert path.read_bytes() == source -def test_unrecoverable_exception_on_valid_input_ending_with_backslash_issue_1893(): +def test_unrecoverable_exception_on_valid_input_ending_with_backslash_issue_1893() -> None: """Ensure isort doesn't raise an IndexError on valid input ending with a backslash without a trailing newline, as was the case in issue #1893: https://github.com/PyCQA/isort/issues/1893 @@ -2095,7 +2098,7 @@ def test_unrecoverable_exception_on_valid_input_ending_with_backslash_issue_1893 assert isort.code("import os #\\") == "import os # \\\n" -def test_comment_on_opening_line_of_aliased_import_does_not_move(): +def test_comment_on_opening_line_of_aliased_import_does_not_move() -> None: """Ensure isort doesn't move comments from the opening "from ... import (" line to the alias attribute line when using import aliases that wrap across multiple lines. See: https://github.com/PyCQA/isort/issues/2392 @@ -2170,7 +2173,7 @@ def test_comment_on_opening_line_of_aliased_import_does_not_move(): ) -def test_sort_reexports_with_stdin_raises_error_issue_2393(): +def test_sort_reexports_with_stdin_raises_error_issue_2393() -> None: """Ensure --sort-reexports raises a clear error when used with stdin.""" fake_stdin = TextIOWrapper(BytesIO(b"from test import B, A\n")) with pytest.raises(SystemExit) as exc_info: @@ -2178,7 +2181,7 @@ def test_sort_reexports_with_stdin_raises_error_issue_2393(): assert exc_info.value.code != 0 -def test_split_on_trailing_comma_idempotent_with_non_default_wrap_mode(): +def test_split_on_trailing_comma_idempotent_with_non_default_wrap_mode() -> None: """Ensure isort output is idempotent when ``split_on_trailing_comma`` and ``include_trailing_comma`` are combined with a non-default ``multi_line_output`` mode. @@ -2220,7 +2223,7 @@ def test_split_on_trailing_comma_idempotent_with_non_default_wrap_mode(): ) -def test_noqa_wrap_mode_idempotent_with_existing_comment(): +def test_noqa_wrap_mode_idempotent_with_existing_comment() -> None: """Ensure ``multi_line_output=NOQA`` does not keep prepending ``NOQA`` to an import that already carries its own comment. @@ -2251,7 +2254,7 @@ def test_noqa_wrap_mode_idempotent_with_existing_comment(): assert second_pass.count("NOQA") == 1 -def test_noqa_wrap_mode_does_not_accumulate_spaces_with_as_import(): +def test_noqa_wrap_mode_does_not_accumulate_spaces_with_as_import() -> None: """A long ``as`` import in NOQA mode must not grow extra spaces before ``# NOQA``. With ``force_single_line`` an aliased import that overflows the line length is emitted @@ -2274,7 +2277,7 @@ def test_noqa_wrap_mode_does_not_accumulate_spaces_with_as_import(): assert second_pass == first_pass -def test_sort_reexports_respects_black_profile_issue_2280(): +def test_sort_reexports_respects_black_profile_issue_2280() -> None: """``--sort-reexports`` must honor the active formatting config, not stdlib ``pprint``. ``isort.literal`` used to format the sorted ``__all__`` with stdlib ``pprint``, which @@ -2310,7 +2313,7 @@ def test_sort_reexports_respects_black_profile_issue_2280(): assert isort.code(test_input, profile="black", sort_reexports=True) == expected_output -def test_literal_dict_sort_respects_black_profile_issue_2280(): +def test_literal_dict_sort_respects_black_profile_issue_2280() -> None: """The ``# isort: dict`` literal sort shares the same formatter as ``--sort-reexports`` and must likewise honor the black profile rather than stdlib ``pprint`` (which produced single quotes, pprint-style wrapping and no trailing comma). Same root cause as #2280. @@ -2329,7 +2332,7 @@ def test_literal_dict_sort_respects_black_profile_issue_2280(): assert isort.code(test_input, profile="black") == expected_output -def test_sort_reexports_output_is_black_stable_issue_2280(): +def test_sort_reexports_output_is_black_stable_issue_2280() -> None: """isort's sorted __all__ under the black profile must be a fixpoint for both isort and black (running either again changes nothing). See issue #2280.""" import black # noqa: PLC0415 @@ -2352,7 +2355,7 @@ def test_sort_reexports_output_is_black_stable_issue_2280(): assert black_out == first -def test_sort_reexports_check_mode_multiline_all_issue_2280(): +def test_sort_reexports_check_mode_multiline_all_issue_2280() -> None: """``--check`` on a multi-line ``__all__`` with ``--sort-reexports`` must not crash. Check mode routes output to a null stream whose ``tell()`` is always 0. The reexport @@ -2372,7 +2375,7 @@ def test_sort_reexports_check_mode_multiline_all_issue_2280(): assert isort.check_code(checked, show_diff=False, profile="black", sort_reexports=True) -def test_sort_reexports_preserves_short_multiline_trailing_comma_issue_2578(): +def test_sort_reexports_preserves_short_multiline_trailing_comma_issue_2578() -> None: """A short __all__ with a trailing comma should keep its explicit multiline style.""" test_input = """__all__ = ( "FirstClass", @@ -2382,7 +2385,7 @@ def test_sort_reexports_preserves_short_multiline_trailing_comma_issue_2578(): assert isort.code(test_input, profile="black", sort_reexports=True) == test_input -def test_sort_reexports_preserves_short_multiline_list_trailing_comma_issue_2578(): +def test_sort_reexports_preserves_short_multiline_list_trailing_comma_issue_2578() -> None: """The same trailing-comma preservation applies to list-style __all__ exports.""" test_input = """__all__ = [ "FirstClass", @@ -2392,7 +2395,7 @@ def test_sort_reexports_preserves_short_multiline_list_trailing_comma_issue_2578 assert isort.code(test_input, profile="black", sort_reexports=True) == test_input -def test_sort_reexports_preserves_crlf_line_endings_issue_2668(): +def test_sort_reexports_preserves_crlf_line_endings_issue_2668() -> None: """``--sort-reexports`` must keep CRLF endings instead of injecting bare LFs.""" test_input = ( "from .somewhere import FirstClass, SecondClass\r\n" @@ -2413,7 +2416,7 @@ def test_sort_reexports_preserves_crlf_line_endings_issue_2668(): assert isort.code(test_input, profile="black", sort_reexports=True) == expected -def test_sort_reexports_preserves_crlf_list_style_issue_2668(): +def test_sort_reexports_preserves_crlf_list_style_issue_2668() -> None: """List-style ``__all__`` reexports should preserve CRLF the same way.""" test_input = ( "from .somewhere import FirstClass, SecondClass\r\n" @@ -2434,14 +2437,14 @@ def test_sort_reexports_preserves_crlf_list_style_issue_2668(): assert isort.code(test_input, profile="black", sort_reexports=True) == expected -def test_literal_sort_action_comment_preserves_crlf_issue_2668(): +def test_literal_sort_action_comment_preserves_crlf_issue_2668() -> None: """``# isort: list`` shares the literal formatter and must keep CRLF too.""" test_input = '# isort: list\r\n__all__ = [\r\n "SecondClass",\r\n "FirstClass",\r\n]\r\n' expected = '# isort: list\r\n__all__ = [\r\n "FirstClass",\r\n "SecondClass",\r\n]\r\n' assert isort.code(test_input, profile="black") == expected -def test_noqa_added_to_long_force_single_line_as_import_with_comment_issue_2093(): +def test_noqa_added_to_long_force_single_line_as_import_with_comment_issue_2093() -> None: """A long ``as`` import with inline comment must get ``# NOQA`` in NOQA mode. With ``force_single_line`` an aliased import that carries an inline comment @@ -2466,7 +2469,7 @@ def test_noqa_added_to_long_force_single_line_as_import_with_comment_issue_2093( assert second_pass == first_pass -def test_noqa_added_to_long_as_import_with_opening_comment_issue_2093(): +def test_noqa_added_to_long_as_import_with_opening_comment_issue_2093() -> None: """A long ``as`` import with opening-line comment must get ``# NOQA`` in NOQA mode. When ``use_parentheses`` is enabled, opening-line comments are kept on the @@ -2490,7 +2493,7 @@ def test_noqa_added_to_long_as_import_with_opening_comment_issue_2093(): assert second_pass == first_pass -def test_noqa_added_to_long_combined_straight_imports_issue_2093(): +def test_noqa_added_to_long_combined_straight_imports_issue_2093() -> None: """Long combined straight imports must get ``# NOQA`` in NOQA mode. With ``combine_straight_imports`` enabled, multiple ``import`` statements @@ -2511,7 +2514,7 @@ def test_noqa_added_to_long_combined_straight_imports_issue_2093(): assert second_pass == first_pass -def test_noqa_added_to_long_straight_import_issue_2093(): +def test_noqa_added_to_long_straight_import_issue_2093() -> None: """Long straight imports must get ``# NOQA`` in NOQA mode. Straight imports (``import x`` or ``import x as y``) that exceed the line @@ -2527,7 +2530,7 @@ def test_noqa_added_to_long_straight_import_issue_2093(): assert second_pass == first_pass -def test_noqa_added_to_long_as_import_with_opening_line_comment_issue_2093(): +def test_noqa_added_to_long_as_import_with_opening_line_comment_issue_2093() -> None: """A long ``as`` import with an opening-line comment and ``use_parentheses``. When ``use_parentheses`` is enabled and the import carries an opening-line @@ -2568,7 +2571,7 @@ def test_noqa_added_to_long_as_import_with_opening_line_comment_issue_2093(): ) -def test_noqa_added_to_long_combined_straight_imports_with_bare_comment_issue_2093(): +def test_noqa_added_to_long_combined_straight_imports_with_bare_comment_issue_2093() -> None: """A bare ``#`` comment with ``combine_straight_imports`` gets ``# NOQA``. A bare ``#`` with no text parses to an empty comment string. When such a @@ -2584,7 +2587,7 @@ def test_noqa_added_to_long_combined_straight_imports_with_bare_comment_issue_20 assert first_pass == ("import a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p # # NOQA\n") -def test_isort_skip_is_honored_with_future_import_issue_2092(): +def test_isort_skip_is_honored_with_future_import_issue_2092() -> None: """A per-line ``isort: skip`` must be honored even when a ``__future__`` import is present. ``__future__`` imports are always floated to the very top, which used to make isort splice @@ -2613,7 +2616,7 @@ def test_isort_skip_is_honored_with_future_import_issue_2092(): assert isort.code(sorted_interleaved) == sorted_interleaved -def test_isort_does_not_drop_aliased_import_when_plain_name_has_a_comment(): +def test_isort_does_not_drop_aliased_import_when_plain_name_has_a_comment() -> None: """A name imported both plainly (with a trailing comment) and aliased must keep its alias when a sibling sorts ahead of it. @@ -2633,7 +2636,7 @@ def test_isort_does_not_drop_aliased_import_when_plain_name_has_a_comment(): assert isort.code(relative_sorted) == relative_sorted -def test_add_import_keeps_a_prefixed_module_docstring_first_issue_1893(): +def test_add_import_keeps_a_prefixed_module_docstring_first_issue_1893() -> None: """``add_imports`` must not move an import above a module docstring that carries a string prefix (``r``, ``b``, ``f``, ``u`` or a legal combination of them), as reported in issue #1893: https://github.com/pycqa/isort/issues/1893 @@ -2661,7 +2664,7 @@ def test_add_import_keeps_a_prefixed_module_docstring_first_issue_1893(): assert isort.code(source, add_imports=["import a"]) == source, cased + quote -def test_hanging_indent_with_parentheses_keeps_syntax_out_of_trailing_comments(): +def test_hanging_indent_with_parentheses_keeps_syntax_out_of_trailing_comments() -> None: """``multi_line_output=10`` must not append a comma or the closing parenthesis after a trailing comment, which silently rewrote valid code into code that no longer parses. @@ -2707,7 +2710,7 @@ def test_hanging_indent_with_parentheses_keeps_syntax_out_of_trailing_comments() ast.parse(output) # must never raise -def test_float_to_top_keeps_indented_semicolon_imports_in_place(): +def test_float_to_top_keeps_indented_semicolon_imports_in_place() -> None: """float_to_top must not hoist semicolon separated imports out of an indented block.""" assert ( isort.code("import os\n\nif True:\n import b; import a\n", float_to_top=True) diff --git a/tests/unit/test_settings.py b/tests/unit/test_settings.py index a0b76fe1e..ab6327aab 100644 --- a/tests/unit/test_settings.py +++ b/tests/unit/test_settings.py @@ -12,40 +12,40 @@ class TestConfig: instance = Config() - def test_init(self): + def test_init(self) -> None: assert Config() - def test_init_unsupported_settings_fails_gracefully(self): + def test_init_unsupported_settings_fails_gracefully(self) -> None: with pytest.raises(exceptions.UnsupportedSettings): Config(apply=True) with pytest.raises(exceptions.UnsupportedSettings) as error: Config(apply=True) assert error.value.unsupported_settings == {"apply": {"value": True, "source": "runtime"}} - def test_known_settings(self): + def test_known_settings(self) -> None: assert Config(known_third_party=["one"]).known_third_party == frozenset({"one"}) assert Config(known_thirdparty=["two"]).known_third_party == frozenset({"two"}) assert Config( known_third_party=["one"], known_thirdparty=["two"] ).known_third_party == frozenset({"one"}) - def test_invalid_settings_path(self): + def test_invalid_settings_path(self) -> None: with pytest.raises(exceptions.InvalidSettingsPath): Config(settings_path="this_couldnt_possibly_actually_exists/could_it") - def test_invalid_pyversion(self): + def test_invalid_pyversion(self) -> None: with pytest.raises(ValueError, match=r"The python version 10 is not supported."): Config(py_version=10) - def test_invalid_profile(self): + def test_invalid_profile(self) -> None: with pytest.raises(exceptions.ProfileDoesNotExist): Config(profile="blackandwhitestylemixedwithpep8") - def test_is_skipped(self): + def test_is_skipped(self) -> None: assert Config().is_skipped(Path("C:\\path\\isort.py")) assert Config(skip=["/path/isort.py"]).is_skipped(Path("C:\\path\\isort.py")) - def test_is_supported_filetype(self): + def test_is_supported_filetype(self) -> None: assert self.instance.is_supported_filetype("file.py") assert self.instance.is_supported_filetype("file.pyi") assert self.instance.is_supported_filetype("file.pyx") @@ -54,44 +54,44 @@ def test_is_supported_filetype(self): assert not self.instance.is_supported_filetype("file.txt") assert not self.instance.is_supported_filetype("file.pex") - def test_is_supported_filetype_ioerror(self, tmpdir): - does_not_exist = tmpdir.join("fake.txt") + def test_is_supported_filetype_ioerror(self, tmp_path: Path) -> None: + does_not_exist = tmp_path / "fake.txt" assert not self.instance.is_supported_filetype(str(does_not_exist)) - def test_is_supported_filetype_shebang(self, tmpdir): - path = tmpdir.join("myscript") - path.write("#!/usr/bin/env python\n") + def test_is_supported_filetype_shebang(self, tmp_path: Path) -> None: + path = tmp_path / "myscript" + path.write_text("#!/usr/bin/env python\n") assert self.instance.is_supported_filetype(str(path)) - def test_is_supported_filetype_editor_backup(self, tmpdir): - path = tmpdir.join("myscript~") - path.write("#!/usr/bin/env python\n") + def test_is_supported_filetype_editor_backup(self, tmp_path: Path) -> None: + path = tmp_path / "myscript~" + path.write_text("#!/usr/bin/env python\n") assert not self.instance.is_supported_filetype(str(path)) - def test_is_supported_filetype_defaults(self, tmpdir): - assert self.instance.is_supported_filetype(str(tmpdir.join("stub.pyi"))) - assert self.instance.is_supported_filetype(str(tmpdir.join("source.py"))) - assert self.instance.is_supported_filetype(str(tmpdir.join("source.pyx"))) + def test_is_supported_filetype_defaults(self, tmp_path: Path) -> None: + assert self.instance.is_supported_filetype(str(tmp_path / "stub.pyi")) + assert self.instance.is_supported_filetype(str(tmp_path / "source.py")) + assert self.instance.is_supported_filetype(str(tmp_path / "source.pyx")) - def test_is_supported_filetype_configuration(self, tmpdir): + def test_is_supported_filetype_configuration(self, tmp_path: Path) -> None: config = Config(supported_extensions=("pyx",), blocked_extensions=("py",)) - assert config.is_supported_filetype(str(tmpdir.join("stub.pyx"))) - assert not config.is_supported_filetype(str(tmpdir.join("stub.py"))) + assert config.is_supported_filetype(str(tmp_path / "stub.pyx")) + assert not config.is_supported_filetype(str(tmp_path / "stub.py")) @pytest.mark.skipif( sys.platform == "win32", reason="cannot create fifo file on Windows platform" ) - def test_is_supported_filetype_fifo(self, tmpdir): - fifo_file = os.path.join(tmpdir, "fifo_file") + def test_is_supported_filetype_fifo(self, tmp_path: Path) -> None: + fifo_file = str(tmp_path / "fifo_file") os.mkfifo(fifo_file) assert not self.instance.is_supported_filetype(fifo_file) - def test_src_paths_are_combined_and_deduplicated(self): + def test_src_paths_are_combined_and_deduplicated(self) -> None: src_paths = ["src", "tests"] src_full_paths = (Path(os.getcwd()) / f for f in src_paths) assert sorted(Config(src_paths=src_paths * 2).src_paths) == sorted(src_full_paths) - def test_src_paths_supports_glob_expansion(self, tmp_path): + def test_src_paths_supports_glob_expansion(self, tmp_path: Path) -> None: libs = tmp_path / "libs" libs.mkdir() requests = libs / "requests" @@ -102,16 +102,16 @@ def test_src_paths_supports_glob_expansion(self, tmp_path): (beautifulpasta, requests) ) - def test_deprecated_multi_line_output(self): + def test_deprecated_multi_line_output(self) -> None: assert Config(multi_line_output=6).multi_line_output == WrapModes.VERTICAL_GRID_GROUPED # noqa -def test_as_list(): +def test_as_list() -> None: assert settings._as_list([" one "]) == ["one"] assert settings._as_list("one,two") == ["one", "two"] -def _write_simple_settings(tmp_file): +def _write_simple_settings(tmp_file: Path) -> None: tmp_file.write_text( """ [isort] @@ -121,8 +121,8 @@ def _write_simple_settings(tmp_file): ) -def test_find_config(tmpdir): - tmp_config = tmpdir.join(".isort.cfg") +def test_find_config(tmp_path: Path) -> None: + tmp_config = tmp_path / ".isort.cfg" # can't find config if it has no relevant section tmp_config.write_text( @@ -132,22 +132,23 @@ def test_find_config(tmpdir): """, "utf8", ) - assert not settings._find_config(str(tmpdir))[1] + assert not settings._find_config(str(tmp_path))[1] # or if it is malformed tmp_config.write_text("""arstoyrsyan arienrsaeinrastyngpuywnlguyn354q^%$)(%_)@$""", "utf8") - assert not settings._find_config(str(tmpdir))[1] + assert not settings._find_config(str(tmp_path))[1] # can when it has either a file format, or generic relevant section _write_simple_settings(tmp_config) - assert settings._find_config(str(tmpdir))[1] + assert settings._find_config(str(tmp_path))[1] -def test_find_config_deep(tmpdir): +def test_find_config_deep(tmp_path: Path) -> None: # can't find config if it is further up than MAX_CONFIG_SEARCH_DEPTH dirs = [f"dir{i}" for i in range(settings.MAX_CONFIG_SEARCH_DEPTH + 1)] - tmp_dirs = tmpdir.ensure(*dirs, dirs=True) - tmp_config = tmpdir.join("dir0", ".isort.cfg") + tmp_dirs = tmp_path.joinpath(*dirs) + tmp_dirs.mkdir(parents=True) + tmp_config = tmp_path / "dir0" / ".isort.cfg" _write_simple_settings(tmp_config) assert not settings._find_config(str(tmp_dirs))[1] # but can find config if it is MAX_CONFIG_SEARCH_DEPTH up @@ -165,13 +166,15 @@ def test_find_config_deep(tmpdir): (None, False, False), ], ) -def test_find_config_stops_at_repository_boundary(tmp_path, marker, is_directory, is_repository): +def test_find_config_stops_at_repository_boundary( + tmp_path: Path, marker: str | None, is_directory: bool, is_repository: bool +) -> None: outer_config = tmp_path / ".isort.cfg" _write_simple_settings(outer_config) project = tmp_path / "project" nested = project / "nested" nested.mkdir(parents=True) - if is_directory: + if is_directory and marker is not None: (project / marker).mkdir() elif marker: (project / marker).write_text("gitdir: ../main/.git/worktrees/project\n", encoding="utf8") @@ -186,7 +189,9 @@ def test_find_config_stops_at_repository_boundary(tmp_path, marker, is_directory @pytest.mark.parametrize("config_location", ["outer", "project", "nested"]) -def test_find_config_in_git_worktree(tmp_path, monkeypatch, config_location): +def test_find_config_in_git_worktree( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, config_location: str +) -> None: outer_config = tmp_path / ".isort.cfg" outer_config.write_text("[settings]\nline_length = 42\n", encoding="utf8") project = tmp_path / "project" @@ -209,8 +214,8 @@ def test_find_config_in_git_worktree(tmp_path, monkeypatch, config_location): assert code(source, config=config) == source -def test_get_config_data(tmpdir): - test_config = tmpdir.join("test_config.editorconfig") +def test_get_config_data(tmp_path: Path) -> None: + test_config = tmp_path / "test_config.editorconfig" test_config.write_text( """ root = true @@ -236,18 +241,18 @@ def test_get_config_data(tmpdir): assert loaded_settings["force_grid_wrap"] == 0 assert loaded_settings["indent"] == "\t" assert isinstance(loaded_settings["source"], str) - assert str(tmpdir) in loaded_settings["source"] + assert str(tmp_path) in loaded_settings["source"] -def test_editorconfig_without_sections(tmpdir): - test_config = tmpdir.join("test_config.editorconfig") +def test_editorconfig_without_sections(tmp_path: Path) -> None: + test_config = tmp_path / "test_config.editorconfig" test_config.write_text("\nroot = true\n", "utf8") loaded_settings = settings._get_config_data(str(test_config), sections=("*.py",)) assert not loaded_settings -def test_get_config_data_with_toml_and_utf8(tmpdir): - test_config = tmpdir.join("pyproject.toml") +def test_get_config_data_with_toml_and_utf8(tmp_path: Path) -> None: + test_config = tmp_path / "pyproject.toml" # Exception: UnicodeDecodeError: 'gbk' codec can't decode byte 0x84 in position 57 test_config.write_text( """ @@ -267,10 +272,10 @@ def test_get_config_data_with_toml_and_utf8(tmpdir): ) assert loaded_settings assert isinstance(loaded_settings["source"], str) - assert str(tmpdir) in loaded_settings["source"] + assert str(tmp_path) in loaded_settings["source"] -def test_as_bool(): +def test_as_bool() -> None: assert settings._as_bool("TrUe") is True assert settings._as_bool("true") is True assert settings._as_bool("t") is True @@ -285,7 +290,7 @@ def test_as_bool(): settings._as_bool("truthy") -def test_find_all_configs(tmpdir): +def test_find_all_configs(tmp_path: Path) -> None: setup_cfg = """ [isort] profile=django @@ -306,10 +311,10 @@ def test_find_all_configs(tmpdir): something = nothing """ - dir1 = tmpdir / "subdir1" - dir2 = tmpdir / "subdir2" - dir3 = tmpdir / "subdir3" - dir4 = tmpdir / "subdir4" + dir1 = tmp_path / "subdir1" + dir2 = tmp_path / "subdir2" + dir3 = tmp_path / "subdir3" + dir4 = tmp_path / "subdir4" dir1.mkdir() dir2.mkdir() @@ -328,7 +333,7 @@ def test_find_all_configs(tmpdir): pyproject_toml_file_broken = dir4 / "pyproject.toml" pyproject_toml_file_broken.write_text(pyproject_toml_broken, "utf-8") - config_trie = settings.find_all_configs(str(tmpdir)) + config_trie = settings.find_all_configs(str(tmp_path)) config_info_1 = config_trie.search(str(dir1 / "test1.py")) assert config_info_1[0] == str(setup_cfg_file) @@ -345,5 +350,5 @@ def test_find_all_configs(tmpdir): assert config_info_3[0] == str(isort_cfg_file) assert config_info_3[1]["profile"] == "black" - config_info_4 = config_trie.search(str(tmpdir / "file4.py")) + config_info_4 = config_trie.search(str(tmp_path / "file4.py")) assert config_info_4[0] == "default" diff --git a/tests/unit/test_ticketed_features.py b/tests/unit/test_ticketed_features.py index e98f338fe..0cc8c6e5e 100644 --- a/tests/unit/test_ticketed_features.py +++ b/tests/unit/test_ticketed_features.py @@ -5,6 +5,7 @@ import warnings from functools import partial from io import StringIO +from pathlib import Path import pytest @@ -12,7 +13,7 @@ from isort import Config, exceptions -def test_semicolon_ignored_for_dynamic_lines_after_import_issue_1178(): +def test_semicolon_ignored_for_dynamic_lines_after_import_issue_1178() -> None: """Test to ensure even if a semicolon is in the decorator in the line following an import the correct line spacing determination will be made. See: https://github.com/pycqa/isort/issues/1178. @@ -29,7 +30,7 @@ def test_thing(): pass ) -def test_isort_automatically_removes_duplicate_aliases_issue_1193(): +def test_isort_automatically_removes_duplicate_aliases_issue_1193() -> None: """Test to ensure isort can automatically remove duplicate aliases. See: https://github.com/pycqa/isort/issues/1281 """ @@ -42,7 +43,7 @@ def test_isort_automatically_removes_duplicate_aliases_issue_1193(): assert isort.code("import os as os", remove_redundant_aliases=True) == "import os\n" -def test_isort_enables_floating_imports_to_top_of_module_issue_1228(): +def test_isort_enables_floating_imports_to_top_of_module_issue_1228() -> None: """Test to ensure isort will allow floating all non-indented imports to the top of a file. See: https://github.com/pycqa/isort/issues/1228. """ @@ -158,7 +159,7 @@ def my_function_2(): ) -def test_isort_provides_official_api_for_diff_output_issue_1335(): +def test_isort_provides_official_api_for_diff_output_issue_1335() -> None: """Test to ensure isort API for diff capturing allows capturing diff without sys.stdout. See: https://github.com/pycqa/isort/issues/1335. """ @@ -168,7 +169,7 @@ def test_isort_provides_official_api_for_diff_output_issue_1335(): assert "+import a" in diff_output.read() -def test_isort_warns_when_known_sections_dont_match_issue_1331(): +def test_isort_warns_when_known_sections_dont_match_issue_1331() -> None: """Test to ensure that isort warns if there is a mismatch between sections and known_sections. See: https://github.com/pycqa/isort/issues/1331. """ @@ -202,7 +203,7 @@ def test_isort_warns_when_known_sections_dont_match_issue_1331(): ) -def test_isort_supports_append_only_imports_issue_727(): +def test_isort_supports_append_only_imports_issue_727() -> None: """Test to ensure isort provides a way to only add imports as an append. See: https://github.com/pycqa/isort/issues/727. """ @@ -229,7 +230,7 @@ def test_isort_supports_append_only_imports_issue_727(): ) -def test_isort_supports_shared_profiles_issue_970(): +def test_isort_supports_shared_profiles_issue_970() -> None: """Test to ensure isort provides a way to use shared profiles. See: https://github.com/pycqa/isort/issues/970. """ @@ -239,7 +240,7 @@ def test_isort_supports_shared_profiles_issue_970(): assert isort.code("import a", profile="madeupfake") == "import a\n" # non-existent profile -def test_treating_comments_as_code_issue_1357(): +def test_treating_comments_as_code_issue_1357() -> None: """Test to ensure isort provides a way to treat comments as code. See: https://github.com/pycqa/isort/issues/1357 """ @@ -359,7 +360,7 @@ def test_treating_comments_as_code_issue_1357(): ) -def test_isort_allows_setting_import_types_issue_1181(): +def test_isort_allows_setting_import_types_issue_1181() -> None: """Test to ensure isort provides a way to set the type of imports. See: https://github.com/pycqa/isort/issues/1181 """ @@ -383,7 +384,7 @@ def test_isort_allows_setting_import_types_issue_1181(): ) -def test_isort_enables_deduping_section_headers_issue_953(): +def test_isort_enables_deduping_section_headers_issue_953() -> None: """isort should provide a way to only have identical import headings show up once. See: https://github.com/pycqa/isort/issues/953 """ @@ -418,7 +419,7 @@ def test_isort_enables_deduping_section_headers_issue_953(): assert isort_code("import os") == "import os\n" -def test_isort_doesnt_remove_as_imports_when_combine_star_issue_1380(): +def test_isort_doesnt_remove_as_imports_when_combine_star_issue_1380() -> None: """Test to ensure isort will not remove as imports along side other imports when requested to combine star imports together. See: https://github.com/PyCQA/isort/issues/1380 @@ -449,7 +450,7 @@ def test_isort_doesnt_remove_as_imports_when_combine_star_issue_1380(): ) -def test_isort_support_custom_groups_above_stdlib_that_contain_stdlib_modules_issue_1407(): +def test_isort_support_custom_groups_above_stdlib_that_contain_stdlib_modules_issue_1407() -> None: """Test to ensure it is possible to declare custom groups above standard library that include modules from the standard library. See: https://github.com/PyCQA/isort/issues/1407 @@ -468,7 +469,7 @@ def test_isort_support_custom_groups_above_stdlib_that_contain_stdlib_modules_is ) -def test_isort_intelligently_places_noqa_comments_issue_1456(): +def test_isort_intelligently_places_noqa_comments_issue_1456() -> None: assert isort.check_code( """ from my.horribly.long.import.line.that.just.keeps.on.going.and.going.and.going import ( # noqa @@ -535,14 +536,16 @@ def test_isort_intelligently_places_noqa_comments_issue_1456(): ) -def test_isort_respects_quiet_from_sort_file_api_see_1461(capsys, tmpdir): +def test_isort_respects_quiet_from_sort_file_api_see_1461( + capsys: pytest.CaptureFixture[str], tmp_path: Path +) -> None: """Test to ensure isort respects the quiet API parameter when passed in via the API. See: https://github.com/PyCQA/isort/issues/1461. """ - settings_file = tmpdir.join(".isort.cfg") - custom_settings_file = tmpdir.join(".custom.isort.cfg") - tmp_file = tmpdir.join("file.py") - tmp_file.write("import b\nimport a\n") + settings_file = tmp_path / ".isort.cfg" + custom_settings_file = tmp_path / ".custom.isort.cfg" + tmp_file = tmp_path / "file.py" + tmp_file.write_text("import b\nimport a\n") isort.file(tmp_file) out, error = capsys.readouterr() @@ -550,39 +553,39 @@ def test_isort_respects_quiet_from_sort_file_api_see_1461(capsys, tmpdir): assert "Fixing" in out # When passed in directly as a setting override - tmp_file.write("import b\nimport a\n") + tmp_file.write_text("import b\nimport a\n") isort.file(tmp_file, quiet=True) out, error = capsys.readouterr() assert not error assert not out # Present in an automatically loaded configuration file - settings_file.write( + settings_file.write_text( """ [isort] quiet = true """ ) - tmp_file.write("import b\nimport a\n") + tmp_file.write_text("import b\nimport a\n") isort.file(tmp_file) out, error = capsys.readouterr() assert not error assert not out # In a custom configuration file - settings_file.write( + settings_file.write_text( """ [isort] quiet = false """ ) - custom_settings_file.write( + custom_settings_file.write_text( """ [isort] quiet = true """ ) - tmp_file.write("import b\nimport a\n") + tmp_file.write_text("import b\nimport a\n") isort.file(tmp_file, settings_file=str(custom_settings_file)) out, error = capsys.readouterr() assert not error @@ -596,13 +599,13 @@ def test_isort_respects_quiet_from_sort_file_api_see_1461(capsys, tmpdir): assert not out -def test_isort_should_warn_on_empty_custom_config_issue_1433(tmpdir): +def test_isort_should_warn_on_empty_custom_config_issue_1433(tmp_path: Path) -> None: """Feedback should be provided when a user provides a custom settings file that has no discoverable configuration. See: https://github.com/PyCQA/isort/issues/1433 """ - settings_file = tmpdir.join(".custom.cfg") - settings_file.write( + settings_file = tmp_path / ".custom.cfg" + settings_file.write_text( """ [settings] quiet = true @@ -611,7 +614,7 @@ def test_isort_should_warn_on_empty_custom_config_issue_1433(tmpdir): with pytest.warns(UserWarning): assert not Config(settings_file=str(settings_file)).quiet - settings_file.write( + settings_file.write_text( """ [isort] quiet = true @@ -622,7 +625,7 @@ def test_isort_should_warn_on_empty_custom_config_issue_1433(tmpdir): assert Config(settings_file=str(settings_file)).quiet -def test_float_to_top_should_respect_existing_newlines_between_imports_issue_1502(): +def test_float_to_top_should_respect_existing_newlines_between_imports_issue_1502() -> None: """When a file has an existing top of file import block before code but after comments isort's float to top feature should respect the existing spacing between the top file comment and the import statements. @@ -722,19 +725,21 @@ def my_function(): ) -def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): +def test_api_to_allow_custom_diff_and_output_stream_1583( + capsys: pytest.CaptureFixture[str], tmp_path: Path +) -> None: """isort should provide a way from the Python API to process an existing file and output to a stream the new version of that file, as well as a diff to a different stream. See: https://github.com/PyCQA/isort/issues/1583 """ - tmp_file = tmpdir.join("file.py") - tmp_file.write("import b\nimport a\n") + tmp_file = tmp_path / "file.py" + tmp_file.write_text("import b\nimport a\n") isort_diff = StringIO() isort_output = StringIO() - isort.file(tmp_file, show_diff=isort_diff, output=isort_output) + isort.file(str(tmp_file), show_diff=isort_diff, output=isort_output) _, error = capsys.readouterr() assert not error @@ -749,13 +754,13 @@ def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): assert isort_output.read().splitlines() == ["import a", "import b"] # should still work with no diff produced - tmp_file2 = tmpdir.join("file2.py") - tmp_file2.write("import a\nimport b\n") + tmp_file2 = tmp_path / "file2.py" + tmp_file2.write_text("import a\nimport b\n") isort_diff2 = StringIO() isort_output2 = StringIO() - isort.file(tmp_file2, show_diff=isort_diff2, output=isort_output2) + isort.file(str(tmp_file2), show_diff=isort_diff2, output=isort_output2) _, error = capsys.readouterr() assert not error @@ -764,7 +769,7 @@ def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir): assert not isort_diff2.read() -def test_autofix_mixed_indent_imports_1575(): +def test_autofix_mixed_indent_imports_1575() -> None: """isort should automatically fix import statements that are sent in with incorrect mixed indentation. See: https://github.com/PyCQA/isort/issues/1575 @@ -811,7 +816,7 @@ def one(): ) -def test_indented_import_headings_issue_1604(): +def test_indented_import_headings_issue_1604() -> None: """Test to ensure it is possible to toggle import headings on indented import sections See: https://github.com/PyCQA/isort/issues/1604 """ @@ -859,7 +864,7 @@ def function(): ) -def test_isort_auto_detects_and_ignores_invalid_from_imports_issue_1688(): +def test_isort_auto_detects_and_ignores_invalid_from_imports_issue_1688() -> None: """isort should automatically detect and ignore incorrectly written from import statements see: https://github.com/PyCQA/isort/issues/1688 """ @@ -880,7 +885,7 @@ def test_isort_auto_detects_and_ignores_invalid_from_imports_issue_1688(): ) -def test_isort_allows_reversing_sort_order_issue_1645(): +def test_isort_allows_reversing_sort_order_issue_1645() -> None: """isort allows reversing the sort order for those who prefer Z or longer imports first. see: https://github.com/PyCQA/isort/issues/1688 """ @@ -910,7 +915,7 @@ def test_isort_allows_reversing_sort_order_issue_1645(): ) -def test_isort_can_push_star_imports_above_others_issue_1504(): +def test_isort_can_push_star_imports_above_others_issue_1504() -> None: """isort should provide a way to push star imports above other imports to avoid explicit imports from being overwritten. see: https://github.com/PyCQA/isort/issues/1504 @@ -933,7 +938,7 @@ def test_isort_can_push_star_imports_above_others_issue_1504(): ) -def test_isort_can_combine_reverse_sort_with_force_sort_within_sections_issue_1726(): +def test_isort_can_combine_reverse_sort_with_force_sort_within_sections_issue_1726() -> None: """isort should support reversing import order even with force sort within sections turned on. See: https://github.com/PyCQA/isort/issues/1726 """ @@ -966,7 +971,7 @@ def test_isort_can_combine_reverse_sort_with_force_sort_within_sections_issue_17 ) -def test_isort_can_turn_off_import_adds_with_action_comment_issue_1737(): +def test_isort_can_turn_off_import_adds_with_action_comment_issue_1737() -> None: assert ( isort.code( """ @@ -1058,7 +1063,7 @@ def test_sort_configurable_sort_issue_1732() -> None: isort.code(test_input, sort_order="round") -def test_cython_pure_python_imports_2062(): +def test_cython_pure_python_imports_2062() -> None: """Test to ensure an import form a cython.cimports remains import, not cimport. See: https://github.com/pycqa/isort/issues/2062. """ @@ -1075,7 +1080,7 @@ def use_libc_math(): ) -def test_unindented_comment_in_indented_block_issue_1899(): +def test_unindented_comment_in_indented_block_issue_1899() -> None: """Test that unindented comments before indented imports are not corrupted. See: https://github.com/PyCQA/isort/issues/1899 @@ -1089,7 +1094,7 @@ def test_unindented_comment_in_indented_block_issue_1899(): assert isort.code(test_input) == test_input -def test_sort_separate_packages_issue_2104(): +def test_sort_separate_packages_issue_2104() -> None: """ Test to ensure that packages within a section can be separated by blank lines. See: https://github.com/PyCQA/isort/issues/2104 diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 0bc7a0fe8..b38b50369 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1,7 +1,7 @@ from isort.utils import Trie -def test_trie(): +def test_trie() -> None: trie_root = Trie("default", {"line_length": 70}) trie_root.insert("/temp/config1/.isort.cfg", {"line_length": 71}) diff --git a/tests/unit/test_wrap.py b/tests/unit/test_wrap.py index 1645f8eca..d3d4bce0d 100644 --- a/tests/unit/test_wrap.py +++ b/tests/unit/test_wrap.py @@ -5,7 +5,7 @@ from isort.wrap_modes import WrapModes -def test_import_statement(): +def test_import_statement() -> None: assert wrap.import_statement("", [], []) == "" assert ( wrap.import_statement("from x import ", ["y"], [], config=Config(balanced_wrapping=True)) @@ -37,7 +37,9 @@ def test_import_statement(): ), ], ) -def test_line__comment_with_brackets__expects_unchanged_comment(multi_line_output, expected): +def test_line__comment_with_brackets__expects_unchanged_comment( + multi_line_output: WrapModes, expected: str +) -> None: content = ( "from a import b as c " "# comment that is long enough that this import doesn't fit in one line (parens)" diff --git a/tests/unit/test_wrap_modes.py b/tests/unit/test_wrap_modes.py index 3497679b5..76c901a9f 100644 --- a/tests/unit/test_wrap_modes.py +++ b/tests/unit/test_wrap_modes.py @@ -7,13 +7,13 @@ from isort.wrap_modes import WrapModes, _wrap_modes -def test_wrap_mode_interface(): +def test_wrap_mode_interface() -> None: assert ( wrap_modes._wrap_mode_interface("statement", [], "", "", 80, [], "", "", True, True) == "" ) -def test_auto_saved(): +def test_auto_saved() -> None: """hypothesis_auto tests cases that have been saved to ensure they run each test cycle""" assert ( wrap_modes.noqa( @@ -62,7 +62,7 @@ def test_auto_saved(): ) -def test_backslash_grid(): +def test_backslash_grid() -> None: """Tests the backslash_grid grid wrap mode, ensuring it matches formatting expectations. See: https://github.com/PyCQA/isort/issues/1434 """ @@ -96,7 +96,7 @@ def test_vertical_grid_size_near_line_length( multi_line_output: int, line_length: int, include_trailing_comma: bool, -): +) -> None: separator = " " # Cases where the input should be wrapped: if ( @@ -144,17 +144,17 @@ def test_vertical_grid_size_near_line_length( remove_comments=st.booleans(), ) def test_fuzz_backslash_grid( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.backslash_grid( statement=statement, @@ -185,17 +185,17 @@ def test_fuzz_backslash_grid( remove_comments=st.booleans(), ) def test_fuzz_grid( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.grid( statement=statement, @@ -226,17 +226,17 @@ def test_fuzz_grid( remove_comments=st.booleans(), ) def test_fuzz_hanging_indent( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.hanging_indent( statement=statement, @@ -255,7 +255,9 @@ def test_fuzz_hanging_indent( @pytest.mark.parametrize("include_trailing_comma", [True, False]) -def test_hanging_indent__with_include_trailing_comma__expect_same_result(include_trailing_comma): +def test_hanging_indent__with_include_trailing_comma__expect_same_result( + include_trailing_comma: bool, +) -> None: result = isort.wrap_modes.hanging_indent( statement="from datetime import ", imports=["datetime", "time", "timedelta", "timezone", "tzinfo"], @@ -285,17 +287,17 @@ def test_hanging_indent__with_include_trailing_comma__expect_same_result(include remove_comments=st.booleans(), ) def test_fuzz_hanging_indent_with_parentheses( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.hanging_indent_with_parentheses( statement=statement, @@ -326,17 +328,17 @@ def test_fuzz_hanging_indent_with_parentheses( remove_comments=st.booleans(), ) def test_fuzz_noqa( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.noqa( statement=statement, @@ -367,17 +369,17 @@ def test_fuzz_noqa( remove_comments=st.booleans(), ) def test_fuzz_vertical( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.vertical( statement=statement, @@ -408,17 +410,17 @@ def test_fuzz_vertical( remove_comments=st.booleans(), ) def test_fuzz_vertical_grid( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.vertical_grid( statement=statement, @@ -449,17 +451,17 @@ def test_fuzz_vertical_grid( remove_comments=st.booleans(), ) def test_fuzz_vertical_grid_grouped( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.vertical_grid_grouped( statement=statement, @@ -490,17 +492,17 @@ def test_fuzz_vertical_grid_grouped( remove_comments=st.booleans(), ) def test_fuzz_vertical_hanging_indent( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.vertical_hanging_indent( statement=statement, @@ -531,17 +533,17 @@ def test_fuzz_vertical_hanging_indent( remove_comments=st.booleans(), ) def test_fuzz_vertical_hanging_indent_bracket( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.vertical_hanging_indent_bracket( statement=statement, @@ -572,17 +574,17 @@ def test_fuzz_vertical_hanging_indent_bracket( remove_comments=st.booleans(), ) def test_fuzz_vertical_prefix_from_module_import( - statement, - imports, - white_space, - indent, - line_length, - comments, - line_separator, - comment_prefix, - include_trailing_comma, - remove_comments, -): + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> None: try: isort.wrap_modes.vertical_prefix_from_module_import( statement=statement, @@ -600,7 +602,7 @@ def test_fuzz_vertical_prefix_from_module_import( reject() -def test_enum_matches_order_of_definitions(): +def test_enum_matches_order_of_definitions() -> None: """Before isort 9.0.0 WrapModes was dynamically generated, this tests for breaking changes.""" old_enum_logic = {wrap_mode: index for index, wrap_mode in enumerate(_wrap_modes.keys())} diff --git a/tests/unit/utils.py b/tests/unit/utils.py index 3b00ced93..f6fdd421c 100644 --- a/tests/unit/utils.py +++ b/tests/unit/utils.py @@ -1,15 +1,16 @@ from io import BytesIO, StringIO, TextIOWrapper +from typing import Any import isort class UnseekableTextIOWrapper(TextIOWrapper): - def seek(self, *args, **kwargs): + def seek(self, offset: int, whence: int = 0, /) -> int: raise ValueError("underlying stream is not seekable") class UnreadableStream(StringIO): - def readable(self, *args, **kwargs) -> bool: + def readable(self, *args: object, **kwargs: object) -> bool: return False @@ -17,7 +18,7 @@ def as_stream(text: str) -> UnseekableTextIOWrapper: return UnseekableTextIOWrapper(BytesIO(text.encode("utf8"))) -def isort_test(code: str, expected_output: str = "", **config): +def isort_test(code: str, expected_output: str = "", **config: Any) -> None: """Runs isort against the given code snippet and ensures that it gives consistent output across multiple runs, and if an expected_output is given - that it matches that.