diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-01-30 10:54:10 +0300 |
---|---|---|
committer | Alexander Smirnov <alex@ydb.tech> | 2024-01-31 14:24:07 +0300 |
commit | 324e7ada8dba65f92c7a63af6286ff1332a1cd9e (patch) | |
tree | 323a863c67efaa91306a789aa26dd0df4258e29a /contrib/python | |
parent | 3cae04dfa7fb9b08b4204c3459fbe5394867e2a7 (diff) | |
download | ydb-324e7ada8dba65f92c7a63af6286ff1332a1cd9e.tar.gz |
Intermediate changes
Diffstat (limited to 'contrib/python')
10 files changed, 90 insertions, 54 deletions
diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA index 3c60980953f..fd179215d83 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.0 +Version: 6.93.2 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/hypothesis/control.py b/contrib/python/hypothesis/py3/hypothesis/control.py index 3a973f666f9..5b662197a79 100644 --- a/contrib/python/hypothesis/py3/hypothesis/control.py +++ b/contrib/python/hypothesis/py3/hypothesis/control.py @@ -105,16 +105,13 @@ class BuildContext: ) ) - def prep_args_kwargs_from_strategies(self, arg_strategies, kwarg_strategies): + def prep_args_kwargs_from_strategies(self, kwarg_strategies): arg_labels = {} - all_s = [(None, s) for s in arg_strategies] + list(kwarg_strategies.items()) - args = [] kwargs = {} - for i, (k, s) in enumerate(all_s): + for k, s in kwarg_strategies.items(): start_idx = self.data.index - obj = self.data.draw(s) + obj = self.data.draw(s, observe_as=f"generate:{k}") end_idx = self.data.index - assert k is not None kwargs[k] = obj # This high up the stack, we can't see or really do much with the conjecture @@ -124,10 +121,10 @@ class BuildContext: # pass a dict of such out so that the pretty-printer knows where to place # the which-parts-matter comments later. if start_idx != end_idx: - arg_labels[k or i] = (start_idx, end_idx) + arg_labels[k] = (start_idx, end_idx) self.data.arg_slices.add((start_idx, end_idx)) - return args, kwargs, arg_labels + return kwargs, arg_labels def __enter__(self): self.assign_variable = _current_build_context.with_value(self) diff --git a/contrib/python/hypothesis/py3/hypothesis/core.py b/contrib/python/hypothesis/py3/hypothesis/core.py index 86b20ea6f98..75f1cc70e98 100644 --- a/contrib/python/hypothesis/py3/hypothesis/core.py +++ b/contrib/python/hypothesis/py3/hypothesis/core.py @@ -15,6 +15,7 @@ import contextlib import datetime import inspect import io +import math import sys import time import types @@ -605,6 +606,7 @@ def execute_explicit_examples(state, wrapped_test, arguments, kwargs, original_s data=empty_data, how_generated="explicit example", string_repr=state._string_repr, + timing=state._timing_features, ) deliver_json_blob(tc) @@ -816,34 +818,30 @@ class StateForActualGivenExecution: self._string_repr = "" text_repr = None - if self.settings.deadline is None: + if self.settings.deadline is None and not TESTCASE_CALLBACKS: test = self.test else: @proxies(self.test) def test(*args, **kwargs): - arg_drawtime = sum(data.draw_times) - initial_draws = len(data.draw_times) + arg_drawtime = math.fsum(data.draw_times.values()) start = time.perf_counter() try: result = self.test(*args, **kwargs) finally: finish = time.perf_counter() - internal_draw_time = sum(data.draw_times[initial_draws:]) - runtime = datetime.timedelta( - seconds=finish - start - internal_draw_time - ) + in_drawtime = math.fsum(data.draw_times.values()) - arg_drawtime + runtime = datetime.timedelta(seconds=finish - start - in_drawtime) self._timing_features = { - "time_running_test": finish - start - internal_draw_time, - "time_drawing_args": arg_drawtime, - "time_interactive_draws": internal_draw_time, + "execute_test": finish - start - in_drawtime, + **data.draw_times, } - current_deadline = self.settings.deadline - if not is_final: - current_deadline = (current_deadline // 4) * 5 - if runtime >= current_deadline: - raise DeadlineExceeded(runtime, self.settings.deadline) + if (current_deadline := self.settings.deadline) is not None: + if not is_final: + current_deadline = (current_deadline // 4) * 5 + if runtime >= current_deadline: + raise DeadlineExceeded(runtime, self.settings.deadline) return result def run(data): @@ -854,10 +852,9 @@ class StateForActualGivenExecution: args = self.stuff.args kwargs = dict(self.stuff.kwargs) if example_kwargs is None: - a, kw, argslices = context.prep_args_kwargs_from_strategies( - (), self.stuff.given_kwargs + kw, argslices = context.prep_args_kwargs_from_strategies( + self.stuff.given_kwargs ) - assert not a, "strategies all moved to kwargs by now" else: kw = example_kwargs argslices = {} @@ -944,7 +941,7 @@ class StateForActualGivenExecution: if expected_failure is not None: exception, traceback = expected_failure if isinstance(exception, DeadlineExceeded) and ( - runtime_secs := self._timing_features.get("time_running_test") + runtime_secs := self._timing_features.get("execute_test") ): report( "Unreliable test timings! On an initial run, this " @@ -1066,11 +1063,12 @@ class StateForActualGivenExecution: how_generated=f"generated during {phase} phase", string_repr=self._string_repr, arguments={**self._jsonable_arguments, **data._observability_args}, - metadata=self._timing_features, + timing=self._timing_features, + metadata={}, coverage=tractable_coverage_report(trace) or None, ) deliver_json_blob(tc) - self._timing_features.clear() + self._timing_features = {} def run_engine(self): """Run the test function many times, on database input and generated @@ -1184,17 +1182,17 @@ class StateForActualGivenExecution: "status": "passed" if sys.exc_info()[0] else "failed", "status_reason": str(origin or "unexpected/flaky pass"), "representation": self._string_repr, + "arguments": self._jsonable_arguments, "how_generated": "minimal failing example", "features": { **{ - k: v + f"target:{k}".strip(":"): v for k, v in ran_example.target_observations.items() - if isinstance(k, str) }, **ran_example.events, - **self._timing_features, }, - "coverage": None, # TODO: expose this? + "timing": self._timing_features, + "coverage": None, # Not recorded when we're replaying the MFE "metadata": {"traceback": tb}, } deliver_json_blob(tc) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py index 1939e337b37..03f489fa50d 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py @@ -1429,7 +1429,7 @@ class ConjectureData: self.events: Dict[str, Union[str, int, float]] = {} self.forced_indices: "Set[int]" = set() self.interesting_origin: Optional[InterestingOrigin] = None - self.draw_times: "List[float]" = [] + self.draw_times: "Dict[str, float]" = {} self.max_depth = 0 self.has_discards = False self.provider = PrimitiveProvider(self) @@ -1553,6 +1553,8 @@ class ConjectureData: def draw_bytes(self, size: int, *, forced: Optional[bytes] = None) -> bytes: assert forced is None or len(forced) == size + assert size >= 0 + return self.provider.draw_bytes(size, forced=forced) def draw_boolean(self, p: float = 0.5, *, forced: Optional[bool] = None) -> bool: @@ -1597,7 +1599,12 @@ class ConjectureData: value = repr(value) self.output += value - def draw(self, strategy: "SearchStrategy[Ex]", label: Optional[int] = None) -> "Ex": + def draw( + self, + strategy: "SearchStrategy[Ex]", + label: Optional[int] = None, + observe_as: Optional[str] = None, + ) -> "Ex": if self.is_find and not strategy.supports_find: raise InvalidArgument( f"Cannot use strategy {strategy!r} within a call to find " @@ -1634,7 +1641,8 @@ class ConjectureData: try: return strategy.do_draw(self) finally: - self.draw_times.append(time.perf_counter() - start_time) + key = observe_as or f"unlabeled_{len(self.draw_times)}" + self.draw_times[key] = time.perf_counter() - start_time finally: self.stop_example() @@ -1778,15 +1786,6 @@ class ConjectureData: assert result.bit_length() <= n return result - def write(self, string: bytes) -> Optional[bytes]: - """Write ``string`` to the output buffer.""" - self.__assert_not_frozen("write") - string = bytes(string) - if not string: - return None - self.draw_bits(len(string) * 8, forced=int_from_bytes(string)) - return self.buffer[-len(string) :] - def __check_capacity(self, n: int) -> None: if self.index + n > self.max_length: self.mark_overrun() diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py index c5d33480e24..961774816f8 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py @@ -53,7 +53,39 @@ class HealthCheckState: valid_examples: int = attr.ib(default=0) invalid_examples: int = attr.ib(default=0) overrun_examples: int = attr.ib(default=0) - draw_times: list = attr.ib(factory=list) + draw_times: "defaultdict[str, list[float]]" = attr.ib( + factory=lambda: defaultdict(list) + ) + + @property + def total_draw_time(self): + return math.fsum(sum(self.draw_times.values(), start=[])) + + def timing_report(self): + """Return a terminal report describing what was slow.""" + if not self.draw_times: + return "" + width = max(len(k[len("generate:") :].strip(": ")) for k in self.draw_times) + out = [f"\n {'':^{width}} count | fraction | slowest draws (seconds)"] + args_in_order = sorted(self.draw_times.items(), key=lambda kv: -sum(kv[1])) + for i, (argname, times) in enumerate(args_in_order): # pragma: no branch + # If we have very many unique keys, which can happen due to interactive + # draws with computed labels, we'll skip uninformative rows. + if ( + 5 <= i < (len(self.draw_times) - 2) + and math.fsum(times) * 20 < self.total_draw_time + ): + out.append(f" (skipped {len(self.draw_times) - i} rows of fast draws)") + break + # Compute the row to report, omitting times <1ms to focus on slow draws + reprs = [f"{t:>6.3f}," for t in sorted(times)[-5:] if t > 5e-4] + desc = " ".join(([" -- "] * 5 + reprs)[-5:]).rstrip(",") + arg = argname[len("generate:") :].strip(": ") # removeprefix in py3.9 + out.append( + f" {arg:^{width}} | {len(times):>4} | " + f"{math.fsum(times)/self.total_draw_time:>7.0%} | {desc}" + ) + return "\n".join(out) class ExitReason(Enum): @@ -205,7 +237,7 @@ class ConjectureRunner: call_stats = { "status": data.status.name.lower(), "runtime": data.finish_time - data.start_time, - "drawtime": math.fsum(data.draw_times), + "drawtime": math.fsum(data.draw_times.values()), "events": sorted( k if v == "" else f"{k}: {v}" for k, v in data.events.items() ), @@ -328,7 +360,8 @@ class ConjectureRunner: if state is None: return - state.draw_times.extend(data.draw_times) + for k, v in data.draw_times.items(): + state.draw_times[k].append(v) if data.status == Status.VALID: state.valid_examples += 1 @@ -371,7 +404,7 @@ class ConjectureRunner: HealthCheck.filter_too_much, ) - draw_time = sum(state.draw_times) + draw_time = state.total_draw_time # Allow at least the greater of one second or 5x the deadline. If deadline # is None, allow 30s - the user can disable the healthcheck too if desired. @@ -383,7 +416,8 @@ class ConjectureRunner: f"{state.valid_examples} valid examples in {draw_time:.2f} seconds " f"({state.invalid_examples} invalid ones and {state.overrun_examples} " "exceeded maximum size). Try decreasing size of the data you're " - "generating (with e.g. max_size or max_leaves parameters).", + "generating (with e.g. max_size or max_leaves parameters)." + + state.timing_report(), HealthCheck.too_slow, ) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/observability.py b/contrib/python/hypothesis/py3/hypothesis/internal/observability.py index 0da4aca7649..284a2072d75 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/observability.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/observability.py @@ -37,6 +37,7 @@ def make_testcase( how_generated: str = "unknown", string_repr: str = "<unknown>", arguments: Optional[dict] = None, + timing: Dict[str, float], metadata: Optional[dict] = None, coverage: Optional[Dict[str, List[int]]] = None, ) -> dict: @@ -65,6 +66,7 @@ def make_testcase( }, **data.events, }, + "timing": timing, "metadata": { **(metadata or {}), "traceback": getattr(data.extra_information, "_expected_traceback", None), diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py index b3a558c3068..804901268bb 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py @@ -2100,10 +2100,10 @@ class DataObject: def draw(self, strategy: SearchStrategy[Ex], label: Any = None) -> Ex: check_strategy(strategy, "strategy") - result = self.conjecture_data.draw(strategy) self.count += 1 printer = RepresentationPrinter(context=current_build_context()) desc = f"Draw {self.count}{'' if label is None else f' ({label})'}: " + result = self.conjecture_data.draw(strategy, observe_as=f"generate:{desc}") if TESTCASE_CALLBACKS: self.conjecture_data._observability_args[desc] = to_jsonable(result) diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py index b2a7661cd6b..bd56d2287ea 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py @@ -184,5 +184,11 @@ def to_jsonable(obj: object) -> object: if (pyd := sys.modules.get("pydantic")) and isinstance(obj, pyd.BaseModel): return to_jsonable(obj.model_dump()) + # Hey, might as well try calling a .to_json() method - it works for Pandas! + try: + return to_jsonable(obj.to_json()) # type: ignore + except Exception: + pass + # If all else fails, we'll just pretty-print as a string. return pretty(obj) diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py index c06671451dd..dc8e1e70f09 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, 0) +__version_info__ = (6, 93, 2) __version__ = ".".join(map(str, __version_info__)) diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make index 729a0324978..3e25d0b11c8 100644 --- a/contrib/python/hypothesis/py3/ya.make +++ b/contrib/python/hypothesis/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.93.0) +VERSION(6.93.2) LICENSE(MPL-2.0) |