From d91abf2c8fdb6ca7c89cd18c90bcb666c09b951f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Wed, 8 Apr 2026 20:40:51 +0300 Subject: [PATCH 01/12] Add PyPy support and update Python classifiers --- .github/workflows/tests_and_coverage.yml | 27 ++++++++++++++++++++---- pyproject.toml | 2 ++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests_and_coverage.yml b/.github/workflows/tests_and_coverage.yml index 41d3a91..cc39034 100644 --- a/.github/workflows/tests_and_coverage.yml +++ b/.github/workflows/tests_and_coverage.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.14t', '3.15.0-alpha.1'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.14t', '3.15.0-alpha.1', 'pypy3.9', 'pypy3.10', 'pypy3.11'] steps: - uses: actions/checkout@v4 @@ -25,22 +25,41 @@ jobs: python-version: ${{ matrix.python-version }} - name: Set up uv + if: "!startsWith(matrix.python-version, 'pypy')" uses: astral-sh/setup-uv@v7 with: enable-cache: true - - name: Install dependencies + - name: Install dependencies (uv) + if: "!startsWith(matrix.python-version, 'pypy')" shell: bash run: uv pip install --system -r requirements_dev.txt - - name: Install the library + - name: Install dependencies (pip) + if: startsWith(matrix.python-version, 'pypy') + shell: bash + run: pip install -r requirements_dev.txt + + - name: Install the library (uv) + if: "!startsWith(matrix.python-version, 'pypy')" shell: bash run: uv pip install --system . - - name: Print all libs + - name: Install the library (pip) + if: startsWith(matrix.python-version, 'pypy') + shell: bash + run: pip install . + + - name: Print all libs (uv) + if: "!startsWith(matrix.python-version, 'pypy')" shell: bash run: uv pip list --system + - name: Print all libs (pip) + if: startsWith(matrix.python-version, 'pypy') + shell: bash + run: pip list + - name: Run tests and show the branch coverage on the command line shell: bash run: | diff --git a/pyproject.toml b/pyproject.toml index 0b125b0..c7706f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,8 @@ classifiers = [ 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', 'Programming Language :: Python :: 3.15', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', 'Programming Language :: Python :: Free Threading', 'Programming Language :: Python :: Free Threading :: 3 - Stable', 'License :: OSI Approved :: MIT License', From f696f74c9787531adf80872b78cf1030d0b50330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Wed, 8 Apr 2026 21:01:57 +0300 Subject: [PATCH 02/12] Fix pypy install by installing greenlet first --- .github/workflows/tests_and_coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests_and_coverage.yml b/.github/workflows/tests_and_coverage.yml index cc39034..b7cf6fd 100644 --- a/.github/workflows/tests_and_coverage.yml +++ b/.github/workflows/tests_and_coverage.yml @@ -38,7 +38,7 @@ jobs: - name: Install dependencies (pip) if: startsWith(matrix.python-version, 'pypy') shell: bash - run: pip install -r requirements_dev.txt + run: pip install --ignore-installed greenlet && pip install -r requirements_dev.txt - name: Install the library (uv) if: "!startsWith(matrix.python-version, 'pypy')" From 2dc1517d3022e5619c7a7555d3af9110ea0a01d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Wed, 8 Apr 2026 21:37:07 +0300 Subject: [PATCH 03/12] Fix pypy install order to ensure greenlet is installed before uv --- .github/workflows/tests_and_coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests_and_coverage.yml b/.github/workflows/tests_and_coverage.yml index b7cf6fd..cfe62cd 100644 --- a/.github/workflows/tests_and_coverage.yml +++ b/.github/workflows/tests_and_coverage.yml @@ -38,7 +38,7 @@ jobs: - name: Install dependencies (pip) if: startsWith(matrix.python-version, 'pypy') shell: bash - run: pip install --ignore-installed greenlet && pip install -r requirements_dev.txt + run: pip install --ignore-installed -r requirements_dev.txt - name: Install the library (uv) if: "!startsWith(matrix.python-version, 'pypy')" From 47dbdb37a7023142b9e8f9b8e2e6bc0c11f7c983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Wed, 8 Apr 2026 21:59:27 +0300 Subject: [PATCH 04/12] Fix UTF-8 test to use Unicode escape sequence --- tests/units/test_run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/units/test_run.py b/tests/units/test_run.py index 30957a7..6a1af04 100644 --- a/tests/units/test_run.py +++ b/tests/units/test_run.py @@ -2434,7 +2434,7 @@ def test_utf8_output_accepts_non_ascii_text(fd, result_attr): result = run( sys.executable, '-c', - f'import os; os.write({fd}, "привет\\n".encode("utf-8"))', + f'import os; os.write({fd}, "\\u043f\\u0440\\u0438\\u0432\\u0435\\u0442\\n".encode("utf-8"))', split=False, catch_output=True, ) From 99c193709f657307587fd0747fe4d5f6258cbbbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Thu, 9 Apr 2026 00:35:09 +0300 Subject: [PATCH 05/12] Add PyPy support for Darwin kqueue event-driven waiter --- suby/process_waiting.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/suby/process_waiting.py b/suby/process_waiting.py index 746ff27..c6e57dd 100644 --- a/suby/process_waiting.py +++ b/suby/process_waiting.py @@ -23,10 +23,10 @@ def _wait_pidfd(pid: int, timeout_seconds: Optional[float]) -> None: _event_driven_waiter = _wait_pidfd elif sys.platform == 'darwin': # pragma: no cover (!Darwin) - if not hasattr(select, 'kqueue'): # pragma: no cover (Darwin) + if not hasattr(select, 'kqueue') or sys.implementation.name == 'pypy': # pragma: no cover (Darwin and !PyPy) _event_driven_waiter = None else: - def _wait_kqueue(pid: int, timeout_seconds: Optional[float]) -> None: # pragma: no cover (!Darwin) + def _wait_kqueue(pid: int, timeout_seconds: Optional[float]) -> None: # pragma: no cover (!Darwin or PyPy) kq = select.kqueue() try: ev = select.kevent( @@ -39,7 +39,7 @@ def _wait_kqueue(pid: int, timeout_seconds: Optional[float]) -> None: # pragma: finally: kq.close() - _event_driven_waiter = _wait_kqueue + _event_driven_waiter = _wait_kqueue # pragma: no cover (!Darwin or PyPy) def has_event_driven_wait() -> bool: @@ -50,7 +50,7 @@ def wait_for_process_exit(process: 'Popen[str]', timeout_seconds: Optional[float if _event_driven_waiter is not None: try: _event_driven_waiter(process.pid, timeout_seconds) - return # pragma: no cover (Windows or (Linux and Date: Thu, 9 Apr 2026 00:36:42 +0300 Subject: [PATCH 06/12] Bumped version to 0.0.6 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c7706f9..2d4ef0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "suby" -version = "0.0.5" +version = "0.0.6" authors = [ { name="Evgeniy Blinov", email="zheni-b@yandex.ru" }, ] From 783e1061e5de8fd0b802631d4d762f083aae67f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Thu, 9 Apr 2026 13:22:09 +0300 Subject: [PATCH 07/12] Update test workflow to use pytest-cov for coverage reporting --- .github/workflows/tests_and_coverage.yml | 9 +-------- requirements_dev.txt | 1 + tests/conftest.py | 10 ---------- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/.github/workflows/tests_and_coverage.yml b/.github/workflows/tests_and_coverage.yml index cfe62cd..6b2478b 100644 --- a/.github/workflows/tests_and_coverage.yml +++ b/.github/workflows/tests_and_coverage.yml @@ -62,14 +62,7 @@ jobs: - name: Run tests and show the branch coverage on the command line shell: bash - run: | - pth_file="$(python -c 'import sysconfig; print(sysconfig.get_path("purelib"))')/suby_coverage_process_startup.pth" - printf "import os; os.getenv('COVERAGE_PROCESS_START') and __import__('coverage').process_startup()\n" > "$pth_file" - coverage erase - COVERAGE_PROCESS_START="$PWD/pyproject.toml" coverage run -m pytest -n auto --cache-clear --assert=plain - coverage combine - coverage report -m --fail-under=100 --omit='*tests*' - coverage xml --omit='*tests*' + run: pytest --cov=suby --cov-branch --cov-report=term-missing --cov-report=xml:coverage.xml --cov-fail-under=100 -n auto --cache-clear --assert=plain - name: Upload coverage to Coveralls if: runner.os == 'Linux' && matrix.python-version == '3.13' diff --git a/requirements_dev.txt b/requirements_dev.txt index 47af4f7..49f88af 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -3,6 +3,7 @@ pytest-xdist==3.6.1; python_version < '3.9' pytest-xdist==3.8.0; python_version >= '3.9' coverage==7.6.1 coverage-pyver-pragma==0.4.0 +pytest-cov==6.1.1 build==1.2.2.post1 mypy==1.14.1 pytest-mypy-testing==0.1.3 diff --git a/tests/conftest.py b/tests/conftest.py index 09a3483..1e31942 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,16 +9,6 @@ _run_module = importlib.import_module('suby.run') -def pytest_configure(config): - """Keep coverage enabled for xdist workers, but disable inheritance by child `run(... python -c ...)` subprocesses. - - If this hook is removed, every short-lived Python subprocess spawned by the test suite will inherit - `COVERAGE_PROCESS_START`, import coverage from the `.pth` bootstrap, and the parallel coverage run will become - significantly slower again. - """ - if hasattr(config, 'workerinput'): - os.environ.pop('COVERAGE_PROCESS_START', None) - def pytest_ignore_collect(collection_path, config): """Skip typing-snippet tests in mutmut's copied test tree, because mutmut is a runtime mutation runner.""" From bb759250afa02520bd9d6977e2ed8e78d1533d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Thu, 9 Apr 2026 13:38:11 +0300 Subject: [PATCH 08/12] Update pytest-cov version based on Python version --- requirements_dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index 49f88af..3a91697 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -3,7 +3,8 @@ pytest-xdist==3.6.1; python_version < '3.9' pytest-xdist==3.8.0; python_version >= '3.9' coverage==7.6.1 coverage-pyver-pragma==0.4.0 -pytest-cov==6.1.1 +pytest-cov==5.0.0; python_version < '3.9' +pytest-cov==6.1.1; python_version >= '3.9' build==1.2.2.post1 mypy==1.14.1 pytest-mypy-testing==0.1.3 From 85410db183de288f42f525f5121e84944aa5af60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Thu, 9 Apr 2026 13:53:26 +0300 Subject: [PATCH 09/12] Fix pytest coverage inheritance in child processes --- tests/conftest.py | 12 ++++++++++++ tests/units/test_process_waiting.py | 3 +++ 2 files changed, 15 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 1e31942..c0b9c8b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,6 +9,18 @@ _run_module = importlib.import_module('suby.run') +def pytest_configure(config): + """Prevent child processes spawned by run() from inheriting pytest-cov's subprocess coverage. + + Without this, every short-lived Python subprocess spawned by the test suite inherits COV_CORE_* + environment variables, causing pytest-cov's .pth hook to activate coverage in each child process. + """ + if hasattr(config, 'workerinput'): + for key in list(os.environ): + if key.startswith('COV_CORE_'): + os.environ.pop(key) + + def pytest_ignore_collect(collection_path, config): """Skip typing-snippet tests in mutmut's copied test tree, because mutmut is a runtime mutation runner.""" diff --git a/tests/units/test_process_waiting.py b/tests/units/test_process_waiting.py index b07c650..450a114 100644 --- a/tests/units/test_process_waiting.py +++ b/tests/units/test_process_waiting.py @@ -70,6 +70,9 @@ def _load_process_waiting_for_platform(monkeypatch, platform_value, pidfd_open, module = importlib.util.module_from_spec(spec) monkeypatch.setattr(sys, 'platform', platform_value) + fake_implementation = type(sys.implementation)(**{**vars(sys.implementation), 'name': 'cpython'}) + monkeypatch.setattr(sys, 'implementation', fake_implementation) + if pidfd_open is None: monkeypatch.delattr(os, 'pidfd_open', raising=False) else: From 128f0a9ee82ff47b9e616a14a503e2544aaa9b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Thu, 9 Apr 2026 14:12:00 +0300 Subject: [PATCH 10/12] Fix test isolation and coverage for PyPy and macOS --- .github/workflows/tests_and_coverage.yml | 15 ++++++++++++++- tests/conftest.py | 8 +++++--- tests/units/test_process_waiting.py | 11 ++++++----- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests_and_coverage.yml b/.github/workflows/tests_and_coverage.yml index 6b2478b..64411b5 100644 --- a/.github/workflows/tests_and_coverage.yml +++ b/.github/workflows/tests_and_coverage.yml @@ -60,7 +60,20 @@ jobs: shell: bash run: pip list - - name: Run tests and show the branch coverage on the command line + - name: Run tests and show the branch coverage on the command line (CPython) + if: "!startsWith(matrix.python-version, 'pypy')" + shell: bash + run: | + pth_file="$(python -c 'import sysconfig; print(sysconfig.get_path("purelib"))')/suby_coverage_process_startup.pth" + printf "import os; os.getenv('COVERAGE_PROCESS_START') and __import__('coverage').process_startup()\n" > "$pth_file" + coverage erase + COVERAGE_PROCESS_START="$PWD/pyproject.toml" coverage run -m pytest -p no:cov -n auto --cache-clear --assert=plain + coverage combine + coverage report -m --fail-under=100 --omit='*tests*' + coverage xml --omit='*tests*' + + - name: Run tests and show the branch coverage on the command line (PyPy) + if: startsWith(matrix.python-version, 'pypy') shell: bash run: pytest --cov=suby --cov-branch --cov-report=term-missing --cov-report=xml:coverage.xml --cov-fail-under=100 -n auto --cache-clear --assert=plain diff --git a/tests/conftest.py b/tests/conftest.py index c0b9c8b..7d49cf4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,12 +10,14 @@ def pytest_configure(config): - """Prevent child processes spawned by run() from inheriting pytest-cov's subprocess coverage. + """Prevent child processes spawned by run() from inheriting subprocess coverage hooks. - Without this, every short-lived Python subprocess spawned by the test suite inherits COV_CORE_* - environment variables, causing pytest-cov's .pth hook to activate coverage in each child process. + Without this, every short-lived Python subprocess spawned by the test suite inherits coverage + environment variables (COVERAGE_PROCESS_START for coverage run, COV_CORE_* for pytest-cov), + causing coverage to activate in each child process and slowing the suite significantly. """ if hasattr(config, 'workerinput'): + os.environ.pop('COVERAGE_PROCESS_START', None) for key in list(os.environ): if key.startswith('COV_CORE_'): os.environ.pop(key) diff --git a/tests/units/test_process_waiting.py b/tests/units/test_process_waiting.py index 450a114..b1b32ae 100644 --- a/tests/units/test_process_waiting.py +++ b/tests/units/test_process_waiting.py @@ -27,6 +27,7 @@ _is_event_driven_platform = has_event_driven_wait() _is_macos = sys.platform == 'darwin' _is_linux = sys.platform == 'linux' +_is_pypy = sys.implementation.name == 'pypy' _has_pidfd = hasattr(os, 'pidfd_open') _COORDINATION_TIMEOUT_SECONDS = 5.0 @@ -207,7 +208,7 @@ def test_oserror_fallback_with_reaped_pid(): @pytest.mark.parametrize( 'expected', [ - pytest.param(True, marks=pytest.mark.skipif(not _is_macos, reason='macOS only')), + pytest.param(True, marks=pytest.mark.skipif(not _is_macos or _is_pypy, reason='macOS CPython only')), pytest.param(True, marks=pytest.mark.skipif(not (_is_linux and _has_pidfd), reason='Linux 3.9+ only')), pytest.param(False, marks=pytest.mark.skipif(_is_event_driven_platform, reason='Only for fallback platforms')), ], @@ -315,7 +316,7 @@ def worker(): @pytest.mark.parametrize( 'waiter_name', [ - pytest.param('_wait_kqueue', marks=pytest.mark.skipif(not _is_macos, reason='macOS only')), + pytest.param('_wait_kqueue', marks=pytest.mark.skipif(not _is_macos or _is_pypy, reason='macOS CPython only')), pytest.param('_wait_pidfd', marks=pytest.mark.skipif(not (_is_linux and _has_pidfd), reason='Linux 3.9+ only')), ], ) @@ -337,7 +338,7 @@ def test_platform_waiter_directly_returns_without_killing_running_process(waiter process.wait() -@pytest.mark.skipif(not _is_macos, reason='macOS only') +@pytest.mark.skipif(not _is_macos or _is_pypy, reason='macOS CPython only') def test_macos_wait_for_process_exit_passes_none_to_event_driven_waiter(): """On macOS, None timeout is forwarded to the event-driven waiter unchanged.""" process = subprocess.Popen( @@ -353,7 +354,7 @@ def test_macos_wait_for_process_exit_passes_none_to_event_driven_waiter(): process.wait() -@pytest.mark.skipif(not _is_macos, reason='macOS only') +@pytest.mark.skipif(not _is_macos or _is_pypy, reason='macOS CPython only') def test_macos_wait_kqueue_builds_subscription_and_closes_queue(): """The macOS kqueue waiter subscribes to the child-process exit event and closes the kqueue handle afterwards.""" mock_kqueue = MagicMock() @@ -375,7 +376,7 @@ def test_macos_wait_kqueue_builds_subscription_and_closes_queue(): mock_kqueue.close.assert_called_once() -@pytest.mark.skipif(not _is_macos, reason='macOS only') +@pytest.mark.skipif(not _is_macos or _is_pypy, reason='macOS CPython only') def test_macos_wait_for_process_exit_falls_back_after_kqueue_oserror(): """If the macOS waiter raises OSError, wait_for_process_exit falls back to process.wait().""" process = subprocess.Popen( From 3a319babfc8341c29f726f2f35c16d89b69d28a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Thu, 9 Apr 2026 14:27:40 +0300 Subject: [PATCH 11/12] Remove PyPy-specific test job in coverage workflow --- .github/workflows/tests_and_coverage.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/tests_and_coverage.yml b/.github/workflows/tests_and_coverage.yml index 64411b5..8482da2 100644 --- a/.github/workflows/tests_and_coverage.yml +++ b/.github/workflows/tests_and_coverage.yml @@ -60,8 +60,7 @@ jobs: shell: bash run: pip list - - name: Run tests and show the branch coverage on the command line (CPython) - if: "!startsWith(matrix.python-version, 'pypy')" + - name: Run tests and show the branch coverage on the command line shell: bash run: | pth_file="$(python -c 'import sysconfig; print(sysconfig.get_path("purelib"))')/suby_coverage_process_startup.pth" @@ -72,11 +71,6 @@ jobs: coverage report -m --fail-under=100 --omit='*tests*' coverage xml --omit='*tests*' - - name: Run tests and show the branch coverage on the command line (PyPy) - if: startsWith(matrix.python-version, 'pypy') - shell: bash - run: pytest --cov=suby --cov-branch --cov-report=term-missing --cov-report=xml:coverage.xml --cov-fail-under=100 -n auto --cache-clear --assert=plain - - name: Upload coverage to Coveralls if: runner.os == 'Linux' && matrix.python-version == '3.13' env: From 9fbe0d4999b66a53a0dfdf2f0ef1bd5b7e235416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=91=D0=BB?= =?UTF-8?q?=D0=B8=D0=BD=D0=BE=D0=B2?= Date: Thu, 9 Apr 2026 15:16:51 +0300 Subject: [PATCH 12/12] Remove PyPy support from CI and tests --- .github/workflows/tests_and_coverage.yml | 29 ++++-------------------- pyproject.toml | 2 -- requirements_dev.txt | 2 -- suby/process_waiting.py | 8 +++---- tests/conftest.py | 12 ++++------ tests/units/test_process_waiting.py | 14 ++++-------- tests/units/test_run.py | 2 +- 7 files changed, 19 insertions(+), 50 deletions(-) diff --git a/.github/workflows/tests_and_coverage.yml b/.github/workflows/tests_and_coverage.yml index 8482da2..41d3a91 100644 --- a/.github/workflows/tests_and_coverage.yml +++ b/.github/workflows/tests_and_coverage.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.14t', '3.15.0-alpha.1', 'pypy3.9', 'pypy3.10', 'pypy3.11'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.14t', '3.15.0-alpha.1'] steps: - uses: actions/checkout@v4 @@ -25,48 +25,29 @@ jobs: python-version: ${{ matrix.python-version }} - name: Set up uv - if: "!startsWith(matrix.python-version, 'pypy')" uses: astral-sh/setup-uv@v7 with: enable-cache: true - - name: Install dependencies (uv) - if: "!startsWith(matrix.python-version, 'pypy')" + - name: Install dependencies shell: bash run: uv pip install --system -r requirements_dev.txt - - name: Install dependencies (pip) - if: startsWith(matrix.python-version, 'pypy') - shell: bash - run: pip install --ignore-installed -r requirements_dev.txt - - - name: Install the library (uv) - if: "!startsWith(matrix.python-version, 'pypy')" + - name: Install the library shell: bash run: uv pip install --system . - - name: Install the library (pip) - if: startsWith(matrix.python-version, 'pypy') - shell: bash - run: pip install . - - - name: Print all libs (uv) - if: "!startsWith(matrix.python-version, 'pypy')" + - name: Print all libs shell: bash run: uv pip list --system - - name: Print all libs (pip) - if: startsWith(matrix.python-version, 'pypy') - shell: bash - run: pip list - - name: Run tests and show the branch coverage on the command line shell: bash run: | pth_file="$(python -c 'import sysconfig; print(sysconfig.get_path("purelib"))')/suby_coverage_process_startup.pth" printf "import os; os.getenv('COVERAGE_PROCESS_START') and __import__('coverage').process_startup()\n" > "$pth_file" coverage erase - COVERAGE_PROCESS_START="$PWD/pyproject.toml" coverage run -m pytest -p no:cov -n auto --cache-clear --assert=plain + COVERAGE_PROCESS_START="$PWD/pyproject.toml" coverage run -m pytest -n auto --cache-clear --assert=plain coverage combine coverage report -m --fail-under=100 --omit='*tests*' coverage xml --omit='*tests*' diff --git a/pyproject.toml b/pyproject.toml index 2d4ef0c..fd422ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,8 +30,6 @@ classifiers = [ 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', 'Programming Language :: Python :: 3.15', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy', 'Programming Language :: Python :: Free Threading', 'Programming Language :: Python :: Free Threading :: 3 - Stable', 'License :: OSI Approved :: MIT License', diff --git a/requirements_dev.txt b/requirements_dev.txt index 3a91697..47af4f7 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -3,8 +3,6 @@ pytest-xdist==3.6.1; python_version < '3.9' pytest-xdist==3.8.0; python_version >= '3.9' coverage==7.6.1 coverage-pyver-pragma==0.4.0 -pytest-cov==5.0.0; python_version < '3.9' -pytest-cov==6.1.1; python_version >= '3.9' build==1.2.2.post1 mypy==1.14.1 pytest-mypy-testing==0.1.3 diff --git a/suby/process_waiting.py b/suby/process_waiting.py index c6e57dd..746ff27 100644 --- a/suby/process_waiting.py +++ b/suby/process_waiting.py @@ -23,10 +23,10 @@ def _wait_pidfd(pid: int, timeout_seconds: Optional[float]) -> None: _event_driven_waiter = _wait_pidfd elif sys.platform == 'darwin': # pragma: no cover (!Darwin) - if not hasattr(select, 'kqueue') or sys.implementation.name == 'pypy': # pragma: no cover (Darwin and !PyPy) + if not hasattr(select, 'kqueue'): # pragma: no cover (Darwin) _event_driven_waiter = None else: - def _wait_kqueue(pid: int, timeout_seconds: Optional[float]) -> None: # pragma: no cover (!Darwin or PyPy) + def _wait_kqueue(pid: int, timeout_seconds: Optional[float]) -> None: # pragma: no cover (!Darwin) kq = select.kqueue() try: ev = select.kevent( @@ -39,7 +39,7 @@ def _wait_kqueue(pid: int, timeout_seconds: Optional[float]) -> None: # pragma: finally: kq.close() - _event_driven_waiter = _wait_kqueue # pragma: no cover (!Darwin or PyPy) + _event_driven_waiter = _wait_kqueue def has_event_driven_wait() -> bool: @@ -50,7 +50,7 @@ def wait_for_process_exit(process: 'Popen[str]', timeout_seconds: Optional[float if _event_driven_waiter is not None: try: _event_driven_waiter(process.pid, timeout_seconds) - return # pragma: no cover (Windows or (Linux and