aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/hypothesis
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-03-29 09:33:24 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-03-29 09:42:32 +0300
commitbc252a2deca02a342c35fcee243369a761c4f3d4 (patch)
treeb8bbbcf6aa4422c4976fd84c09ff80c3488df416 /contrib/python/hypothesis
parenta21f8f835e55425029f398bd3c1829b01c82f3c8 (diff)
downloadydb-bc252a2deca02a342c35fcee243369a761c4f3d4.tar.gz
Intermediate changes
Diffstat (limited to 'contrib/python/hypothesis')
-rw-r--r--contrib/python/hypothesis/py3/.dist-info/METADATA2
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py71
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py61
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py67
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py1
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/floats.py10
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/version.py2
-rw-r--r--contrib/python/hypothesis/py3/ya.make2
8 files changed, 172 insertions, 44 deletions
diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA
index ff84629a91..492afb5396 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.99.5
+Version: 6.99.6
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/internal/conjecture/data.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
index 40850a2b5b..24e97984fc 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
@@ -920,6 +920,50 @@ class IRNode:
kwargs: IRKWargsType = attr.ib()
was_forced: bool = attr.ib()
+ def copy(self, *, with_value: IRType) -> "IRNode":
+ # we may want to allow this combination in the future, but for now it's
+ # a footgun.
+ assert not self.was_forced, "modifying a forced node doesn't make sense"
+ return IRNode(
+ ir_type=self.ir_type,
+ value=with_value,
+ kwargs=self.kwargs,
+ was_forced=self.was_forced,
+ )
+
+
+def ir_value_permitted(value, ir_type, kwargs):
+ if ir_type == "integer":
+ if kwargs["min_value"] is not None and value < kwargs["min_value"]:
+ return False
+ if kwargs["max_value"] is not None and value > kwargs["max_value"]:
+ return False
+
+ return True
+ elif ir_type == "float":
+ if math.isnan(value):
+ return kwargs["allow_nan"]
+ return (
+ sign_aware_lte(kwargs["min_value"], value)
+ and sign_aware_lte(value, kwargs["max_value"])
+ ) and not (0 < abs(value) < kwargs["smallest_nonzero_magnitude"])
+ elif ir_type == "string":
+ if len(value) < kwargs["min_size"]:
+ return False
+ if kwargs["max_size"] is not None and len(value) > kwargs["max_size"]:
+ return False
+ return all(ord(c) in kwargs["intervals"] for c in value)
+ elif ir_type == "bytes":
+ return len(value) == kwargs["size"]
+ elif ir_type == "boolean":
+ if kwargs["p"] <= 2 ** (-64):
+ return value is False
+ if kwargs["p"] >= (1 - 2 ** (-64)):
+ return value is True
+ return True
+
+ raise NotImplementedError(f"unhandled type {type(value)} of ir value {value}")
+
@dataclass_transform()
@attr.s(slots=True)
@@ -1991,8 +2035,8 @@ class ConjectureData:
p: float = 0.5,
*,
forced: Optional[bool] = None,
- observe: bool = True,
fake_forced: bool = False,
+ observe: bool = True,
) -> bool:
# Internally, we treat probabilities lower than 1 / 2**64 as
# unconditionally false.
@@ -2049,9 +2093,30 @@ class ConjectureData:
def _pop_ir_tree_node(self, ir_type: IRTypeName, kwargs: IRKWargsType) -> IRNode:
assert self.ir_tree_nodes is not None
+
+ if self.ir_tree_nodes == []:
+ self.mark_overrun()
+
node = self.ir_tree_nodes.pop(0)
- assert node.ir_type == ir_type
- assert kwargs == node.kwargs
+ # If we're trying to draw a different ir type at the same location, then
+ # this ir tree has become badly misaligned. We don't have many good/simple
+ # options here for realigning beyond giving up.
+ #
+ # This is more of an issue for ir nodes while shrinking than it was for
+ # buffers: misaligned buffers are still usually valid, just interpreted
+ # differently. This would be somewhat like drawing a random value for
+ # the new ir type here. For what it's worth, misaligned buffers are
+ # rather unlikely to be *useful* buffers, so giving up isn't a big downgrade.
+ # (in fact, it is possible that giving up early here results in more time
+ # for useful shrinks to run).
+ if node.ir_type != ir_type:
+ self.mark_invalid()
+
+ # if a node has different kwargs (and so is misaligned), but has a value
+ # that is allowed by the expected kwargs, then we can coerce this node
+ # into an aligned one by using its value. It's unclear how useful this is.
+ if not ir_value_permitted(node.value, node.ir_type, kwargs):
+ self.mark_invalid()
return node
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py
index c8e5d70aa7..6ab4c2783f 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py
@@ -30,7 +30,12 @@ from hypothesis.internal.conjecture.data import (
Status,
StringKWargs,
)
-from hypothesis.internal.floats import count_between_floats, float_to_int, int_to_float
+from hypothesis.internal.floats import (
+ count_between_floats,
+ float_to_int,
+ int_to_float,
+ sign_aware_lte,
+)
class PreviouslyUnseenBehaviour(HypothesisException):
@@ -184,7 +189,35 @@ def compute_max_children(ir_type, kwargs):
return sum(len(intervals) ** k for k in range(min_size, max_size + 1))
elif ir_type == "float":
- return count_between_floats(kwargs["min_value"], kwargs["max_value"])
+ min_value = kwargs["min_value"]
+ max_value = kwargs["max_value"]
+ smallest_nonzero_magnitude = kwargs["smallest_nonzero_magnitude"]
+
+ count = count_between_floats(min_value, max_value)
+
+ # we have two intervals:
+ # a. [min_value, max_value]
+ # b. [-smallest_nonzero_magnitude, smallest_nonzero_magnitude]
+ #
+ # which could be subsets (in either order), overlapping, or disjoint. We
+ # want the interval difference a - b.
+
+ # next_down because endpoints are ok with smallest_nonzero_magnitude
+ min_point = max(min_value, -flt.next_down(smallest_nonzero_magnitude))
+ max_point = min(max_value, flt.next_down(smallest_nonzero_magnitude))
+
+ if min_point > max_point:
+ # case: disjoint intervals.
+ return count
+
+ count -= count_between_floats(min_point, max_point)
+ if sign_aware_lte(min_value, -0.0) and sign_aware_lte(-0.0, max_value):
+ # account for -0.0
+ count += 1
+ if sign_aware_lte(min_value, 0.0) and sign_aware_lte(0.0, max_value):
+ # account for 0.0
+ count += 1
+ return count
raise NotImplementedError(f"unhandled ir_type {ir_type}")
@@ -247,16 +280,30 @@ def all_children(ir_type, kwargs):
min_value = kwargs["min_value"]
max_value = kwargs["max_value"]
+ smallest_nonzero_magnitude = kwargs["smallest_nonzero_magnitude"]
+
+ # handle zeroes separately so smallest_nonzero_magnitude can think of
+ # itself as a complete interval (instead of a hole at ±0).
+ if sign_aware_lte(min_value, -0.0) and sign_aware_lte(-0.0, max_value):
+ yield -0.0
+ if sign_aware_lte(min_value, 0.0) and sign_aware_lte(0.0, max_value):
+ yield 0.0
if flt.is_negative(min_value):
if flt.is_negative(max_value):
- # if both are negative, have to invert order
- yield from floats_between(max_value, min_value)
+ # case: both negative.
+ max_point = min(max_value, -smallest_nonzero_magnitude)
+ # float_to_int increases as negative magnitude increases, so
+ # invert order.
+ yield from floats_between(max_point, min_value)
else:
- yield from floats_between(-0.0, min_value)
- yield from floats_between(0.0, max_value)
+ # case: straddles midpoint (which is between -0.0 and 0.0).
+ yield from floats_between(-smallest_nonzero_magnitude, min_value)
+ yield from floats_between(smallest_nonzero_magnitude, max_value)
else:
- yield from floats_between(min_value, max_value)
+ # case: both positive.
+ min_point = max(min_value, smallest_nonzero_magnitude)
+ yield from floats_between(min_point, max_value)
@attr.s(slots=True)
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py
index b762b89c96..5746ad6b9f 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py
@@ -8,6 +8,7 @@
# 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/.
+import math
from collections import defaultdict
from typing import TYPE_CHECKING, Callable, Dict, Optional
@@ -19,14 +20,9 @@ from hypothesis.internal.conjecture.choicetree import (
prefix_selection_order,
random_selection_order,
)
-from hypothesis.internal.conjecture.data import (
- DRAW_FLOAT_LABEL,
- ConjectureData,
- ConjectureResult,
- Status,
-)
+from hypothesis.internal.conjecture.data import ConjectureData, ConjectureResult, Status
from hypothesis.internal.conjecture.dfa import ConcreteDFA
-from hypothesis.internal.conjecture.floats import float_to_lex, lex_to_float
+from hypothesis.internal.conjecture.floats import is_simple
from hypothesis.internal.conjecture.junkdrawer import (
binary_search,
find_integer,
@@ -379,6 +375,12 @@ class Shrinker:
test function."""
return self.engine.call_count
+ def consider_new_tree(self, tree):
+ data = ConjectureData.for_ir_tree(tree)
+ self.engine.test_function(data)
+
+ return self.consider_new_buffer(data.buffer)
+
def consider_new_buffer(self, buffer):
"""Returns True if after running this buffer the result would be
the current shrink_target."""
@@ -775,6 +777,10 @@ class Shrinker:
return self.shrink_target.blocks
@property
+ def nodes(self):
+ return self.shrink_target.examples.ir_tree_nodes
+
+ @property
def examples(self):
return self.shrink_target.examples
@@ -1207,31 +1213,32 @@ class Shrinker:
anything particularly meaningful for non-float values.
"""
- ex = chooser.choose(
- self.examples,
- lambda ex: (
- ex.label == DRAW_FLOAT_LABEL
- and len(ex.children) == 2
- and ex.children[1].length == 8
- ),
+ node = chooser.choose(
+ self.nodes,
+ lambda node: node.ir_type == "float" and not node.was_forced
+ # avoid shrinking integer-valued floats. In our current ordering, these
+ # are already simpler than all other floats, so it's better to shrink
+ # them in other passes.
+ and not is_simple(node.value),
)
- u = ex.children[1].start
- v = ex.children[1].end
- buf = self.shrink_target.buffer
- b = buf[u:v]
- f = lex_to_float(int_from_bytes(b))
- b2 = int_to_bytes(float_to_lex(f), 8)
- if b == b2 or self.consider_new_buffer(buf[:u] + b2 + buf[v:]):
- Float.shrink(
- f,
- lambda x: self.consider_new_buffer(
- self.shrink_target.buffer[:u]
- + int_to_bytes(float_to_lex(x), 8)
- + self.shrink_target.buffer[v:]
- ),
- random=self.random,
- )
+ i = self.nodes.index(node)
+ # the Float shrinker was only built to handle positive floats. We'll
+ # shrink the positive portion and reapply the sign after, which is
+ # equivalent to this shrinker's previous behavior. We'll want to refactor
+ # Float to handle negative floats natively in the future. (likely a pure
+ # code quality change, with no shrinking impact.)
+ sign = math.copysign(1.0, node.value)
+ Float.shrink(
+ abs(node.value),
+ lambda val: self.consider_new_tree(
+ self.nodes[:i]
+ + [node.copy(with_value=sign * val)]
+ + self.nodes[i + 1 :]
+ ),
+ random=self.random,
+ node=node,
+ )
@defines_shrink_pass()
def redistribute_block_pairs(self, chooser):
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py
index 090324f839..5acdf85a8d 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py
@@ -133,6 +133,7 @@ class Shrinker:
def consider(self, value):
"""Returns True if make_immutable(value) == self.current after calling
self.incorporate(value)."""
+ self.debug(f"considering {value}")
value = self.make_immutable(value)
if value == self.current:
return True
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/floats.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/floats.py
index f05cdfff30..acc878b7b4 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/floats.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/floats.py
@@ -11,6 +11,7 @@
import math
import sys
+from hypothesis.internal.conjecture.data import ir_value_permitted
from hypothesis.internal.conjecture.floats import float_to_lex
from hypothesis.internal.conjecture.shrinking.common import Shrinker
from hypothesis.internal.conjecture.shrinking.integer import Integer
@@ -19,9 +20,16 @@ MAX_PRECISE_INTEGER = 2**53
class Float(Shrinker):
- def setup(self):
+ def setup(self, node):
self.NAN = math.nan
self.debugging_enabled = True
+ self.node = node
+
+ def consider(self, value):
+ if not ir_value_permitted(value, "float", self.node.kwargs):
+ self.debug(f"rejecting {value} as disallowed for {self.node.kwargs}")
+ return False
+ return super().consider(value)
def make_immutable(self, f):
f = float(f)
diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py
index 19c05ed3e7..56b220d17c 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, 99, 5)
+__version_info__ = (6, 99, 6)
__version__ = ".".join(map(str, __version_info__))
diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make
index 68834541fd..5174ace633 100644
--- a/contrib/python/hypothesis/py3/ya.make
+++ b/contrib/python/hypothesis/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(6.99.5)
+VERSION(6.99.6)
LICENSE(MPL-2.0)