From fe8b43f9432e8147e296a598b10f9e75b3d201d5 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Wed, 29 May 2024 18:14:30 +0200 Subject: [PATCH] Add support for a hook to override skip decision. This declares a pytest_dependency_override_skip which can be implemented to decide not to skip a test when a specific dep is skipped or unknown. This can be used when the dependency result is cached in some way, and the dependent test can retrieve it instead of rerunning a costly test. Signed-off-by: Yann Dirson --- setup.py | 2 +- src/pytest_dependency.py | 16 ++++++++++++++++ src/pytest_dependency_hooks.py | 9 +++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/pytest_dependency_hooks.py diff --git a/setup.py b/setup.py index db3a8db..60f422d 100755 --- a/setup.py +++ b/setup.py @@ -158,7 +158,7 @@ def run(self): ), package_dir = {"": "src"}, python_requires = ">=3.4", - py_modules = ["pytest_dependency"], + py_modules = ["pytest_dependency", "pytest_dependency_hooks"], install_requires = ["setuptools", "pytest >= 3.7.0"], entry_points = { "pytest11": [ diff --git a/src/pytest_dependency.py b/src/pytest_dependency.py index 43224ee..aa7c8ff 100644 --- a/src/pytest_dependency.py +++ b/src/pytest_dependency.py @@ -30,6 +30,8 @@ def addResult(self, rep): def isSuccess(self): return list(self.results.values()) == ['passed', 'passed', 'passed'] + def hasFailure(self): + return any(outcome == 'failed' for outcome in self.results.values()) class DependencyManager(object): """Dependency manager, stores the results of tests. @@ -82,16 +84,23 @@ def checkDepend(self, depends, item): logger.debug("check dependencies of %s in %s scope ...", item.name, self.scope) for i in depends: + has_failure = False if i in self.results: if self.results[i].isSuccess(): logger.debug("... %s succeeded", i) continue else: logger.debug("... %s has not succeeded", i) + if self.results[i].hasFailure(): + has_failure = True else: logger.debug("... %s is unknown", i) if _ignore_unknown: continue + if not has_failure and any(item.config.hook.pytest_dependency_override_skip( + item=item, dependency=i, scope=self.scope)): + logger.info("NOT skipping %s because its dependency on %s", item.name, i) + continue logger.info("skip %s because it depends on %s", item.name, i) pytest.skip("%s depends on %s" % (item.name, i)) @@ -170,3 +179,10 @@ def pytest_runtest_setup(item): scope = marker.kwargs.get('scope', 'module') manager = DependencyManager.getManager(item, scope=scope) manager.checkDepend(depends, item) + + +def pytest_addhooks(pluginmanager): + """This example assumes the hooks are grouped in the 'sample_hook' module.""" + import pytest_dependency_hooks + + pluginmanager.add_hookspecs(pytest_dependency_hooks) diff --git a/src/pytest_dependency_hooks.py b/src/pytest_dependency_hooks.py new file mode 100644 index 0000000..6afe810 --- /dev/null +++ b/src/pytest_dependency_hooks.py @@ -0,0 +1,9 @@ +import pytest + +def pytest_dependency_override_skip(item: pytest.Item, dependency: str, scope: str) -> bool: + """ + Possibly override decision to skip a test item with a dependency. + + If any implementation of this hook returns a true value `dependency` + being unknown or skipped will not cause `item` to be skipped. + """