diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a797854..3f6bbb2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,7 +96,8 @@ jobs: - "3.11" - "3.12" - "3.13" - - "pypy3.10" + - "3.14" + - "pypy3.11" include: - os: macos-latest python: "3.8" diff --git a/dwasfile.py b/dwasfile.py index ef3fbdf..9284569 100644 --- a/dwasfile.py +++ b/dwasfile.py @@ -11,7 +11,16 @@ DOCS_REQUIREMENTS = "-rrequirements/requirements-docs.txt" TEST_REQUIREMENTS = "-rrequirements/requirements-test.txt" TYPES_REQUIREMENTS = "-rrequirements/requirements-types.txt" -SUPPORTED_PYTHONS = ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "pypy3.10"] +SUPPORTED_PYTHONS = [ + "3.8", + "3.9", + "3.10", + "3.11", + "3.12", + "3.13", + "3.14", + "pypy3.11", +] OLDEST_SUPPORTED_PYTHON = SUPPORTED_PYTHONS[0] PYTHON_FILES = [ "docs/conf.py", @@ -31,13 +40,13 @@ # Formatting ## dwas.register_managed_step( - dwas.predefined.unimport(), + dwas.predefined.unimport(files=PYTHON_FILES), description="Show which imports are unnecessary", ) dwas.register_managed_step(dwas.predefined.isort(files=PYTHON_FILES)) dwas.register_managed_step( dwas.predefined.docformatter(files=PYTHON_FILES), - dependencies=["docformatter[tomli]<1.7.1"], + dependencies=["docformatter[tomli]"], ) dwas.register_managed_step(dwas.predefined.black()) dwas.register_step_group( @@ -47,6 +56,7 @@ # With auto fix dwas.register_managed_step( dwas.predefined.unimport( + files=PYTHON_FILES, additional_arguments=["--diff", "--remove", "--check", "--gitignore"], ), name="unimport:fix", @@ -68,7 +78,7 @@ ), name="docformatter:fix", run_by_default=False, - dependencies=["docformatter[tomli]<1.7.1"], + dependencies=["docformatter[tomli]"], requires=["isort:fix"], ) dwas.register_managed_step( @@ -83,7 +93,6 @@ additional_arguments=["check", "--fix", "--show-fixes", "--fix-only"], ), dependencies=["ruff"], - python=OLDEST_SUPPORTED_PYTHON, name="ruff:fix", requires=["black:fix"], run_by_default=False, diff --git a/pyproject.toml b/pyproject.toml index ea19140..d59af42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Testing", diff --git a/src/dwas/_io.py b/src/dwas/_io.py index b422df7..d821ba8 100644 --- a/src/dwas/_io.py +++ b/src/dwas/_io.py @@ -95,10 +95,14 @@ def flush( class StreamHandler(io.TextIOWrapper): def __init__( - self, var: ContextVar[TextIO], log_var: ContextVar[TextIO] + self, + var: ContextVar[TextIO], + log_var: ContextVar[TextIO], + fileno: int, ) -> None: self._var = var self._log_var = log_var + self._fileno = fileno def read(self, size: int | None = None) -> str: # noqa: ARG002 raise io.UnsupportedOperation("can't read from a memorypipe") @@ -117,6 +121,9 @@ def flush(self) -> None: fd.flush() self._var.get().flush() + def fileno(self) -> int: + return self._fileno + @contextmanager def instrument_streams() -> Generator[None, None, None]: @@ -124,9 +131,9 @@ def instrument_streams() -> Generator[None, None, None]: STDERR.set(sys.stderr) LOG_FILE.set(NoOpWriter()) - with redirect_stdout(StreamHandler(STDOUT, LOG_FILE)), redirect_stderr( - StreamHandler(STDERR, LOG_FILE) - ): + with redirect_stdout( + StreamHandler(STDOUT, LOG_FILE, sys.stdout.fileno()) + ), redirect_stderr(StreamHandler(STDERR, LOG_FILE, sys.stderr.fileno())): yield diff --git a/src/dwas/_steps/parametrize.py b/src/dwas/_steps/parametrize.py index 32ed08f..92ddcc5 100644 --- a/src/dwas/_steps/parametrize.py +++ b/src/dwas/_steps/parametrize.py @@ -304,7 +304,7 @@ def build_parameters(**kwargs: Any) -> Callable[[T], T]: def extract_parameters( - func: Callable[..., Any] + func: Callable[..., Any], ) -> list[tuple[str, dict[str, Any]]]: defaults = getattr(func, _DEFAULTS, {}) diff --git a/src/dwas/_steps/steps.py b/src/dwas/_steps/steps.py index 3f7f943..09ee60e 100644 --- a/src/dwas/_steps/steps.py +++ b/src/dwas/_steps/steps.py @@ -258,11 +258,9 @@ def setup_dependent( Run some logic into a dependent step. :param original_step: The original step handler that was used - when the step defining this method was - called. + when the step defining this method was called. :param current_step: The current step handler, that contains the - context of the step that is going to be - executed. + context of the step that is going to be executed. """ @@ -352,11 +350,10 @@ def gather_artifacts(self, step: StepRunner) -> dict[str, list[Any]]: Gather all artifacts exposed by this step. :param step: The step handler that was used when running the - step. + step. :return: A dictionary of artifact key to a list of arbitrary - data. This **must** return a list, as they are merged - with other steps' artifacts into a single list per - artifact key. + data. This **must** return a list, as they are merged with + other steps' artifacts into a single list per artifact key. """ diff --git a/tests/predefined/test_docformatter.py b/tests/predefined/test_docformatter.py index ec6504b..be6840d 100644 --- a/tests/predefined/test_docformatter.py +++ b/tests/predefined/test_docformatter.py @@ -1,3 +1,5 @@ +import sys + import pytest from .mixins import BaseLinterWithAutofixTest @@ -25,3 +27,29 @@ class TestDocformatter(BaseLinterWithAutofixTest): @pytest.mark.skip("docformatter does not support colored output") def test_respects_color_settings(self): pass # pragma: nocover + + @pytest.mark.parametrize( + "valid", + ( + pytest.param( + True, + id="valid-project", + marks=pytest.mark.xfail( + sys.version_info >= (3, 14), + reason="docformatter does not support python3.14 yet", + strict=True, + ), + ), + pytest.param(False, id="invalid-project"), + ), + ) + def test_simple_behavior(self, cache_path, tmp_path, valid): + return super().test_simple_behavior(cache_path, tmp_path, valid) + + @pytest.mark.xfail( + sys.version_info >= (3, 14), + reason="docformatter does not support python3.14 yet", + strict=True, + ) + def test_can_apply_fixes(self, cache_path, tmp_path): + return super().test_can_apply_fixes(cache_path, tmp_path)