diff options
Diffstat (limited to 'contrib/python/hypothesis/py3/_hypothesis_pytestplugin.py')
| -rw-r--r-- | contrib/python/hypothesis/py3/_hypothesis_pytestplugin.py | 172 |
1 files changed, 90 insertions, 82 deletions
diff --git a/contrib/python/hypothesis/py3/_hypothesis_pytestplugin.py b/contrib/python/hypothesis/py3/_hypothesis_pytestplugin.py index f7a89268452..340bf90ebfa 100644 --- a/contrib/python/hypothesis/py3/_hypothesis_pytestplugin.py +++ b/contrib/python/hypothesis/py3/_hypothesis_pytestplugin.py @@ -49,20 +49,6 @@ _ALL_OPTIONS = [ SEED_OPTION, EXPLAIN_OPTION, ] -_FIXTURE_MSG = """Function-scoped fixture {0!r} used by {1!r} - -Function-scoped fixtures are not reset between examples generated by -`@given(...)`, which is often surprising and can cause subtle test bugs. - -If you were expecting the fixture to run separately for each generated example, -then unfortunately you will need to find a different way to achieve your goal -(e.g. using a similar context manager instead of a fixture). - -If you are confident that your test will work correctly even though the -fixture is not reset between generated examples, you can suppress this health -check to assure Hypothesis that you understand what you are doing. -""" - STATS_KEY = "_hypothesis_stats" FAILING_EXAMPLES_KEY = "_hypothesis_failing_examples" @@ -160,7 +146,9 @@ else: settings_str = settings.default.show_changed() if settings_str != "": settings_str = f" -> {settings_str}" - return f"hypothesis profile {settings._current_profile!r}{settings_str}" + return ( + f"hypothesis profile {settings.get_current_profile_name()!r}{settings_str}" + ) def pytest_configure(config): config.addinivalue_line("markers", "hypothesis: Tests which use hypothesis.") @@ -174,7 +162,9 @@ else: verbosity_name = config.getoption(VERBOSITY_OPTION) if verbosity_name and verbosity_name != settings.default.verbosity.name: verbosity_value = Verbosity[verbosity_name] - name = f"{settings._current_profile}-with-{verbosity_name}-verbosity" + name = ( + f"{settings.get_current_profile_name()}-with-{verbosity_name}-verbosity" + ) # register_profile creates a new profile, exactly like the current one, # with the extra values given (in this case 'verbosity') settings.register_profile(name, verbosity=verbosity_value) @@ -183,7 +173,7 @@ else: config.getoption(EXPLAIN_OPTION) and Phase.explain not in settings.default.phases ): - name = f"{settings._current_profile}-with-explain-phase" + name = f"{settings.get_current_profile_name()}-with-explain-phase" phases = (*settings.default.phases, Phase.explain) settings.register_profile(name, phases=phases) settings.load_profile(name) @@ -240,79 +230,97 @@ else: raise_hypothesis_usage_error(message % (name,)) yield - else: - from hypothesis import HealthCheck, settings as Settings - from hypothesis.internal.escalation import current_pytest_item - from hypothesis.internal.healthcheck import fail_health_check - from hypothesis.reporting import with_reporter - from hypothesis.statistics import collector, describe_statistics + return - # Retrieve the settings for this test from the test object, which - # is normally a Hypothesis wrapped_test wrapper. If this doesn't - # work, the test object is probably something weird - # (e.g a stateful test wrapper), so we skip the function-scoped - # fixture check. - settings = getattr( - item.obj, "_hypothesis_internal_use_settings", Settings.default - ) + from hypothesis import HealthCheck, settings as Settings + from hypothesis.internal.escalation import current_pytest_item + from hypothesis.internal.healthcheck import fail_health_check + from hypothesis.reporting import with_reporter + from hypothesis.statistics import collector, describe_statistics - # Check for suspicious use of function-scoped fixtures, but only - # if the corresponding health check is not suppressed. - fixture_params = False - if not set(settings.suppress_health_check).issuperset( - {HealthCheck.function_scoped_fixture, HealthCheck.differing_executors} - ): - # Warn about function-scoped fixtures, excluding autouse fixtures because - # the advice is probably not actionable and the status quo seems OK... - # See https://github.com/HypothesisWorks/hypothesis/issues/377 for detail. - argnames = None - for fx_defs in item._request._fixturemanager.getfixtureinfo( - node=item, func=item.function, cls=None - ).name2fixturedefs.values(): - if argnames is None: - argnames = frozenset(signature(item.function).parameters) - for fx in fx_defs: - fixture_params |= bool(fx.params) - if fx.argname in argnames: - active_fx = item._request._get_active_fixturedef(fx.argname) - if active_fx.scope == "function": - fail_health_check( - settings, - _FIXTURE_MSG.format(fx.argname, item.nodeid), - HealthCheck.function_scoped_fixture, - ) + # Retrieve the settings for this test from the test object, which + # is normally a Hypothesis wrapped_test wrapper. If this doesn't + # work, the test object is probably something weird + # (e.g a stateful test wrapper), so we skip the function-scoped + # fixture check. + settings = getattr( + item.obj, "_hypothesis_internal_use_settings", Settings.default + ) - if fixture_params or (item.get_closest_marker("parametrize") is not None): - # Disable the differing_executors health check due to false alarms: - # see https://github.com/HypothesisWorks/hypothesis/issues/3733 - from hypothesis import settings as Settings + # Check for suspicious use of function-scoped fixtures, but only + # if the corresponding health check is not suppressed. + fixture_params = False + if not set(settings.suppress_health_check).issuperset( + {HealthCheck.function_scoped_fixture, HealthCheck.differing_executors} + ): + # Warn about function-scoped fixtures, excluding autouse fixtures because + # the advice is probably not actionable and the status quo seems OK... + # See https://github.com/HypothesisWorks/hypothesis/issues/377 for detail. + argnames = None + for fx_defs in item._request._fixturemanager.getfixtureinfo( + node=item, func=item.function, cls=None + ).name2fixturedefs.values(): + if argnames is None: + argnames = frozenset(signature(item.function).parameters) + for fx in fx_defs: + fixture_params |= bool(fx.params) + if fx.argname not in argnames: + continue + active_fx = item._request._get_active_fixturedef(fx.argname) + if active_fx.scope == "function": + fail_health_check( + settings, + f"{item.nodeid!r} uses a function-scoped fixture {fx.argname!r}." + "\n\n" + "Function-scoped fixtures are not reset between inputs " + "generated by `@given(...)`, which is often surprising and " + "can cause subtle test bugs." + "\n\n" + "If you were expecting the fixture to run separately " + "for each generated input, then unfortunately you " + "will need to find a different way to achieve your " + "goal (for example, replacing the fixture with a similar " + "context manager inside of the test)." + "\n\n" + "If you are confident that your test will work correctly " + "even though the fixture is not reset between generated " + "inputs, you can suppress this health check with " + "@settings(suppress_health_check=[HealthCheck.function_scoped_fixture]). " + "See " + "https://hypothesis.readthedocs.io/en/latest/reference/api.html#hypothesis.HealthCheck " + "for details.", + HealthCheck.function_scoped_fixture, + ) - fn = getattr(item.obj, "__func__", item.obj) - fn._hypothesis_internal_use_settings = Settings( - parent=settings, - suppress_health_check={HealthCheck.differing_executors} - | set(settings.suppress_health_check), - ) + if fixture_params or (item.get_closest_marker("parametrize") is not None): + # Disable the differing_executors health check due to false alarms: + # see https://github.com/HypothesisWorks/hypothesis/issues/3733 + fn = getattr(item.obj, "__func__", item.obj) + fn._hypothesis_internal_use_settings = Settings( + parent=settings, + suppress_health_check={HealthCheck.differing_executors} + | set(settings.suppress_health_check), + ) + + # Give every parametrized test invocation a unique database key + key = item.nodeid.encode() + item.obj.hypothesis.inner_test._hypothesis_internal_add_digest = key - # Give every parametrized test invocation a unique database key - key = item.nodeid.encode() - item.obj.hypothesis.inner_test._hypothesis_internal_add_digest = key + store = StoringReporter(item.config) - store = StoringReporter(item.config) + def note_statistics(stats): + stats["nodeid"] = item.nodeid + item.hypothesis_statistics = describe_statistics(stats) - def note_statistics(stats): - stats["nodeid"] = item.nodeid - item.hypothesis_statistics = describe_statistics(stats) + with ( + collector.with_value(note_statistics), + with_reporter(store), + current_pytest_item.with_value(item), + ): + yield - with collector.with_value(note_statistics): - # NOTE: For compatibility with Python 3.9's LL(1) - # parser, this is written as a nested with-statement, - # instead of a compound one. - with with_reporter(store): - with current_pytest_item.with_value(item): - yield - if store.results: - item.hypothesis_report_information = "\n".join(store.results) + if store.results: + item.hypothesis_report_information = "\n".join(store.results) def _stash_get(config, key, default): if hasattr(config, "stash"): |
