commit: e7af2910a9b2adacca62a54f3dc5df69ab9d233c
Author: Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Sun Dec 7 16:32:54 2025 +0000
Commit: Brian Harring <ferringb <AT> gmail <DOT> com>
CommitDate: Sun Dec 7 16:33:49 2025 +0000
URL:
https://gitweb.gentoo.org/proj/pkgcore/snakeoil.git/commit/?id=e7af2910
chore: fix test.protect_process so it handles being ran from w/in the tests
directory itself
Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
src/snakeoil/test/__init__.py | 48 ++++++++++++++++++++++++-------------------
tests/test_test.py | 30 +++++++++++++++++++--------
2 files changed, 48 insertions(+), 30 deletions(-)
diff --git a/src/snakeoil/test/__init__.py b/src/snakeoil/test/__init__.py
index 89bab4c..9b8ef5b 100644
--- a/src/snakeoil/test/__init__.py
+++ b/src/snakeoil/test/__init__.py
@@ -10,7 +10,6 @@ __all__ = (
"Slots",
)
-import functools
import os
import random
import string
@@ -51,19 +50,16 @@ def protect_process(
extra_env: dict[str, str] | None = None,
):
def wrapper(functor):
- @functools.wraps(functor)
- def _inner_run(self, *args, **kwargs):
- # we're in the child. Just run it.
- if os.environ.get(marker_env_var, False):
- return functor(self, *args, **kwargs)
-
- # we're in the parent. if capsys is in there, we have
- # to intercept it for the code below.
- capsys = kwargs.get("capsys")
+ if os.environ.get(marker_env_var, False):
+ functor.__protect_process_is_child__ = True
+ return functor
+
+ @staticmethod
+ def parent_runner(pytestconfig):
env = os.environ.copy()
if extra_env:
env.update(extra_env)
- env[marker_env_var] = "disable"
+ env[marker_env_var] = "in_child"
test = (
os.environ["PYTEST_CURRENT_TEST"]
if forced_test is None
@@ -72,24 +68,34 @@ def protect_process(
#
https://docs.pytest.org/en/latest/example/simple.html#pytest-current-test-environment-variable
assert test.endswith(" (call)")
test = test[: -len(" (call)")]
- args = [sys.executable, "-m", "pytest", "-v", test]
+ # pytestconfig.rootpath is used so that if someone has cd'd within
the tests directory, the test
+ # can still be found. PYTEST_CURRENT_TEST is rooted against the
root, thus this requirement
+ args = [
+ sys.executable,
+ "-m",
+ "pytest",
+ "-v",
+ f"{pytestconfig.rootpath}/{test}",
+ ]
p = subprocess.Popen(
args,
env=env,
- stdout=None if capsys else subprocess.PIPE,
- stderr=None if capsys else subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
)
stdout, stderr = p.communicate()
- if capsys:
- result = capsys.readouterr()
- stdout, stderr = result.out, result.err
- ret = p.wait()
- assert ret == 0, (
- f"subprocess run: {args!r}\nnon zero exit:
{ret}\nstdout:\n{stdout.decode()}'n\nstderr:\n{stderr.decode()}"
+ exit_code = p.wait()
+ assert 0 == exit_code, (
+ f"subprocess run: {args!r}\nnon zero exit:
{exit_code}\nstdout:\n{stdout.decode()}'n\nstderr:\n{stderr.decode()}"
)
- return _inner_run
+ # do not do a functools wraps; it'll stomp our pytestconfig signature.
Just do basic transfer.
+ for a in ("__name__", "__module__", "__qualname__"):
+ if (val := getattr(functor, a, None)) is not None:
+ setattr(parent_runner, a, val)
+ parent_runner.__protect_process_is_child__ = False
+ return parent_runner
return wrapper
diff --git a/tests/test_test.py b/tests/test_test.py
index f325d66..5fc2a3b 100644
--- a/tests/test_test.py
+++ b/tests/test_test.py
@@ -6,12 +6,18 @@ from snakeoil import test
class Test_protect_process:
- def test_success(self, capsys):
+ def test_success(self, pytestconfig, capsys, in_child=False):
@test.protect_process()
- def no_fail(self) -> None:
+ def no_fail() -> None:
pass
- assert None is no_fail(capsys)
+ if no_fail.__protect_process_is_child__: # pyright:
ignore[reportFunctionMemberAccess]
+ assert None is no_fail()
+ return
+
+ assert None is no_fail(pytestconfig=pytestconfig) # pyright:
ignore[reportCallIssue]
+ if in_child:
+ return
captured = capsys.readouterr()
assert "" == captured.out, (
"no stdout should be captured for success: {captured.out}"
@@ -20,19 +26,25 @@ class Test_protect_process:
"no stderr should be captured for success: {captured.err}"
)
- def test_failure(self, capsys):
+ def test_failure(self, pytestconfig):
unique_string =
"asdfasdfasdfasdfdasdfdasdfasdfasdfasdfasdfasdfasdfsadf"
@test.protect_process(extra_env={unique_string: unique_string})
- def fail(self, capsys) -> None:
+ def fail() -> None:
raise AssertionError(unique_string)
- if os.environ.get(unique_string):
- # we're in the child.
- fail(self, capsys)
+ if fail.__protect_process_is_child__: # pyright:
ignore[reportFunctionMemberAccess]
+ is_in_env = os.environ.get(unique_string) == unique_string
+ assert is_in_env, (
+ "unique_string wasn't in the child. extra_env didn't pass
down"
+ )
+ # trigger the exception.
+ fail()
+ # chuck this if we make it this far, because it means fail is
misimplemented or
+ # protect_process didn't return the raw functor to run
raise Exception("implementation is broke, fail didn't throw an
exception")
with pytest.raises(AssertionError) as failed:
- fail(self, capsys)
+ fail(pytestconfig=pytestconfig) # pyright: ignore[reportCallIssue]
assert unique_string in str(failed.value)