Skip to content

Commit

Permalink
Calculate number of days
Browse files Browse the repository at this point in the history
Some months have fewer than 31 days!  Shocking but true.
  • Loading branch information
Zac-HD committed Jun 4, 2019
1 parent 389d47c commit b0408de
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 35 deletions.
4 changes: 4 additions & 0 deletions hypothesis-python/RELEASE.rst
@@ -0,0 +1,4 @@
RELEASE_TYPE: patch

This patch makes :func:`~hypothesis.strategies.datetimes` more efficient,
as it now handles short months correctly by construction instead of filtering.
22 changes: 8 additions & 14 deletions hypothesis-python/src/hypothesis/searchstrategy/datetime.py
Expand Up @@ -18,6 +18,7 @@
from __future__ import absolute_import, division, print_function

import datetime as dt
from calendar import monthrange

from hypothesis.internal.conjecture import utils
from hypothesis.searchstrategy.strategies import SearchStrategy
Expand All @@ -44,39 +45,32 @@ def __init__(self, min_value, max_value, timezones_strat):
self.max_dt = max_value
self.tz_strat = timezones_strat

def _attempt_one_draw(self, data):
def do_draw(self, data):
result = dict()
cap_low, cap_high = True, True
for name in ("year", "month", "day", "hour", "minute", "second", "microsecond"):
low = getattr(self.min_dt if cap_low else dt.datetime.min, name)
high = getattr(self.max_dt if cap_high else dt.datetime.max, name)
if name == "day" and not cap_high:
_, high = monthrange(**result)
if name == "year":
val = utils.integer_range(data, low, high, 2000)
else:
val = utils.integer_range(data, low, high)
result[name] = val
cap_low = cap_low and val == low
cap_high = cap_high and val == high
result = dt.datetime(**result)
tz = data.draw(self.tz_strat)
try:
result = dt.datetime(**result)
if is_pytz_timezone(tz):
# Can't just construct; see http://pytz.sourceforge.net
return tz.normalize(tz.localize(result))
return result.replace(tzinfo=tz)
except (ValueError, OverflowError):
return None

def do_draw(self, data):
for _ in range(3):
result = self._attempt_one_draw(data)
if result is not None:
return result
data.note_event(
"3 attempts to create a datetime between %r and %r "
"with timezone from %r failed." % (self.min_dt, self.max_dt, self.tz_strat)
)
data.mark_invalid()
msg = "Failed to draw a datetime between %r and %r with timezone from %r."
data.note_event(msg % (self.min_dt, self.max_dt, self.tz_strat))
data.mark_invalid()


class DateStrategy(SearchStrategy):
Expand Down
25 changes: 4 additions & 21 deletions hypothesis-python/tests/cover/test_datetimes.py
Expand Up @@ -23,9 +23,7 @@

from hypothesis import given
from hypothesis.internal.compat import hrange
from hypothesis.internal.conjecture.data import ConjectureData, Status, StopTest
from hypothesis.searchstrategy.datetime import DatetimeStrategy
from hypothesis.strategies import binary, dates, datetimes, none, timedeltas, times
from hypothesis.strategies import dates, datetimes, timedeltas, times
from tests.common.debug import find_any, minimal


Expand Down Expand Up @@ -88,21 +86,6 @@ def test_bordering_on_a_leap_year():
assert x.year == 2004


def test_DatetimeStrategy_draw_may_fail():
def is_failure_inducing(b):
try:
return strat._attempt_one_draw(ConjectureData.for_buffer(b)) is None
except StopTest:
return False

strat = DatetimeStrategy(dt.datetime.min, dt.datetime.max, none())
failure_inducing = minimal(binary(), is_failure_inducing, timeout_after=30)
data = ConjectureData.for_buffer(failure_inducing * 100)
with pytest.raises(StopTest):
data.draw(strat)
assert data.status == Status.INVALID


def test_can_find_after_the_year_2000():
assert minimal(dates(), lambda x: x.year > 2000).year == 2001

Expand All @@ -111,9 +94,9 @@ def test_can_find_before_the_year_2000():
assert minimal(dates(), lambda x: x.year < 2000).year == 1999


def test_can_find_each_month():
for month in hrange(1, 13):
find_any(dates(), lambda x: x.month == month)
@pytest.mark.parametrize("month", hrange(1, 13))
def test_can_find_each_month(month):
find_any(dates(), lambda x: x.month == month)


def test_min_year_is_respected():
Expand Down

0 comments on commit b0408de

Please sign in to comment.