diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-01-31 11:50:40 +0300 |
---|---|---|
committer | Alexander Smirnov <alex@ydb.tech> | 2024-01-31 14:24:23 +0300 |
commit | 24224c8cb74d4d8a6cc0fe7ecb4e78e315e0d945 (patch) | |
tree | 67f82419968d3906da780c7a4cc7b6cb68e50050 | |
parent | 636f483b4d31b42e989912db9acb7b451ce0b477 (diff) | |
download | ydb-24224c8cb74d4d8a6cc0fe7ecb4e78e315e0d945.tar.gz |
Intermediate changes
14 files changed, 157 insertions, 9 deletions
diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA index fd179215d8..c94200ebd3 100644 --- a/contrib/python/hypothesis/py3/.dist-info/METADATA +++ b/contrib/python/hypothesis/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: hypothesis -Version: 6.93.2 +Version: 6.94.0 Summary: A library for property-based testing Home-page: https://hypothesis.works Author: David R. MacIver and Zac Hatfield-Dodds diff --git a/contrib/python/hypothesis/py3/.dist-info/top_level.txt b/contrib/python/hypothesis/py3/.dist-info/top_level.txt index 93b8370b8d..77a969c858 100644 --- a/contrib/python/hypothesis/py3/.dist-info/top_level.txt +++ b/contrib/python/hypothesis/py3/.dist-info/top_level.txt @@ -1,3 +1,4 @@ _hypothesis_ftz_detector +_hypothesis_globals _hypothesis_pytestplugin hypothesis diff --git a/contrib/python/hypothesis/py3/_hypothesis_globals.py b/contrib/python/hypothesis/py3/_hypothesis_globals.py new file mode 100644 index 0000000000..e97e091879 --- /dev/null +++ b/contrib/python/hypothesis/py3/_hypothesis_globals.py @@ -0,0 +1,28 @@ +# 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/. + +""" +Module for globals shared between plugin(s) and the main hypothesis module, without +depending on either. This file should have no imports outside of stdlib. +""" + +import os + +in_initialization = 1 +"""If nonzero, indicates that hypothesis is still initializing (importing or loading +the test environment). `import hypothesis` will cause this number to be decremented, +and the pytest plugin increments at load time, then decrements it just before the test +session starts. However, this leads to a hole in coverage if another pytest plugin +imports hypothesis before our plugin is loaded. HYPOTHESIS_EXTEND_INITIALIZATION may +be set to pre-increment the value on behalf of _hypothesis_pytestplugin, plugging the +hole.""" + +if os.environ.get("HYPOTHESIS_EXTEND_INITIALIZATION"): + in_initialization += 1 diff --git a/contrib/python/hypothesis/py3/_hypothesis_pytestplugin.py b/contrib/python/hypothesis/py3/_hypothesis_pytestplugin.py index 9875e067f5..944304ccdb 100644 --- a/contrib/python/hypothesis/py3/_hypothesis_pytestplugin.py +++ b/contrib/python/hypothesis/py3/_hypothesis_pytestplugin.py @@ -21,9 +21,12 @@ See https://github.com/HypothesisWorks/hypothesis/issues/3140 for details. import base64 import json +import os import sys +import warnings from inspect import signature +import _hypothesis_globals import pytest try: @@ -94,6 +97,19 @@ if tuple(map(int, pytest.__version__.split(".")[:2])) < (4, 6): # pragma: no co warnings.warn(PYTEST_TOO_OLD_MESSAGE % (pytest.__version__,), stacklevel=1) else: + # Restart side-effect detection as early as possible, to maximize coverage. We + # need balanced increment/decrement in configure/sessionstart to support nested + # pytest (e.g. runpytest_inprocess), so this early increment in effect replaces + # the first one in pytest_configure. + _configured = False + if not os.environ.get("HYPOTHESIS_EXTEND_INITIALIZATION"): + _hypothesis_globals.in_initialization += 1 + if "hypothesis" in sys.modules: + # Some other plugin has imported hypothesis, so we'll check if there + # have been undetected side-effects and warn if so. + from hypothesis.configuration import notice_initialization_restarted + + notice_initialization_restarted() def pytest_addoption(parser): group = parser.getgroup("hypothesis", "Hypothesis") @@ -147,6 +163,12 @@ else: return f"hypothesis profile {settings._current_profile!r}{settings_str}" def pytest_configure(config): + global _configured + # skip first increment because we pre-incremented at import time + if _configured: + _hypothesis_globals.in_initialization += 1 + _configured = True + config.addinivalue_line("markers", "hypothesis: Tests which use hypothesis.") if not _any_hypothesis_option(config): return @@ -407,6 +429,9 @@ else: if isinstance(item, pytest.Function) and is_hypothesis_test(item.obj): item.add_marker("hypothesis") + def pytest_sessionstart(session): + _hypothesis_globals.in_initialization -= 1 + # Monkeypatch some internals to prevent applying @pytest.fixture() to a # function which has already been decorated with @hypothesis.given(). # (the reverse case is already an explicit error in Hypothesis) diff --git a/contrib/python/hypothesis/py3/hypothesis/__init__.py b/contrib/python/hypothesis/py3/hypothesis/__init__.py index db140b8165..cfb55119f7 100644 --- a/contrib/python/hypothesis/py3/hypothesis/__init__.py +++ b/contrib/python/hypothesis/py3/hypothesis/__init__.py @@ -15,6 +15,8 @@ It verifies your code against a wide range of input and minimizes any failing examples it finds. """ +import _hypothesis_globals + from hypothesis._settings import HealthCheck, Phase, Verbosity, settings from hypothesis.control import ( assume, @@ -54,3 +56,6 @@ __all__ = [ run() del run + +_hypothesis_globals.in_initialization -= 1 +del _hypothesis_globals diff --git a/contrib/python/hypothesis/py3/hypothesis/configuration.py b/contrib/python/hypothesis/py3/hypothesis/configuration.py index 6e6ab29516..2586c729f7 100644 --- a/contrib/python/hypothesis/py3/hypothesis/configuration.py +++ b/contrib/python/hypothesis/py3/hypothesis/configuration.py @@ -9,8 +9,13 @@ # obtain one at https://mozilla.org/MPL/2.0/. import os +import warnings from pathlib import Path +import _hypothesis_globals + +from hypothesis.errors import HypothesisSideeffectWarning + __hypothesis_home_directory_default = Path.cwd() / ".hypothesis" __hypothesis_home_directory = None @@ -21,7 +26,12 @@ def set_hypothesis_home_dir(directory): __hypothesis_home_directory = None if directory is None else Path(directory) -def storage_directory(*names): +def storage_directory(*names, intent_to_write=True): + if intent_to_write: + check_sideeffect_during_initialization( + "accessing storage for {}", "/".join(names) + ) + global __hypothesis_home_directory if not __hypothesis_home_directory: if where := os.getenv("HYPOTHESIS_STORAGE_DIRECTORY"): @@ -29,3 +39,53 @@ def storage_directory(*names): if not __hypothesis_home_directory: __hypothesis_home_directory = __hypothesis_home_directory_default return __hypothesis_home_directory.joinpath(*names) + + +_first_postinit_what = None + + +def check_sideeffect_during_initialization( + what: str, *fmt_args: object, extra: str = "" +) -> None: + """Called from locations that should not be executed during initialization, for example + touching disk or materializing lazy/deferred strategies from plugins. If initialization + is in progress, a warning is emitted. + + Note that computing the repr can take nontrivial time or memory, so we avoid doing so + unless (and until) we're actually emitting the warning. + """ + global _first_postinit_what + # This is not a particularly hot path, but neither is it doing productive work, so we want to + # minimize the cost by returning immediately. The drawback is that we require + # notice_initialization_restarted() to be called if in_initialization changes away from zero. + if _first_postinit_what is not None: + return + elif _hypothesis_globals.in_initialization: + # Note: -Werror is insufficient under pytest, as doesn't take effect until + # test session start. + msg = what.format(*fmt_args) + warnings.warn( + f"Slow code in plugin: avoid {msg} at import time! Set PYTHONWARNINGS=error " + "to get a traceback and show which plugin is responsible." + extra, + HypothesisSideeffectWarning, + stacklevel=3, + ) + else: + _first_postinit_what = (what, fmt_args) + + +def notice_initialization_restarted(*, warn: bool = True) -> None: + """Reset _first_postinit_what, so that we don't think we're in post-init. Additionally, if it + was set that means that there has been a sideeffect that we haven't warned about, so do that + now (the warning text will be correct, and we also hint that the stacktrace can be improved). + """ + global _first_postinit_what + if _first_postinit_what is not None: + what, *fmt_args = _first_postinit_what + _first_postinit_what = None + if warn: + check_sideeffect_during_initialization( + what, + *fmt_args, + extra=" Additionally, set HYPOTHESIS_EXTEND_INITIALIZATION=1 to pinpoint the exact location.", + ) diff --git a/contrib/python/hypothesis/py3/hypothesis/database.py b/contrib/python/hypothesis/py3/hypothesis/database.py index 4abce10c01..9fbdd31597 100644 --- a/contrib/python/hypothesis/py3/hypothesis/database.py +++ b/contrib/python/hypothesis/py3/hypothesis/database.py @@ -59,7 +59,7 @@ def _db_for_path(path=None): "https://hypothesis.readthedocs.io/en/latest/settings.html#settings-profiles" ) - path = storage_directory("examples") + path = storage_directory("examples", intent_to_write=False) if not _usable_dir(path): # pragma: no cover warnings.warn( "The database setting is not configured, and the default " @@ -495,6 +495,8 @@ class GitHubArtifactDatabase(ExampleDatabase): self._initialized = True def _initialize_db(self) -> None: + # Trigger warning that we suppressed earlier by intent_to_write=False + storage_directory(self.path.name) # Create the cache directory if it doesn't exist self.path.mkdir(exist_ok=True, parents=True) diff --git a/contrib/python/hypothesis/py3/hypothesis/errors.py b/contrib/python/hypothesis/py3/hypothesis/errors.py index 8387a87586..0d376a7493 100644 --- a/contrib/python/hypothesis/py3/hypothesis/errors.py +++ b/contrib/python/hypothesis/py3/hypothesis/errors.py @@ -117,6 +117,13 @@ class HypothesisDeprecationWarning(HypothesisWarning, FutureWarning): """ +class HypothesisSideeffectWarning(HypothesisWarning): + """A warning issued by Hypothesis when it sees actions that are + discouraged at import or initialization time because they are + slow or have user-visible side effects. + """ + + class Frozen(HypothesisException): """Raised when a mutation method has been called on a ConjectureData object after freeze() has been called.""" diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py index 804901268b..028d8405c7 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py @@ -55,6 +55,7 @@ import attr from hypothesis._settings import note_deprecation from hypothesis.control import cleanup, current_build_context, note from hypothesis.errors import ( + HypothesisSideeffectWarning, HypothesisWarning, InvalidArgument, ResolutionFailed, @@ -2196,14 +2197,25 @@ def register_type_strategy( f"{custom_type=} is not allowed to be registered, " f"because there is no such thing as a runtime instance of {custom_type!r}" ) - elif not (isinstance(strategy, SearchStrategy) or callable(strategy)): + if not (isinstance(strategy, SearchStrategy) or callable(strategy)): raise InvalidArgument( f"{strategy=} must be a SearchStrategy, or a function that takes " "a generic type and returns a specific SearchStrategy" ) - elif isinstance(strategy, SearchStrategy) and strategy.is_empty: - raise InvalidArgument(f"{strategy=} must not be empty") - elif types.has_type_arguments(custom_type): + if isinstance(strategy, SearchStrategy): + with warnings.catch_warnings(): + warnings.simplefilter("error", HypothesisSideeffectWarning) + + # Calling is_empty forces materialization of lazy strategies. If this is done at import + # time, lazy strategies will warn about it; here, we force that warning to raise to + # avoid the materialization. Ideally, we'd just check if the strategy is lazy, but the + # lazy strategy may be wrapped underneath another strategy so that's complicated. + try: + if strategy.is_empty: + raise InvalidArgument(f"{strategy=} must not be empty") + except HypothesisSideeffectWarning: # pragma: no cover + pass + if types.has_type_arguments(custom_type): raise InvalidArgument( f"Cannot register generic type {custom_type!r}, because it has type " "arguments which would not be handled. Instead, register a function " diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/deferred.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/deferred.py index 489b4d7b7a..f7dae9a1e5 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/deferred.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/deferred.py @@ -10,6 +10,7 @@ import inspect +from hypothesis.configuration import check_sideeffect_during_initialization from hypothesis.errors import InvalidArgument from hypothesis.internal.reflection import get_pretty_function_description from hypothesis.strategies._internal.strategies import SearchStrategy, check_strategy @@ -27,6 +28,8 @@ class DeferredStrategy(SearchStrategy): @property def wrapped_strategy(self): if self.__wrapped_strategy is None: + check_sideeffect_during_initialization("deferred evaluation of {!r}", self) + if not inspect.isfunction(self.__definition): raise InvalidArgument( f"Expected definition to be a function but got {self.__definition!r} " diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py index 5e493a9099..d6bb13c7c1 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py @@ -12,6 +12,7 @@ from inspect import signature from typing import MutableMapping from weakref import WeakKeyDictionary +from hypothesis.configuration import check_sideeffect_during_initialization from hypothesis.internal.reflection import ( convert_keyword_arguments, convert_positional_arguments, @@ -100,6 +101,8 @@ class LazyStrategy(SearchStrategy): @property def wrapped_strategy(self): if self.__wrapped_strategy is None: + check_sideeffect_during_initialization("lazy evaluation of {!r}", self) + unwrapped_args = tuple(unwrap_strategies(s) for s in self.__args) unwrapped_kwargs = { k: unwrap_strategies(v) for k, v in self.__kwargs.items() diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py index dc8e1e70f0..fd4613cb61 100644 --- a/contrib/python/hypothesis/py3/hypothesis/version.py +++ b/contrib/python/hypothesis/py3/hypothesis/version.py @@ -8,5 +8,5 @@ # 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/. -__version_info__ = (6, 93, 2) +__version_info__ = (6, 94, 0) __version__ = ".".join(map(str, __version_info__)) diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make index 3e25d0b11c..476e93730e 100644 --- a/contrib/python/hypothesis/py3/ya.make +++ b/contrib/python/hypothesis/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.93.2) +VERSION(6.94.0) LICENSE(MPL-2.0) @@ -20,6 +20,7 @@ NO_CHECK_IMPORTS( PY_SRCS( TOP_LEVEL _hypothesis_ftz_detector.py + _hypothesis_globals.py _hypothesis_pytestplugin.py hypothesis/__init__.py hypothesis/_settings.py diff --git a/yt/yt/client/table_client/schema.cpp b/yt/yt/client/table_client/schema.cpp index 2717aaeb8a..733d997ffc 100644 --- a/yt/yt/client/table_client/schema.cpp +++ b/yt/yt/client/table_client/schema.cpp @@ -1514,6 +1514,7 @@ void ValidateColumnSchema( "first", "xdelta", "_yt_stored_replica_set", + "_yt_last_seen_replica_set", }; const auto& stableName = columnSchema.StableName(); |