Skip to content

Commit 16a3bd9

Browse files
committed
TST: Improve integration of Hypothesis
Responding to review from jreback on GH22280.
1 parent bf195da commit 16a3bd9

File tree

4 files changed

+54
-55
lines changed

4 files changed

+54
-55
lines changed

doc/source/contributing.rst

+2
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,8 @@ Tests that we have ``parametrized`` are now accessible via the test name, for ex
795795
test_cool_feature.py::test_series[int8] PASSED
796796
797797
798+
.. _using-hypothesis:
799+
798800
Using ``hypothesis``
799801
~~~~~~~~~~~~~~~~~~~~
800802

pandas/conftest.py

+34
Original file line numberDiff line numberDiff line change
@@ -453,3 +453,37 @@ def mock():
453453
return importlib.import_module("unittest.mock")
454454
else:
455455
return pytest.importorskip("mock")
456+
457+
458+
# ----------------------------------------------------------------
459+
# Global setup for tests using Hypothesis
460+
461+
from hypothesis import strategies as st
462+
463+
# Registering these strategies makes them globally available via st.from_type,
464+
# which is use for offsets in tests/tseries/offsets/test_offsets_properties.py
465+
for name in 'MonthBegin MonthEnd BMonthBegin BMonthEnd'.split():
466+
cls = getattr(pd.tseries.offsets, name)
467+
st.register_type_strategy(cls, st.builds(
468+
cls,
469+
n=st.integers(-99, 99),
470+
normalize=st.booleans(),
471+
))
472+
473+
for name in 'YearBegin YearEnd BYearBegin BYearEnd'.split():
474+
cls = getattr(pd.tseries.offsets, name)
475+
st.register_type_strategy(cls, st.builds(
476+
cls,
477+
n=st.integers(-5, 5),
478+
normalize=st.booleans(),
479+
month=st.integers(min_value=1, max_value=12),
480+
))
481+
482+
for name in 'QuarterBegin QuarterEnd BQuarterBegin BQuarterEnd'.split():
483+
cls = getattr(pd.tseries.offsets, name)
484+
st.register_type_strategy(cls, st.builds(
485+
cls,
486+
n=st.integers(-24, 24),
487+
normalize=st.booleans(),
488+
startingMonth=st.integers(min_value=1, max_value=12)
489+
))

pandas/tests/tseries/offsets/test_offsets_properties.py

+7-36
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,11 @@
1717
import pandas as pd
1818

1919
from pandas.tseries.offsets import (
20-
Hour, Minute, Second, Milli, Micro, Nano,
2120
MonthEnd, MonthBegin, BMonthEnd, BMonthBegin,
2221
QuarterEnd, QuarterBegin, BQuarterEnd, BQuarterBegin,
2322
YearEnd, YearBegin, BYearEnd, BYearBegin,
2423
)
2524

26-
27-
tick_classes = [Hour, Minute, Second, Milli, Micro, Nano]
28-
yqm_classes = [MonthBegin, MonthEnd, BMonthBegin, BMonthEnd,
29-
QuarterBegin, QuarterEnd, BQuarterBegin, BQuarterEnd,
30-
YearBegin, YearEnd, BYearBegin, BYearEnd]
31-
3225
# ----------------------------------------------------------------
3326
# Helpers for generating random data
3427

@@ -50,35 +43,13 @@
5043
timezones=st.one_of(st.none(), dateutil_timezones(), pytz_timezones())
5144
)
5245

53-
# Register the various offset classes so st.from_type can create instances.
54-
# We *could* just append the strategies to a list, but this provides a nice
55-
# demo and enables future tests to use a simple e.g. `from_type(Hour)`.
56-
for cls in tick_classes + [MonthBegin, MonthEnd, BMonthBegin, BMonthEnd]:
57-
st.register_type_strategy(cls, st.builds(
58-
cls,
59-
n=st.integers(-99, 99),
60-
normalize=st.booleans(),
61-
))
62-
63-
for cls in [YearBegin, YearEnd, BYearBegin, BYearEnd]:
64-
st.register_type_strategy(cls, st.builds(
65-
cls,
66-
n=st.integers(-5, 5),
67-
normalize=st.booleans(),
68-
month=st.integers(min_value=1, max_value=12),
69-
))
70-
71-
for cls in [QuarterBegin, QuarterEnd, BQuarterBegin, BQuarterEnd]:
72-
st.register_type_strategy(cls, st.builds(
73-
cls,
74-
n=st.integers(-24, 24),
75-
normalize=st.booleans(),
76-
startingMonth=st.integers(min_value=1, max_value=12)
77-
))
78-
79-
# This strategy can generate any kind of Offset in `tick_classes` or
80-
# `yqm_classes`, with arguments as specified directly above in registration.
81-
gen_yqm_offset = st.one_of([st.from_type(cls) for cls in yqm_classes])
46+
# The strategy for each type is registered in conftest.py, as they don't carry
47+
# enough runtime information (e.g. type hints) to infer how to build them.
48+
gen_yqm_offset = st.one_of(*map(st.from_type, [
49+
MonthBegin, MonthEnd, BMonthBegin, BMonthEnd,
50+
QuarterBegin, QuarterEnd, BQuarterBegin, BQuarterEnd,
51+
YearBegin, YearEnd, BYearBegin, BYearEnd
52+
]))
8253

8354

8455
# ----------------------------------------------------------------

pandas/tests/tseries/offsets/test_ticks.py

+11-19
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import pytest
88
import numpy as np
9-
from hypothesis import given, assume, strategies as st
9+
from hypothesis import given, assume, example, strategies as st
1010

1111
from pandas import Timedelta, Timestamp
1212
from pandas.tseries import offsets
@@ -36,9 +36,11 @@ def test_delta_to_tick():
3636
assert (tick == offsets.Day(3))
3737

3838

39-
@given(cls=st.sampled_from(tick_classes),
40-
n=st.integers(-999, 999),
41-
m=st.integers(-999, 999))
39+
@pytest.mark.parametrize('cls', tick_classes)
40+
@example(n=2, m=3)
41+
@example(n=800, m=300)
42+
@example(n=1000, m=5)
43+
@given(n=st.integers(-999, 999), m=st.integers(-999, 999))
4244
def test_tick_add_sub(cls, n, m):
4345
# For all Tick subclasses and all integers n, m, we should have
4446
# tick(n) + tick(m) == tick(n+m)
@@ -54,8 +56,9 @@ def test_tick_add_sub(cls, n, m):
5456
assert left - right == expected
5557

5658

57-
@given(cls=st.sampled_from(tick_classes),
58-
n=st.integers(-999, 999), m=st.integers(-999, 999))
59+
@pytest.mark.parametrize('cls', tick_classes)
60+
@example(n=2, m=3)
61+
@given(n=st.integers(-999, 999), m=st.integers(-999, 999))
5962
def test_tick_equality(cls, n, m):
6063
assume(m != n)
6164
# tick == tock iff tick.n == tock.n
@@ -68,6 +71,8 @@ def test_tick_equality(cls, n, m):
6871
assert left == right
6972
assert not (left != right)
7073

74+
assert cls(n) != cls(-n)
75+
7176

7277
# ---------------------------------------------------------------------
7378

@@ -234,21 +239,8 @@ def test_tick_zero(cls1, cls2):
234239

235240
@pytest.mark.parametrize('cls', tick_classes)
236241
def test_tick_equalities(cls):
237-
assert cls(3) == cls(3)
238242
assert cls() == cls(1)
239243

240-
# not equals
241-
assert cls(3) != cls(2)
242-
assert cls(3) != cls(-3)
243-
244-
245-
@pytest.mark.parametrize('cls', tick_classes)
246-
def test_tick_operators(cls):
247-
assert cls(3) + cls(2) == cls(5)
248-
assert cls(3) - cls(2) == cls(1)
249-
assert cls(800) + cls(300) == cls(1100)
250-
assert cls(1000) - cls(5) == cls(995)
251-
252244

253245
@pytest.mark.parametrize('cls', tick_classes)
254246
def test_tick_offset(cls):

0 commit comments

Comments
 (0)