Skip to content

Commit 8cd355b

Browse files
committed
Intermediate changes
1 parent 775af71 commit 8cd355b

File tree

9 files changed

+108
-36
lines changed

9 files changed

+108
-36
lines changed

contrib/python/hypothesis/py3/.dist-info/METADATA

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Metadata-Version: 2.1
22
Name: hypothesis
3-
Version: 6.108.5
3+
Version: 6.108.8
44
Summary: A library for property-based testing
55
Home-page: https://hypothesis.works
66
Author: David R. MacIver and Zac Hatfield-Dodds
@@ -41,10 +41,10 @@ Requires-Dist: exceptiongroup >=1.0.0 ; python_version < "3.11"
4141
Provides-Extra: all
4242
Requires-Dist: black >=19.10b0 ; extra == 'all'
4343
Requires-Dist: click >=7.0 ; extra == 'all'
44-
Requires-Dist: crosshair-tool >=0.0.63 ; extra == 'all'
44+
Requires-Dist: crosshair-tool >=0.0.65 ; extra == 'all'
4545
Requires-Dist: django >=3.2 ; extra == 'all'
4646
Requires-Dist: dpcontracts >=0.4 ; extra == 'all'
47-
Requires-Dist: hypothesis-crosshair >=0.0.9 ; extra == 'all'
47+
Requires-Dist: hypothesis-crosshair >=0.0.11 ; extra == 'all'
4848
Requires-Dist: lark >=0.10.1 ; extra == 'all'
4949
Requires-Dist: libcst >=0.3.16 ; extra == 'all'
5050
Requires-Dist: numpy >=1.17.3 ; extra == 'all'
@@ -63,8 +63,8 @@ Requires-Dist: rich >=9.0.0 ; extra == 'cli'
6363
Provides-Extra: codemods
6464
Requires-Dist: libcst >=0.3.16 ; extra == 'codemods'
6565
Provides-Extra: crosshair
66-
Requires-Dist: hypothesis-crosshair >=0.0.9 ; extra == 'crosshair'
67-
Requires-Dist: crosshair-tool >=0.0.63 ; extra == 'crosshair'
66+
Requires-Dist: hypothesis-crosshair >=0.0.11 ; extra == 'crosshair'
67+
Requires-Dist: crosshair-tool >=0.0.65 ; extra == 'crosshair'
6868
Provides-Extra: dateutil
6969
Requires-Dist: python-dateutil >=1.4 ; extra == 'dateutil'
7070
Provides-Extra: django

contrib/python/hypothesis/py3/hypothesis/control.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,9 @@ def __init__(self, data, *, is_final=False, close_on_capture=True):
139139
self.known_object_printers = defaultdict(list)
140140

141141
def record_call(self, obj, func, args, kwargs):
142-
name = get_pretty_function_description(func)
143142
self.known_object_printers[IDKey(obj)].append(
144-
lambda obj, p, cycle: p.maybe_repr_known_object_as_call(
145-
obj, cycle, name, args, kwargs
143+
lambda obj, p, cycle, *, _func=func: p.maybe_repr_known_object_as_call(
144+
obj, cycle, get_pretty_function_description(_func), args, kwargs
146145
)
147146
)
148147

contrib/python/hypothesis/py3/hypothesis/internal/cache.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# obtain one at https://mozilla.org/MPL/2.0/.
1010

1111
import threading
12+
from collections import OrderedDict
1213

1314
import attr
1415

@@ -282,3 +283,64 @@ def new_entry(self, key, value):
282283

283284
def on_access(self, key, value, score):
284285
return (2, self.tick())
286+
287+
288+
class LRUCache:
289+
"""
290+
This is a drop-in replacement for a GenericCache (despite the lack of inheritance)
291+
in performance critical environments. It turns out that GenericCache's heap
292+
balancing for arbitrary scores can be quite expensive compared to the doubly
293+
linked list approach of lru_cache or OrderedDict.
294+
295+
This class is a pure LRU and does not provide any sort of affininty towards
296+
the number of accesses beyond recency. If soft-pinning entries which have been
297+
accessed at least once is important, use LRUReusedCache.
298+
"""
299+
300+
# Here are some nice performance references for lru_cache vs OrderedDict:
301+
# https://github.com/python/cpython/issues/72426#issuecomment-1093727671
302+
# https://discuss.python.org/t/simplify-lru-cache/18192/6
303+
#
304+
# We use OrderedDict here because it is unclear to me we can provide the same
305+
# api as GenericCache without messing with @lru_cache internals.
306+
#
307+
# Anecdotally, OrderedDict seems quite competitive with lru_cache, but perhaps
308+
# that is localized to our access patterns.
309+
310+
def __init__(self, max_size):
311+
assert max_size > 0
312+
self.max_size = max_size
313+
self._threadlocal = threading.local()
314+
315+
@property
316+
def cache(self):
317+
try:
318+
return self._threadlocal.cache
319+
except AttributeError:
320+
self._threadlocal.cache = OrderedDict()
321+
return self._threadlocal.cache
322+
323+
def __setitem__(self, key, value):
324+
self.cache[key] = value
325+
self.cache.move_to_end(key)
326+
327+
while len(self.cache) > self.max_size:
328+
self.cache.popitem(last=False)
329+
330+
def __getitem__(self, key):
331+
val = self.cache[key]
332+
self.cache.move_to_end(key)
333+
return val
334+
335+
def __iter__(self):
336+
return iter(self.cache)
337+
338+
def __len__(self):
339+
return len(self.cache)
340+
341+
def __contains__(self, key):
342+
return key in self.cache
343+
344+
# implement GenericCache interface, for tests
345+
def check_valid(self):
346+
pass

contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import attr
4242

4343
from hypothesis.errors import Frozen, InvalidArgument, StopTest
44-
from hypothesis.internal.cache import LRUReusedCache
44+
from hypothesis.internal.cache import LRUCache
4545
from hypothesis.internal.compat import add_note, floor, int_from_bytes, int_to_bytes
4646
from hypothesis.internal.conjecture.floats import float_to_lex, lex_to_float
4747
from hypothesis.internal.conjecture.junkdrawer import (
@@ -200,9 +200,11 @@ def structural_coverage(label: int) -> StructuralCoverageTag:
200200
NASTY_FLOATS = list(map(float, NASTY_FLOATS))
201201
NASTY_FLOATS.extend([-x for x in NASTY_FLOATS])
202202

203-
FLOAT_INIT_LOGIC_CACHE = LRUReusedCache(4096)
204-
205-
POOLED_KWARGS_CACHE = LRUReusedCache(4096)
203+
# These caches, especially the kwargs cache, can be quite hot and so we prefer
204+
# LRUCache over LRUReusedCache for performance. We lose scan resistance, but
205+
# that's probably fine here.
206+
FLOAT_INIT_LOGIC_CACHE = LRUCache(4096)
207+
POOLED_KWARGS_CACHE = LRUCache(4096)
206208

207209
DRAW_STRING_DEFAULT_MAX_SIZE = 10**10 # "arbitrarily large"
208210

contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
current_build_context,
6060
deprecate_random_in_strategy,
6161
note,
62+
should_note,
6263
)
6364
from hypothesis.errors import (
6465
HypothesisSideeffectWarning,
@@ -2115,9 +2116,11 @@ def draw(self, strategy: SearchStrategy[Ex], label: Any = None) -> Ex:
21152116
if TESTCASE_CALLBACKS:
21162117
self.conjecture_data._observability_args[desc] = to_jsonable(result)
21172118

2118-
printer.text(desc)
2119-
printer.pretty(result)
2120-
note(printer.getvalue())
2119+
# optimization to avoid needless printer.pretty
2120+
if should_note():
2121+
printer.text(desc)
2122+
printer.pretty(result)
2123+
note(printer.getvalue())
21212124
return result
21222125

21232126

contrib/python/hypothesis/py3/hypothesis/strategies/_internal/functions.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from weakref import WeakKeyDictionary
1212

13-
from hypothesis.control import note
13+
from hypothesis.control import note, should_note
1414
from hypothesis.errors import InvalidState
1515
from hypothesis.internal.reflection import (
1616
convert_positional_arguments,
@@ -50,13 +50,15 @@ def inner(*args, **kwargs):
5050
cache = self._cache.setdefault(inner, {})
5151
if key not in cache:
5252
cache[key] = data.draw(self.returns)
53-
rep = repr_call(self.like, args, kwargs, reorder=False)
54-
note(f"Called function: {rep} -> {cache[key]!r}")
53+
if should_note(): # optimization to avoid needless repr_call
54+
rep = repr_call(self.like, args, kwargs, reorder=False)
55+
note(f"Called function: {rep} -> {cache[key]!r}")
5556
return cache[key]
5657
else:
5758
val = data.draw(self.returns)
58-
rep = repr_call(self.like, args, kwargs, reorder=False)
59-
note(f"Called function: {rep} -> {val!r}")
59+
if should_note():
60+
rep = repr_call(self.like, args, kwargs, reorder=False)
61+
note(f"Called function: {rep} -> {val!r}")
6062
return val
6163

6264
return inner

contrib/python/hypothesis/py3/hypothesis/vendor/pretty.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,6 @@ def __init__(self, output=None, *, context=None):
143143
self.group_queue = GroupQueue(root_group)
144144
self.indentation = 0
145145

146-
self.snans = 0
147-
148146
self.stack = []
149147
self.singleton_pprinters = {}
150148
self.type_pprinters = {}
@@ -358,12 +356,6 @@ def _enumerate(self, seq):
358356

359357
def flush(self):
360358
"""Flush data that is left in the buffer."""
361-
if self.snans:
362-
# Reset self.snans *before* calling breakable(), which might flush()
363-
snans = self.snans
364-
self.snans = 0
365-
self.breakable(" ")
366-
self.text(f"# Saw {snans} signaling NaN" + "s" * (snans > 1))
367359
for data in self.buffer:
368360
self.output_width += data.output(self.output, self.output_width)
369361
self.buffer.clear()
@@ -747,19 +739,31 @@ def _exception_pprint(obj, p, cycle):
747739
p.pretty(arg)
748740

749741

742+
def _repr_integer(obj, p, cycle):
743+
if abs(obj) < 1_000_000_000:
744+
p.text(repr(obj))
745+
elif abs(obj) < 10**640:
746+
# add underscores for integers over ten decimal digits
747+
p.text(f"{obj:#_d}")
748+
else:
749+
# for very very large integers, use hex because power-of-two bases are cheaper
750+
# https://docs.python.org/3/library/stdtypes.html#integer-string-conversion-length-limitation
751+
p.text(f"{obj:#_x}")
752+
753+
750754
def _repr_float_counting_nans(obj, p, cycle):
751-
if isnan(obj) and hasattr(p, "snans"):
755+
if isnan(obj):
752756
if struct.pack("!d", abs(obj)) != struct.pack("!d", float("nan")):
753-
p.snans += 1
754-
if copysign(1.0, obj) == -1.0:
755-
p.text("-nan")
756-
return
757+
show = hex(*struct.unpack("Q", struct.pack("d", obj)))
758+
return p.text(f"struct.unpack('d', struct.pack('Q', {show}))[0]")
759+
elif copysign(1.0, obj) == -1.0:
760+
return p.text("-nan")
757761
p.text(repr(obj))
758762

759763

760764
#: printers for builtin types
761765
_type_pprinters = {
762-
int: _repr_pprint,
766+
int: _repr_integer,
763767
float: _repr_float_counting_nans,
764768
str: _repr_pprint,
765769
tuple: _seq_pprinter_factory("(", ")", tuple),

contrib/python/hypothesis/py3/hypothesis/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
99
# obtain one at https://mozilla.org/MPL/2.0/.
1010

11-
__version_info__ = (6, 108, 5)
11+
__version_info__ = (6, 108, 8)
1212
__version__ = ".".join(map(str, __version_info__))

contrib/python/hypothesis/py3/ya.make

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
PY3_LIBRARY()
44

5-
VERSION(6.108.5)
5+
VERSION(6.108.8)
66

77
LICENSE(MPL-2.0)
88

0 commit comments

Comments
 (0)