扫码登录,获取cookies
This commit is contained in:
738
backend/venv/Lib/site-packages/hypothesis/_settings.py
Normal file
738
backend/venv/Lib/site-packages/hypothesis/_settings.py
Normal file
@@ -0,0 +1,738 @@
|
||||
# This file is part of Hypothesis, which may be found at
|
||||
# https://github.com/HypothesisWorks/hypothesis/
|
||||
#
|
||||
# Copyright the Hypothesis Authors.
|
||||
# Individual contributors are listed in AUTHORS.rst and the git log.
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
# obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
"""The settings module configures runtime options for Hypothesis.
|
||||
|
||||
Either an explicit settings object can be used or the default object on
|
||||
this module can be modified.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import datetime
|
||||
import inspect
|
||||
import os
|
||||
import warnings
|
||||
from enum import Enum, EnumMeta, IntEnum, unique
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
ClassVar,
|
||||
Collection,
|
||||
Dict,
|
||||
List,
|
||||
Optional,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
|
||||
import attr
|
||||
|
||||
from hypothesis.errors import (
|
||||
HypothesisDeprecationWarning,
|
||||
InvalidArgument,
|
||||
InvalidState,
|
||||
)
|
||||
from hypothesis.internal.reflection import get_pretty_function_description
|
||||
from hypothesis.internal.validation import check_type, try_convert
|
||||
from hypothesis.utils.conventions import not_set
|
||||
from hypothesis.utils.dynamicvariables import DynamicVariable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from hypothesis.database import ExampleDatabase
|
||||
|
||||
__all__ = ["settings"]
|
||||
|
||||
all_settings: Dict[str, "Setting"] = {}
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class settingsProperty:
|
||||
def __init__(self, name, show_default):
|
||||
self.name = name
|
||||
self.show_default = show_default
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
if obj is None:
|
||||
return self
|
||||
else:
|
||||
try:
|
||||
result = obj.__dict__[self.name]
|
||||
# This is a gross hack, but it preserves the old behaviour that
|
||||
# you can change the storage directory and it will be reflected
|
||||
# in the default database.
|
||||
if self.name == "database" and result is not_set:
|
||||
from hypothesis.database import ExampleDatabase
|
||||
|
||||
result = ExampleDatabase(not_set)
|
||||
return result
|
||||
except KeyError:
|
||||
raise AttributeError(self.name) from None
|
||||
|
||||
def __set__(self, obj, value):
|
||||
obj.__dict__[self.name] = value
|
||||
|
||||
def __delete__(self, obj):
|
||||
raise AttributeError(f"Cannot delete attribute {self.name}")
|
||||
|
||||
@property
|
||||
def __doc__(self):
|
||||
description = all_settings[self.name].description
|
||||
default = (
|
||||
repr(getattr(settings.default, self.name))
|
||||
if self.show_default
|
||||
else "(dynamically calculated)"
|
||||
)
|
||||
return f"{description}\n\ndefault value: ``{default}``"
|
||||
|
||||
|
||||
default_variable = DynamicVariable(None)
|
||||
|
||||
|
||||
class settingsMeta(type):
|
||||
def __init__(cls, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def default(cls):
|
||||
v = default_variable.value
|
||||
if v is not None:
|
||||
return v
|
||||
if hasattr(settings, "_current_profile"):
|
||||
settings.load_profile(settings._current_profile)
|
||||
assert default_variable.value is not None
|
||||
return default_variable.value
|
||||
|
||||
def _assign_default_internal(cls, value):
|
||||
default_variable.value = value
|
||||
|
||||
def __setattr__(cls, name, value):
|
||||
if name == "default":
|
||||
raise AttributeError(
|
||||
"Cannot assign to the property settings.default - "
|
||||
"consider using settings.load_profile instead."
|
||||
)
|
||||
elif not (isinstance(value, settingsProperty) or name.startswith("_")):
|
||||
raise AttributeError(
|
||||
f"Cannot assign hypothesis.settings.{name}={value!r} - the settings "
|
||||
"class is immutable. You can change the global default "
|
||||
"settings with settings.load_profile, or use @settings(...) "
|
||||
"to decorate your test instead."
|
||||
)
|
||||
return super().__setattr__(name, value)
|
||||
|
||||
|
||||
class settings(metaclass=settingsMeta):
|
||||
"""A settings object configures options including verbosity, runtime controls,
|
||||
persistence, determinism, and more.
|
||||
|
||||
Default values are picked up from the settings.default object and
|
||||
changes made there will be picked up in newly created settings.
|
||||
"""
|
||||
|
||||
__definitions_are_locked = False
|
||||
_profiles: ClassVar[Dict[str, "settings"]] = {}
|
||||
__module__ = "hypothesis"
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in all_settings:
|
||||
return all_settings[name].default
|
||||
else:
|
||||
raise AttributeError(f"settings has no attribute {name}")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: Optional["settings"] = None,
|
||||
*,
|
||||
# This looks pretty strange, but there's good reason: we want Mypy to detect
|
||||
# bad calls downstream, but not to freak out about the `= not_set` part even
|
||||
# though it's not semantically valid to pass that as an argument value.
|
||||
# The intended use is "like **kwargs, but more tractable for tooling".
|
||||
max_examples: int = not_set, # type: ignore
|
||||
derandomize: bool = not_set, # type: ignore
|
||||
database: Optional["ExampleDatabase"] = not_set, # type: ignore
|
||||
verbosity: "Verbosity" = not_set, # type: ignore
|
||||
phases: Collection["Phase"] = not_set, # type: ignore
|
||||
stateful_step_count: int = not_set, # type: ignore
|
||||
report_multiple_bugs: bool = not_set, # type: ignore
|
||||
suppress_health_check: Collection["HealthCheck"] = not_set, # type: ignore
|
||||
deadline: Union[int, float, datetime.timedelta, None] = not_set, # type: ignore
|
||||
print_blob: bool = not_set, # type: ignore
|
||||
) -> None:
|
||||
if parent is not None:
|
||||
check_type(settings, parent, "parent")
|
||||
if derandomize not in (not_set, False):
|
||||
if database not in (not_set, None): # type: ignore
|
||||
raise InvalidArgument(
|
||||
"derandomize=True implies database=None, so passing "
|
||||
f"{database=} too is invalid."
|
||||
)
|
||||
database = None
|
||||
|
||||
defaults = parent or settings.default
|
||||
if defaults is not None:
|
||||
for setting in all_settings.values():
|
||||
value = locals()[setting.name]
|
||||
if value is not_set:
|
||||
object.__setattr__(
|
||||
self, setting.name, getattr(defaults, setting.name)
|
||||
)
|
||||
else:
|
||||
object.__setattr__(self, setting.name, setting.validator(value))
|
||||
|
||||
def __call__(self, test: T) -> T:
|
||||
"""Make the settings object (self) an attribute of the test.
|
||||
|
||||
The settings are later discovered by looking them up on the test itself.
|
||||
"""
|
||||
# Aliasing as Any avoids mypy errors (attr-defined) when accessing and
|
||||
# setting custom attributes on the decorated function or class.
|
||||
_test: Any = test
|
||||
|
||||
# Using the alias here avoids a mypy error (return-value) later when
|
||||
# ``test`` is returned, because this check results in type refinement.
|
||||
if not callable(_test):
|
||||
raise InvalidArgument(
|
||||
"settings objects can be called as a decorator with @given, "
|
||||
f"but decorated {test=} is not callable."
|
||||
)
|
||||
if inspect.isclass(test):
|
||||
from hypothesis.stateful import RuleBasedStateMachine
|
||||
|
||||
if issubclass(_test, RuleBasedStateMachine):
|
||||
attr_name = "_hypothesis_internal_settings_applied"
|
||||
if getattr(test, attr_name, False):
|
||||
raise InvalidArgument(
|
||||
"Applying the @settings decorator twice would "
|
||||
"overwrite the first version; merge their arguments "
|
||||
"instead."
|
||||
)
|
||||
setattr(test, attr_name, True)
|
||||
_test.TestCase.settings = self
|
||||
return test # type: ignore
|
||||
else:
|
||||
raise InvalidArgument(
|
||||
"@settings(...) can only be used as a decorator on "
|
||||
"functions, or on subclasses of RuleBasedStateMachine."
|
||||
)
|
||||
if hasattr(_test, "_hypothesis_internal_settings_applied"):
|
||||
# Can't use _hypothesis_internal_use_settings as an indicator that
|
||||
# @settings was applied, because @given also assigns that attribute.
|
||||
descr = get_pretty_function_description(test)
|
||||
raise InvalidArgument(
|
||||
f"{descr} has already been decorated with a settings object.\n"
|
||||
f" Previous: {_test._hypothesis_internal_use_settings!r}\n"
|
||||
f" This: {self!r}"
|
||||
)
|
||||
|
||||
_test._hypothesis_internal_use_settings = self
|
||||
_test._hypothesis_internal_settings_applied = True
|
||||
return test
|
||||
|
||||
@classmethod
|
||||
def _define_setting(
|
||||
cls,
|
||||
name,
|
||||
description,
|
||||
*,
|
||||
default,
|
||||
options=None,
|
||||
validator=None,
|
||||
show_default=True,
|
||||
):
|
||||
"""Add a new setting.
|
||||
|
||||
- name is the name of the property that will be used to access the
|
||||
setting. This must be a valid python identifier.
|
||||
- description will appear in the property's docstring
|
||||
- default is the default value. This may be a zero argument
|
||||
function in which case it is evaluated and its result is stored
|
||||
the first time it is accessed on any given settings object.
|
||||
"""
|
||||
if settings.__definitions_are_locked:
|
||||
raise InvalidState(
|
||||
"settings have been locked and may no longer be defined."
|
||||
)
|
||||
if options is not None:
|
||||
options = tuple(options)
|
||||
assert default in options
|
||||
|
||||
def validator(value):
|
||||
if value not in options:
|
||||
msg = f"Invalid {name}, {value!r}. Valid options: {options!r}"
|
||||
raise InvalidArgument(msg)
|
||||
return value
|
||||
|
||||
else:
|
||||
assert validator is not None
|
||||
|
||||
all_settings[name] = Setting(
|
||||
name=name,
|
||||
description=description.strip(),
|
||||
default=default,
|
||||
validator=validator,
|
||||
)
|
||||
setattr(settings, name, settingsProperty(name, show_default))
|
||||
|
||||
@classmethod
|
||||
def lock_further_definitions(cls):
|
||||
settings.__definitions_are_locked = True
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
raise AttributeError("settings objects are immutable")
|
||||
|
||||
def __repr__(self):
|
||||
bits = sorted(f"{name}={getattr(self, name)!r}" for name in all_settings)
|
||||
return "settings({})".format(", ".join(bits))
|
||||
|
||||
def show_changed(self):
|
||||
bits = []
|
||||
for name, setting in all_settings.items():
|
||||
value = getattr(self, name)
|
||||
if value != setting.default:
|
||||
bits.append(f"{name}={value!r}")
|
||||
return ", ".join(sorted(bits, key=len))
|
||||
|
||||
@staticmethod
|
||||
def register_profile(
|
||||
name: str,
|
||||
parent: Optional["settings"] = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Registers a collection of values to be used as a settings profile.
|
||||
|
||||
Settings profiles can be loaded by name - for example, you might
|
||||
create a 'fast' profile which runs fewer examples, keep the 'default'
|
||||
profile, and create a 'ci' profile that increases the number of
|
||||
examples and uses a different database to store failures.
|
||||
|
||||
The arguments to this method are exactly as for
|
||||
:class:`~hypothesis.settings`: optional ``parent`` settings, and
|
||||
keyword arguments for each setting that will be set differently to
|
||||
parent (or settings.default, if parent is None).
|
||||
"""
|
||||
check_type(str, name, "name")
|
||||
settings._profiles[name] = settings(parent=parent, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def get_profile(name: str) -> "settings":
|
||||
"""Return the profile with the given name."""
|
||||
check_type(str, name, "name")
|
||||
try:
|
||||
return settings._profiles[name]
|
||||
except KeyError:
|
||||
raise InvalidArgument(f"Profile {name!r} is not registered") from None
|
||||
|
||||
@staticmethod
|
||||
def load_profile(name: str) -> None:
|
||||
"""Loads in the settings defined in the profile provided.
|
||||
|
||||
If the profile does not exist, InvalidArgument will be raised.
|
||||
Any setting not defined in the profile will be the library
|
||||
defined default for that setting.
|
||||
"""
|
||||
check_type(str, name, "name")
|
||||
settings._current_profile = name
|
||||
settings._assign_default_internal(settings.get_profile(name))
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def local_settings(s):
|
||||
with default_variable.with_value(s):
|
||||
yield s
|
||||
|
||||
|
||||
@attr.s()
|
||||
class Setting:
|
||||
name = attr.ib()
|
||||
description = attr.ib()
|
||||
default = attr.ib()
|
||||
validator = attr.ib()
|
||||
|
||||
|
||||
def _max_examples_validator(x):
|
||||
check_type(int, x, name="max_examples")
|
||||
if x < 1:
|
||||
raise InvalidArgument(
|
||||
f"max_examples={x!r} should be at least one. You can disable "
|
||||
"example generation with the `phases` setting instead."
|
||||
)
|
||||
return x
|
||||
|
||||
|
||||
settings._define_setting(
|
||||
"max_examples",
|
||||
default=100,
|
||||
validator=_max_examples_validator,
|
||||
description="""
|
||||
Once this many satisfying examples have been considered without finding any
|
||||
counter-example, Hypothesis will stop looking.
|
||||
|
||||
Note that we might call your test function fewer times if we find a bug early
|
||||
or can tell that we've exhausted the search space; or more if we discard some
|
||||
examples due to use of .filter(), assume(), or a few other things that can
|
||||
prevent the test case from completing successfully.
|
||||
|
||||
The default value is chosen to suit a workflow where the test will be part of
|
||||
a suite that is regularly executed locally or on a CI server, balancing total
|
||||
running time against the chance of missing a bug.
|
||||
|
||||
If you are writing one-off tests, running tens of thousands of examples is
|
||||
quite reasonable as Hypothesis may miss uncommon bugs with default settings.
|
||||
For very complex code, we have observed Hypothesis finding novel bugs after
|
||||
*several million* examples while testing :pypi:`SymPy`.
|
||||
If you are running more than 100k examples for a test, consider using our
|
||||
:ref:`integration for coverage-guided fuzzing <fuzz_one_input>` - it really
|
||||
shines when given minutes or hours to run.
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
settings._define_setting(
|
||||
"derandomize",
|
||||
default=False,
|
||||
options=(True, False),
|
||||
description="""
|
||||
If True, seed Hypothesis' random number generator using a hash of the test
|
||||
function, so that every run will test the same set of examples until you
|
||||
update Hypothesis, Python, or the test function.
|
||||
|
||||
This allows you to `check for regressions and look for bugs
|
||||
<https://blog.nelhage.com/post/two-kinds-of-testing/>`__ using
|
||||
:ref:`separate settings profiles <settings_profiles>` - for example running
|
||||
quick deterministic tests on every commit, and a longer non-deterministic
|
||||
nightly testing run.
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def _validate_database(db):
|
||||
from hypothesis.database import ExampleDatabase
|
||||
|
||||
if db is None or isinstance(db, ExampleDatabase):
|
||||
return db
|
||||
raise InvalidArgument(
|
||||
"Arguments to the database setting must be None or an instance of "
|
||||
f"ExampleDatabase. Try passing database=ExampleDatabase({db!r}), or "
|
||||
"construct and use one of the specific subclasses in "
|
||||
"hypothesis.database"
|
||||
)
|
||||
|
||||
|
||||
settings._define_setting(
|
||||
"database",
|
||||
default=not_set,
|
||||
show_default=False,
|
||||
description="""
|
||||
An instance of :class:`~hypothesis.database.ExampleDatabase` that will be
|
||||
used to save examples to and load previous examples from. May be ``None``
|
||||
in which case no storage will be used.
|
||||
|
||||
See the :doc:`example database documentation <database>` for a list of built-in
|
||||
example database implementations, and how to define custom implementations.
|
||||
""",
|
||||
validator=_validate_database,
|
||||
)
|
||||
|
||||
|
||||
@unique
|
||||
class Phase(IntEnum):
|
||||
explicit = 0 #: controls whether explicit examples are run.
|
||||
reuse = 1 #: controls whether previous examples will be reused.
|
||||
generate = 2 #: controls whether new examples will be generated.
|
||||
target = 3 #: controls whether examples will be mutated for targeting.
|
||||
shrink = 4 #: controls whether examples will be shrunk.
|
||||
explain = 5 #: controls whether Hypothesis attempts to explain test failures.
|
||||
|
||||
def __repr__(self):
|
||||
return f"Phase.{self.name}"
|
||||
|
||||
|
||||
class HealthCheckMeta(EnumMeta):
|
||||
def __iter__(self):
|
||||
deprecated = (HealthCheck.return_value, HealthCheck.not_a_test_method)
|
||||
return iter(x for x in super().__iter__() if x not in deprecated)
|
||||
|
||||
|
||||
@unique
|
||||
class HealthCheck(Enum, metaclass=HealthCheckMeta):
|
||||
"""Arguments for :attr:`~hypothesis.settings.suppress_health_check`.
|
||||
|
||||
Each member of this enum is a type of health check to suppress.
|
||||
"""
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}.{self.name}"
|
||||
|
||||
@classmethod
|
||||
def all(cls) -> List["HealthCheck"]:
|
||||
# Skipping of deprecated attributes is handled in HealthCheckMeta.__iter__
|
||||
note_deprecation(
|
||||
"`Healthcheck.all()` is deprecated; use `list(HealthCheck)` instead.",
|
||||
since="2023-04-16",
|
||||
has_codemod=True,
|
||||
stacklevel=1,
|
||||
)
|
||||
return list(HealthCheck)
|
||||
|
||||
data_too_large = 1
|
||||
"""Checks if too many examples are aborted for being too large.
|
||||
|
||||
This is measured by the number of random choices that Hypothesis makes
|
||||
in order to generate something, not the size of the generated object.
|
||||
For example, choosing a 100MB object from a predefined list would take
|
||||
only a few bits, while generating 10KB of JSON from scratch might trigger
|
||||
this health check.
|
||||
"""
|
||||
|
||||
filter_too_much = 2
|
||||
"""Check for when the test is filtering out too many examples, either
|
||||
through use of :func:`~hypothesis.assume()` or :ref:`filter() <filtering>`,
|
||||
or occasionally for Hypothesis internal reasons."""
|
||||
|
||||
too_slow = 3
|
||||
"""Check for when your data generation is extremely slow and likely to hurt
|
||||
testing."""
|
||||
|
||||
return_value = 5
|
||||
"""Deprecated; we always error if a test returns a non-None value."""
|
||||
|
||||
large_base_example = 7
|
||||
"""Checks if the natural example to shrink towards is very large."""
|
||||
|
||||
not_a_test_method = 8
|
||||
"""Deprecated; we always error if :func:`@given <hypothesis.given>` is applied
|
||||
to a method defined by :class:`python:unittest.TestCase` (i.e. not a test)."""
|
||||
|
||||
function_scoped_fixture = 9
|
||||
"""Checks if :func:`@given <hypothesis.given>` has been applied to a test
|
||||
with a pytest function-scoped fixture. Function-scoped fixtures run once
|
||||
for the whole function, not once per example, and this is usually not what
|
||||
you want.
|
||||
|
||||
Because of this limitation, tests that need to set up or reset
|
||||
state for every example need to do so manually within the test itself,
|
||||
typically using an appropriate context manager.
|
||||
|
||||
Suppress this health check only in the rare case that you are using a
|
||||
function-scoped fixture that does not need to be reset between individual
|
||||
examples, but for some reason you cannot use a wider fixture scope
|
||||
(e.g. session scope, module scope, class scope).
|
||||
|
||||
This check requires the :ref:`Hypothesis pytest plugin<pytest-plugin>`,
|
||||
which is enabled by default when running Hypothesis inside pytest."""
|
||||
|
||||
differing_executors = 10
|
||||
"""Checks if :func:`@given <hypothesis.given>` has been applied to a test
|
||||
which is executed by different :ref:`executors<custom-function-execution>`.
|
||||
If your test function is defined as a method on a class, that class will be
|
||||
your executor, and subclasses executing an inherited test is a common way
|
||||
for things to go wrong.
|
||||
|
||||
The correct fix is often to bring the executor instance under the control
|
||||
of hypothesis by explicit parametrization over, or sampling from,
|
||||
subclasses, or to refactor so that :func:`@given <hypothesis.given>` is
|
||||
specified on leaf subclasses."""
|
||||
|
||||
|
||||
@unique
|
||||
class Verbosity(IntEnum):
|
||||
quiet = 0
|
||||
normal = 1
|
||||
verbose = 2
|
||||
debug = 3
|
||||
|
||||
def __repr__(self):
|
||||
return f"Verbosity.{self.name}"
|
||||
|
||||
|
||||
settings._define_setting(
|
||||
"verbosity",
|
||||
options=tuple(Verbosity),
|
||||
default=Verbosity.normal,
|
||||
description="Control the verbosity level of Hypothesis messages",
|
||||
)
|
||||
|
||||
|
||||
def _validate_phases(phases):
|
||||
phases = tuple(phases)
|
||||
for a in phases:
|
||||
if not isinstance(a, Phase):
|
||||
raise InvalidArgument(f"{a!r} is not a valid phase")
|
||||
return tuple(p for p in list(Phase) if p in phases)
|
||||
|
||||
|
||||
settings._define_setting(
|
||||
"phases",
|
||||
default=tuple(Phase),
|
||||
description=(
|
||||
"Control which phases should be run. "
|
||||
"See :ref:`the full documentation for more details <phases>`"
|
||||
),
|
||||
validator=_validate_phases,
|
||||
)
|
||||
|
||||
|
||||
def _validate_stateful_step_count(x):
|
||||
check_type(int, x, name="stateful_step_count")
|
||||
if x < 1:
|
||||
raise InvalidArgument(f"stateful_step_count={x!r} must be at least one.")
|
||||
return x
|
||||
|
||||
|
||||
settings._define_setting(
|
||||
name="stateful_step_count",
|
||||
default=50,
|
||||
validator=_validate_stateful_step_count,
|
||||
description="""
|
||||
Number of steps to run a stateful program for before giving up on it breaking.
|
||||
""",
|
||||
)
|
||||
|
||||
settings._define_setting(
|
||||
name="report_multiple_bugs",
|
||||
default=True,
|
||||
options=(True, False),
|
||||
description="""
|
||||
Because Hypothesis runs the test many times, it can sometimes find multiple
|
||||
bugs in a single run. Reporting all of them at once is usually very useful,
|
||||
but replacing the exceptions can occasionally clash with debuggers.
|
||||
If disabled, only the exception with the smallest minimal example is raised.
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def validate_health_check_suppressions(suppressions):
|
||||
suppressions = try_convert(list, suppressions, "suppress_health_check")
|
||||
for s in suppressions:
|
||||
if not isinstance(s, HealthCheck):
|
||||
raise InvalidArgument(
|
||||
f"Non-HealthCheck value {s!r} of type {type(s).__name__} "
|
||||
"is invalid in suppress_health_check."
|
||||
)
|
||||
if s in (HealthCheck.return_value, HealthCheck.not_a_test_method):
|
||||
note_deprecation(
|
||||
f"The {s.name} health check is deprecated, because this is always an error.",
|
||||
since="2023-03-15",
|
||||
has_codemod=False,
|
||||
stacklevel=2,
|
||||
)
|
||||
return suppressions
|
||||
|
||||
|
||||
settings._define_setting(
|
||||
"suppress_health_check",
|
||||
default=(),
|
||||
description="""A list of :class:`~hypothesis.HealthCheck` items to disable.""",
|
||||
validator=validate_health_check_suppressions,
|
||||
)
|
||||
|
||||
|
||||
class duration(datetime.timedelta):
|
||||
"""A timedelta specifically measured in milliseconds."""
|
||||
|
||||
def __repr__(self):
|
||||
ms = self.total_seconds() * 1000
|
||||
return f"timedelta(milliseconds={int(ms) if ms == int(ms) else ms!r})"
|
||||
|
||||
|
||||
def _validate_deadline(x):
|
||||
if x is None:
|
||||
return x
|
||||
invalid_deadline_error = InvalidArgument(
|
||||
f"deadline={x!r} (type {type(x).__name__}) must be a timedelta object, "
|
||||
"an integer or float number of milliseconds, or None to disable the "
|
||||
"per-test-case deadline."
|
||||
)
|
||||
if isinstance(x, (int, float)):
|
||||
if isinstance(x, bool):
|
||||
raise invalid_deadline_error
|
||||
try:
|
||||
x = duration(milliseconds=x)
|
||||
except OverflowError:
|
||||
raise InvalidArgument(
|
||||
f"deadline={x!r} is invalid, because it is too large to represent "
|
||||
"as a timedelta. Use deadline=None to disable deadlines."
|
||||
) from None
|
||||
if isinstance(x, datetime.timedelta):
|
||||
if x <= datetime.timedelta(0):
|
||||
raise InvalidArgument(
|
||||
f"deadline={x!r} is invalid, because it is impossible to meet a "
|
||||
"deadline <= 0. Use deadline=None to disable deadlines."
|
||||
)
|
||||
return duration(seconds=x.total_seconds())
|
||||
raise invalid_deadline_error
|
||||
|
||||
|
||||
settings._define_setting(
|
||||
"deadline",
|
||||
default=duration(milliseconds=200),
|
||||
validator=_validate_deadline,
|
||||
description="""
|
||||
If set, a duration (as timedelta, or integer or float number of milliseconds)
|
||||
that each individual example (i.e. each time your test
|
||||
function is called, not the whole decorated test) within a test is not
|
||||
allowed to exceed. Tests which take longer than that may be converted into
|
||||
errors (but will not necessarily be if close to the deadline, to allow some
|
||||
variability in test run time).
|
||||
|
||||
Set this to ``None`` to disable this behaviour entirely.
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
def is_in_ci() -> bool:
|
||||
# GitHub Actions, Travis CI and AppVeyor have "CI"
|
||||
# Azure Pipelines has "TF_BUILD"
|
||||
return "CI" in os.environ or "TF_BUILD" in os.environ
|
||||
|
||||
|
||||
settings._define_setting(
|
||||
"print_blob",
|
||||
default=is_in_ci(),
|
||||
show_default=False,
|
||||
options=(True, False),
|
||||
description="""
|
||||
If set to ``True``, Hypothesis will print code for failing examples that can be used with
|
||||
:func:`@reproduce_failure <hypothesis.reproduce_failure>` to reproduce the failing example.
|
||||
The default is ``True`` if the ``CI`` or ``TF_BUILD`` env vars are set, ``False`` otherwise.
|
||||
""",
|
||||
)
|
||||
|
||||
settings.lock_further_definitions()
|
||||
|
||||
|
||||
def note_deprecation(
|
||||
message: str, *, since: str, has_codemod: bool, stacklevel: int = 0
|
||||
) -> None:
|
||||
if since != "RELEASEDAY":
|
||||
date = datetime.date.fromisoformat(since)
|
||||
assert datetime.date(2021, 1, 1) <= date
|
||||
if has_codemod:
|
||||
message += (
|
||||
"\n The `hypothesis codemod` command-line tool can automatically "
|
||||
"refactor your code to fix this warning."
|
||||
)
|
||||
warnings.warn(HypothesisDeprecationWarning(message), stacklevel=2 + stacklevel)
|
||||
|
||||
|
||||
settings.register_profile("default", settings())
|
||||
settings.load_profile("default")
|
||||
assert settings.default is not None
|
||||
|
||||
|
||||
# Check that the kwonly args to settings.__init__ is the same as the set of
|
||||
# defined settings - in case we've added or remove something from one but
|
||||
# not the other.
|
||||
assert set(all_settings) == {
|
||||
p.name
|
||||
for p in inspect.signature(settings.__init__).parameters.values()
|
||||
if p.kind == inspect.Parameter.KEYWORD_ONLY
|
||||
}
|
||||
Reference in New Issue
Block a user