Skip to content

Commit 36add2b

Browse files
committed
Add a regression test for time and datetime cross validation
Also fixes the none check in time validation
1 parent 405c62a commit 36add2b

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

python/ipywidgets/ipywidgets/widgets/tests/test_widget_datetime.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
import pytest
88

9+
from contextlib import nullcontext
910
import datetime
11+
import itertools
1012

1113
import pytz
1214
from traitlets import TraitError
@@ -40,6 +42,44 @@ def test_datetime_validate_value_none():
4042
assert w.value is None
4143

4244

45+
def _permuted_dts():
46+
ret = []
47+
combos = list(itertools.product([None, dt_1442, dt_2002, dt_2056], repeat=3))
48+
for vals in combos:
49+
expected = vals[0]
50+
if vals[1] and vals[2] and vals[1] > vals[2]:
51+
expected = TraitError
52+
elif vals[0] is None:
53+
pass
54+
elif vals[1] and vals[1] > vals[0]:
55+
expected = vals[1]
56+
elif vals[2] and vals[2] < vals[0]:
57+
expected = vals[2]
58+
ret.append(vals + (expected,))
59+
return ret
60+
61+
62+
@pytest.mark.parametrize(
63+
"input_value,input_min,input_max,expected",
64+
_permuted_dts()
65+
)
66+
def test_datetime_cross_validate_value_min_max(
67+
input_value,
68+
input_min,
69+
input_max,
70+
expected,
71+
):
72+
w = DatetimePicker(value=dt_2002, min=dt_2002, max=dt_2002)
73+
should_raise = expected is TraitError
74+
with pytest.raises(expected) if should_raise else nullcontext():
75+
with w.hold_trait_notifications():
76+
w.value = input_value
77+
w.min = input_min
78+
w.max = input_max
79+
if not should_raise:
80+
assert w.value is expected
81+
82+
4383
def test_datetime_validate_value_vs_min():
4484
dt = dt_2002
4585
dt_min = datetime.datetime(2019, 1, 1, tzinfo=pytz.utc)

python/ipywidgets/ipywidgets/widgets/tests/test_widget_time.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ def test_time_creation_value():
2424
assert w.value is t
2525

2626

27+
def test_time_cross_validate_value_min_max():
28+
w = TimePicker(value=datetime.time(2), min=datetime.time(2), max=datetime.time(2))
29+
with w.hold_trait_notifications():
30+
w.value = None
31+
w.min = datetime.time(4)
32+
w.max = datetime.time(6)
33+
assert w.value is None
34+
with w.hold_trait_notifications():
35+
w.value = datetime.time(4)
36+
w.min = None
37+
w.max = None
38+
assert w.value == datetime.time(4)
39+
40+
2741
def test_time_validate_value_none():
2842
t = datetime.time(13, 37, 42, 7)
2943
t_min = datetime.time(2)

python/ipywidgets/ipywidgets/widgets/widget_time.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ class TimePicker(DescriptionWidget, ValueWidget, CoreWidget):
6363
def _validate_value(self, proposal):
6464
"""Cap and floor value"""
6565
value = proposal["value"]
66+
if value is None:
67+
return value
6668
if self.min and self.min > value:
6769
value = max(value, self.min)
6870
if self.max and self.max < value:
@@ -73,6 +75,8 @@ def _validate_value(self, proposal):
7375
def _validate_min(self, proposal):
7476
"""Enforce min <= value <= max"""
7577
min = proposal["value"]
78+
if min is None:
79+
return min
7680
if self.max and min > self.max:
7781
raise TraitError("Setting min > max")
7882
if self.value and min > self.value:
@@ -83,6 +87,8 @@ def _validate_min(self, proposal):
8387
def _validate_max(self, proposal):
8488
"""Enforce min <= value <= max"""
8589
max = proposal["value"]
90+
if max is None:
91+
return max
8692
if self.min and max < self.min:
8793
raise TraitError("setting max < min")
8894
if self.value and max < self.value:

0 commit comments

Comments
 (0)