Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-pytest-forked for
openSUSE:Factory checked in at 2026-08-05 17:49:52
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pytest-forked (Old)
and /work/SRC/openSUSE:Factory/.python-pytest-forked.new.16738 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest-forked"
Wed Aug 5 17:49:52 2026 rev:11 rq:1369619 version:1.6.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-pytest-forked/python-pytest-forked.changes
2025-05-07 19:16:23.527023154 +0200
+++
/work/SRC/openSUSE:Factory/.python-pytest-forked.new.16738/python-pytest-forked.changes
2026-08-05 17:50:45.706819240 +0200
@@ -1,0 +2,6 @@
+Wed Aug 5 04:11:46 UTC 2026 - Steve Kowalik <[email protected]>
+
+- Add patch switch-to-multiprocessing.patch:
+ * Use multiprocessing rather than py.process.ForkedFunc.
+
+-------------------------------------------------------------------
New:
----
_scmsync.obsinfo
build.specials.obscpio
switch-to-multiprocessing.patch
----------(New B)----------
New:
- Add patch switch-to-multiprocessing.patch:
* Use multiprocessing rather than py.process.ForkedFunc.
----------(New E)----------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pytest-forked.spec ++++++
--- /var/tmp/diff_new_pack.1TBCSA/_old 2026-08-05 17:50:46.230837571 +0200
+++ /var/tmp/diff_new_pack.1TBCSA/_new 2026-08-05 17:50:46.234837711 +0200
@@ -24,15 +24,15 @@
License: MIT
URL: https://github.com/pytest-dev/pytest-forked
Source:
https://files.pythonhosted.org/packages/source/p/pytest-forked/pytest-forked-%{version}.tar.gz
+# PATCH-FIX-UPSTREAM Based on gh#pytest-dev/pytest-forked#103
+Patch0: switch-to-multiprocessing.patch
BuildRequires: %{python_module pip}
BuildRequires: %{python_module pytest >= 3.10}
-BuildRequires: %{python_module py}
BuildRequires: %{python_module setuptools_scm}
BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module wheel}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
-Requires: python-py
Requires: python-pytest >= 3.10
BuildArch: noarch
%python_subpackages
@@ -41,7 +41,7 @@
Extraction of pytest-xdist --forked module used for running tests in forked
subprocess
%prep
-%setup -q -n pytest-forked-%{version}
+%autosetup -p1 -n pytest-forked-%{version}
%build
%pyproject_wheel
++++++ _scmsync.obsinfo ++++++
mtime: 1785903114
commit: a9373e506767a6f2472a2d2ea6039267f0334490e94b9cd1657f00cb6c37f0e5
url: https://src.opensuse.org/python-pytest/python-pytest-forked
revision: a9373e506767a6f2472a2d2ea6039267f0334490e94b9cd1657f00cb6c37f0e5
projectscmsync: https://src.opensuse.org/python-pytest/_ObsPrj.git
++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore 1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore 2026-08-05 06:11:54.000000000 +0200
@@ -0,0 +1 @@
+.osc
++++++ switch-to-multiprocessing.patch ++++++
>From c33e4e1efad870b3b9aa308e4ec8ac145884c83d Mon Sep 17 00:00:00 2001
From: Bharath Krishna <[email protected]>
Date: Sun, 24 May 2026 13:28:44 +0000
Subject: [PATCH] Refactor forked process handling and output capture
---
src/pytest_forked/__init__.py | 154 ++++++++++++++++++++++++++++++----
1 file changed, 137 insertions(+), 17 deletions(-)
Index: pytest-forked-1.6.0/src/pytest_forked/__init__.py
===================================================================
--- pytest-forked-1.6.0.orig/src/pytest_forked/__init__.py
+++ pytest-forked-1.6.0/src/pytest_forked/__init__.py
@@ -1,25 +1,24 @@
import os
+import sys
import warnings
-
-import py
+import tempfile
+import marshal
import pytest
from _pytest import runner
+import multiprocessing
# we know this bit is bad, but we cant help it with the current pytest setup
-
# copied from xdist remote
def serialize_report(rep):
- import py
-
d = rep.__dict__.copy()
if hasattr(rep.longrepr, "toterminal"):
d["longrepr"] = str(rep.longrepr)
else:
d["longrepr"] = rep.longrepr
for name in d:
- if isinstance(d[name], py.path.local):
- d[name] = str(d[name])
+ if isinstance(d[name], os.PathLike):
+ d[name] = os.fspath(d[name])
elif name == "result":
d[name] = None # for now
return d
@@ -55,13 +54,61 @@ def pytest_runtest_protocol(item):
return True
+class _ForkedResult:
+ """Mimics py.process.ForkedFunc result object."""
+ def __init__(self):
+ self.retval = None
+ self.exitstatus = 0
+ self.signal = 0
+ self.out = ""
+ self.err = ""
+
+
+def _worker(runforked_fn, stdout_path, stderr_path, retval_path):
+ """
+ Child process entry point.
+ Redirects OS-level fds 1 and 2 to files before running the test,
+ so output is captured even if the process is killed by a signal.
+ """
+ EXITSTATUS_EXCEPTION = 3
+
+ # Redirect stdout/stderr at the OS fd level (survives hard crashes)
+ stdout_fd = os.open(stdout_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
+ stderr_fd = os.open(stderr_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
+ os.dup2(stdout_fd, 1)
+ os.dup2(stderr_fd, 2)
+ os.close(stdout_fd)
+ os.close(stderr_fd)
+
+ # redirect Python-level streams so print() etc. work
+ sys.stdout = open(stdout_path, "w", buffering=1)
+ sys.stderr = open(stderr_path, "w", buffering=1)
+
+ try:
+ retval = runforked_fn()
+ with open(retval_path, "wb") as f:
+ f.write(retval)
+ except KeyboardInterrupt:
+ os._exit(4) # EXITSTATUS_TESTEXIT
+ except SystemExit as e:
+ code = e.code if e.code is not None else 0
+ os._exit(int(code))
+ except Exception:
+ os._exit(EXITSTATUS_EXCEPTION)
+ finally:
+ try:
+ sys.stdout.flush()
+ sys.stderr.flush()
+ except Exception:
+ pass
+
+ os._exit(0)
+
+
def forked_run_report(item):
- # for now, we run setup/teardown in the subprocess
- # XXX optionally allow sharing of setup/teardown
from _pytest.runner import runtestprotocol
EXITSTATUS_TESTEXIT = 4
- import marshal
def runforked():
try:
@@ -70,8 +117,56 @@ def forked_run_report(item):
os._exit(EXITSTATUS_TESTEXIT)
return marshal.dumps([serialize_report(x) for x in reports])
- ff = py.process.ForkedFunc(runforked)
- result = ff.waitfinish()
+ # Use temp files for stdout/stderr — captured at OS fd level, so they
+ # survive a SIGKILL/SIGTERM just like the original ForkedFunc did.
+ with tempfile.TemporaryDirectory() as tmpdir:
+ stdout_path = os.path.join(tmpdir, "stdout")
+ stderr_path = os.path.join(tmpdir, "stderr")
+ retval_path = os.path.join(tmpdir, "retval")
+
+ # Pre-create files so reads don't fail if child never writes
+ open(stdout_path, "w").close()
+ open(stderr_path, "w").close()
+
+ ctx = multiprocessing.get_context("fork")
+ proc = ctx.Process(
+ target=_worker,
+ args=(runforked, stdout_path, stderr_path, retval_path),
+ )
+ proc.start()
+ proc.join()
+
+ result = _ForkedResult()
+ result.exitstatus = proc.exitcode if proc.exitcode is not None else 0
+
+ # Decode signal number from exit code the same way waitpid does:
+ # multiprocessing sets exitcode = -signum for signal-killed children
+ if proc.exitcode is not None and proc.exitcode < 0:
+ result.signal = -proc.exitcode
+
+ # Read captured output — available even after a crash
+ try:
+ with open(stdout_path, "r") as f:
+ result.out = f.read()
+ except OSError:
+ result.out = ""
+
+ try:
+ with open(stderr_path, "r") as f:
+ result.err = f.read()
+ except OSError:
+ result.err = ""
+
+ # Read return value only if child exited cleanly (no signal, no error)
+ if result.signal == 0 and result.exitstatus == 0:
+ try:
+ with open(retval_path, "rb") as f:
+ retval_data = f.read()
+ if retval_data:
+ result.retval = retval_data
+ except OSError:
+ result.retval = None
+
if result.retval is not None:
report_dumps = marshal.loads(result.retval)
return [runner.TestReport(**x) for x in report_dumps]
@@ -82,9 +177,18 @@ def forked_run_report(item):
def report_process_crash(item, result):
- from _pytest._code import getfslineno
+ # getfslineno returns -1 when called from the parent process on an item
+ # whose source is only resolvable in the child. Use the item's own
+ # location (nodeid path + fspath) which is always populated by pytest.
+ try:
+ from _pytest._code import getfslineno
+ path, lineno = getfslineno(item)
+ if lineno == -1:
+ raise ValueError("unresolvable")
+ except Exception:
+ path = getattr(item, "fspath", None) or item.nodeid.split("::")[0]
+ lineno = item.location[1] if item.location[1] is not None else 0
- path, lineno = getfslineno(item)
info = "%s:%s: running the test CRASHED with signal %d" % (
path,
lineno,
@@ -110,11 +214,16 @@ def report_process_crash(item, result):
return rep
rep.outcome = "skipped"
+
+ xfail_reason = xfail_marker.kwargs.get(
+ "reason",
+ xfail_marker.args[0] if xfail_marker.args else "",
+ )
rep.wasxfail = (
"reason: {xfail_reason}; "
"pytest-forked reason: {crash_info}".format(
- xfail_reason=xfail_marker.kwargs["reason"],
- crash_info=info,
+ xfail_reason=xfail_reason,
+ crash_info=info_bare,
)
)
warnings.warn(
Index: pytest-forked-1.6.0/setup.py
===================================================================
--- pytest-forked-1.6.0.orig/setup.py
+++ pytest-forked-1.6.0/setup.py
@@ -19,7 +19,7 @@ setup(
],
},
zip_safe=False,
- install_requires=["py", "pytest>=3.10"],
+ install_requires=["pytest>=3.10"],
setup_requires=["setuptools_scm"],
python_requires=">=3.7",
classifiers=[