commit:     b2efd919716b09b7a3a9e06827cae26a06a5b0eb
Author:     Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Sat Dec  6 14:13:48 2025 +0000
Commit:     Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Sat Dec  6 14:14:05 2025 +0000
URL:        https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=b2efd919

dev-python/pytest-jobserver: New package, v1.0.0

A new package to control parallel xdist runs with a jobserver.  It's
kinda dead upstream but easier to patch it than start over.

Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>

 dev-python/pytest-jobserver/Manifest               |   1 +
 .../files/pytest-jobserver-1.0.0-gentoo.patch      | 125 +++++++++++++++++++++
 dev-python/pytest-jobserver/metadata.xml           |  12 ++
 .../pytest-jobserver/pytest-jobserver-1.0.0.ebuild |  54 +++++++++
 4 files changed, 192 insertions(+)

diff --git a/dev-python/pytest-jobserver/Manifest 
b/dev-python/pytest-jobserver/Manifest
new file mode 100644
index 000000000000..0c5a8e225949
--- /dev/null
+++ b/dev-python/pytest-jobserver/Manifest
@@ -0,0 +1 @@
+DIST pytest-jobserver-1.0.0.tar.gz 6286 BLAKE2B 
88020a09b605c52745761e22e925329ef18f3c1f5bb69c92f430621eb454810f6e533135959a59c19e557c1cc98e1c808ae2b5917e0e947f1d01579ce25918b0
 SHA512 
e98d3908357930e3ee11082e88477c4225e86c7a357ac0e9120ebd75b3cc6ffa38d65fc37d72bbcf35530fe0949badd9c6a1ccb7a370d1ca6ae69c9694ba8e48

diff --git 
a/dev-python/pytest-jobserver/files/pytest-jobserver-1.0.0-gentoo.patch 
b/dev-python/pytest-jobserver/files/pytest-jobserver-1.0.0-gentoo.patch
new file mode 100644
index 000000000000..c26422e30b17
--- /dev/null
+++ b/dev-python/pytest-jobserver/files/pytest-jobserver-1.0.0-gentoo.patch
@@ -0,0 +1,125 @@
+diff --git a/pytest_jobserver/configure.py b/pytest_jobserver/configure.py
+index aa3c998..364729d 100644
+--- a/pytest_jobserver/configure.py
++++ b/pytest_jobserver/configure.py
+@@ -21,9 +21,6 @@ def path_to_file_descriptors(jobserver_path: str) -> 
Optional[FileDescriptorsRW]
+     if os.path.exists(jobserver_path) is False:
+         raise pytest.UsageError("jobserver doesn't exist: 
{}".format(jobserver_path))
+ 
+-    if is_fifo(jobserver_path) is False:
+-        raise pytest.UsageError("jobserver is not a fifo: 
{}".format(jobserver_path))
+-
+     if is_rw_ok(jobserver_path) is False:
+         raise pytest.UsageError(
+             "jobserver is not read/writeable to current user: 
{}".format(jobserver_path)
+@@ -50,16 +47,19 @@ def jobserver_from_env_make(config: Config) -> 
Optional[FileDescriptorsRW]:
+ 
+     parser = argparse.ArgumentParser(prog="makeflags")
+     parser.add_argument("--jobserver-fds", default=None)
+-    parser.add_argument("--jobserver-auth", default=None)
++    parser.add_argument("--jobserver-auth", default="")
+     args, _ = parser.parse_known_args(shlex.split(makeflags))
+ 
++    if args.jobserver_auth.startswith("fifo:"):
++        return path_to_file_descriptors(args.jobserver_auth[5:])
++
+     fds = args.jobserver_fds or args.jobserver_auth
+     if fds is None:
+         return None
+ 
+     if config.pluginmanager.hasplugin("xdist"):
+         raise pytest.UsageError(
+-            "pytest-jobserver does not support using pytest-xdist with 
MAKEFLAGS"
++            "pytest-jobserver does not support using pytest-xdist with 
fd-based jobserver in MAKEFLAGS"
+         )
+ 
+     fd_read, fd_write = tuple(FileDescriptor(int(fd)) for fd in 
fds.split(","))
+diff --git a/pytest_jobserver/plugin.py b/pytest_jobserver/plugin.py
+index fc4f11c..c3b781e 100644
+--- a/pytest_jobserver/plugin.py
++++ b/pytest_jobserver/plugin.py
+@@ -23,16 +23,29 @@ class JobserverPlugin(object):
+         self._fd_read, self._fd_write = fds
+         self._thread_locals = threading.local()
+ 
++    @pytest.hookimpl(hookwrapper=True, tryfirst=True)
++    def pytest_collection(self, session: pytest.Session) -> Iterator[Any]:
++        if os.environ.get("PYTEST_XDIST_WORKER", "gw0") != "gw0":
++            token = os.read(self._fd_read, 1)
++            yield
++            os.write(self._fd_write, token)
++        else:
++            yield
++
+     @pytest.hookimpl(hookwrapper=True, tryfirst=True)
+     def pytest_runtest_protocol(self, item: Item) -> Iterator[Any]:
+-        token = os.read(self._fd_read, 1)
+-        self._thread_locals.token = ord(token)
+-        yield
+-        os.write(self._fd_write, token)
++        if os.environ.get("PYTEST_XDIST_WORKER", "gw0") != "gw0":
++            token = os.read(self._fd_read, 1)
++            self._thread_locals.token = ord(token)
++            yield
++            os.write(self._fd_write, token)
++        else:
++            self._thread_locals.token = None
++            yield
+ 
+     @pytest.fixture(scope="function", autouse=True)
+-    def jobserver_token(self, request: Any) -> int:
+-        int_token: int = self._thread_locals.token
++    def jobserver_token(self, request: Any) -> int | None:
++        int_token: int | None = self._thread_locals.token
+         return int_token
+ 
+     def pytest_report_header(self, config: Config) -> str:
+diff --git a/pytest_jobserver/test/test_plugin.py 
b/pytest_jobserver/test/test_plugin.py
+index 6f51376..45cce4d 100644
+--- a/pytest_jobserver/test/test_plugin.py
++++ b/pytest_jobserver/test/test_plugin.py
+@@ -68,6 +68,25 @@ def test_config_env_pytest(testdir: TestDir) -> None:
+     assert result.ret == 0
+ 
+ 
++def test_config_makeflags_pytest(testdir: TestDir) -> None:
++    testdir.makepyfile(
++        """
++        def test_pass(request):
++            pass
++    """
++    )
++    make_jobserver(testdir.tmpdir, "jobserver_fifo", 1)
++    testdir.monkeypatch.setenv("MAKEFLAGS", 
"--jobserver-auth=fifo:jobserver_fifo")
++
++    result = testdir.runpytest("-v")
++
++    result.stdout.fnmatch_lines(["*::test_pass PASSED*"])
++    result.stdout.fnmatch_lines(
++        ["jobserver: configured at file descriptors (read: *, write: *)"]
++    )
++    assert result.ret == 0
++
++
+ def test_jobserver_token_fixture(testdir: TestDir) -> None:
+     testdir.makepyfile(
+         f"""
+@@ -94,7 +113,9 @@ def test_xdist_makeflags_fails(testdir: TestDir) -> None:
+     result = testdir.runpytest("-v", "-n2")
+     assert result.ret == 4, "Expected pytest would fail to run with MAKEFLAGS 
and xdist"
+     result.stderr.fnmatch_lines(
+-        ["ERROR: pytest-jobserver does not support using pytest-xdist with 
MAKEFLAGS"]
++        [
++            "ERROR: pytest-jobserver does not support using pytest-xdist with 
fd-based jobserver in MAKEFLAGS"
++        ]
+     )
+ 
+ 
+@@ -166,6 +187,7 @@ def test_server_not_found(testdir: TestDir) -> None:
+ 
+ 
+ def test_server_not_fifo(testdir: TestDir) -> None:
++    return
+     testdir.makefile(".txt", jobserver="X")
+     testdir.makepyfile(
+         """

diff --git a/dev-python/pytest-jobserver/metadata.xml 
b/dev-python/pytest-jobserver/metadata.xml
new file mode 100644
index 000000000000..cf7ca257edba
--- /dev/null
+++ b/dev-python/pytest-jobserver/metadata.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE pkgmetadata SYSTEM "https://www.gentoo.org/dtd/metadata.dtd";>
+<pkgmetadata>
+       <maintainer type="project">
+               <email>[email protected]</email>
+       </maintainer>
+       <stabilize-allarches/>
+       <upstream>
+               <remote-id 
type="github">tommilligan/pytest-jobserver</remote-id>
+               <remote-id type="pypi">pytest-jobserver</remote-id>
+       </upstream>
+</pkgmetadata>

diff --git a/dev-python/pytest-jobserver/pytest-jobserver-1.0.0.ebuild 
b/dev-python/pytest-jobserver/pytest-jobserver-1.0.0.ebuild
new file mode 100644
index 000000000000..3e548b71707c
--- /dev/null
+++ b/dev-python/pytest-jobserver/pytest-jobserver-1.0.0.ebuild
@@ -0,0 +1,54 @@
+# Copyright 2025 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+DISTUTILS_USE_PEP517=setuptools
+PYPI_NO_NORMALIZE=1
+PYTHON_COMPAT=( pypy3_11 python3_{11..14} )
+
+inherit distutils-r1 pypi
+
+DESCRIPTION="Limit parallel tests with POSIX jobserver"
+HOMEPAGE="
+       https://github.com/tommilligan/pytest-jobserver/
+       https://pypi.org/project/pytest-jobserver/
+"
+
+LICENSE="Apache-2.0"
+SLOT="0"
+KEYWORDS="~amd64"
+
+RDEPEND="
+       dev-python/pytest[${PYTHON_USEDEP}]
+"
+
+PATCHES=(
+       # Combined Gentoo patches:
+       # 1. Update MAKEFLAGS parsing:
+       #    https://github.com/tommilligan/pytest-jobserver/pull/147
+       # 2. Do not require FIFO:
+       #    https://github.com/tommilligan/pytest-jobserver/pull/149
+       # 3. Acquire job tokens for test collection:
+       #    https://github.com/tommilligan/pytest-jobserver/pull/150
+       # 4. Do not acquire tokens for gw0 (not submitted yet).
+       "${FILESDIR}/${P}-gentoo.patch"
+)
+
+EPYTEST_PLUGIN_LOAD_VIA_ENV=1
+EPYTEST_PLUGINS=( "${PN}" pytest-xdist )
+distutils_enable_tests pytest
+
+python_test() {
+       local EPYTEST_DESELECT=(
+               pytest_jobserver/test/test_plugin.py::test_xdist_makeflags_fails
+               # broken by implicit slot fix
+               
pytest_jobserver/test/test_plugin.py::test_jobserver_token_fixture
+               pytest_jobserver/test/test_plugin.py::test_server_xdist
+       )
+
+       unset A MAKEFLAGS
+
+       # missing conftest.py
+       epytest -p pytester
+}

Reply via email to