Skip to content

Commit 94448a8

Browse files
hamza-mobeencodex
andcommitted
Allow timedelta rel tolerance in approx
Co-authored-by: Codex <codex@openai.com>
1 parent 9658d75 commit 94448a8

2 files changed

Lines changed: 40 additions & 11 deletions

File tree

src/_pytest/python_api.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -565,34 +565,40 @@ class ApproxTimedelta(ApproxBase):
565565
"""Perform approximate comparisons where the expected value is a
566566
datetime or timedelta.
567567
568-
Requires an explicit absolute tolerance as a timedelta.
569-
Relative tolerance is not supported for time-based comparisons.
568+
Requires an explicit tolerance as a timedelta.
569+
Relative tolerance is not supported for datetime comparisons.
570570
"""
571571

572572
def __init__(self, expected, rel=None, abs=None, nan_ok: bool = False) -> None:
573573
__tracebackhide__ = True
574-
if rel is not None:
574+
if isinstance(expected, datetime) and rel is not None:
575575
raise TypeError(
576576
"pytest.approx() does not support relative tolerance for "
577-
"datetime/timedelta comparisons. Use abs=timedelta(...) instead."
577+
"datetime comparisons. Use abs=timedelta(...) instead."
578578
)
579579
if nan_ok:
580580
raise TypeError(
581581
"pytest.approx() does not support nan_ok for "
582582
"datetime/timedelta comparisons."
583583
)
584-
if abs is None:
584+
if abs is None and rel is None:
585585
raise TypeError(
586-
"pytest.approx() requires an absolute tolerance for "
586+
"pytest.approx() requires an explicit tolerance for "
587587
"datetime/timedelta comparisons: "
588588
"e.g. approx(expected, abs=timedelta(seconds=1))"
589589
)
590-
if not isinstance(abs, timedelta):
590+
if abs is not None and not isinstance(abs, timedelta):
591591
raise TypeError(
592592
f"absolute tolerance for datetime/timedelta must be a "
593593
f"timedelta, got {type(abs).__name__}"
594594
)
595-
super().__init__(expected, rel=None, abs=abs, nan_ok=False)
595+
if rel is not None and not isinstance(rel, timedelta):
596+
raise TypeError(
597+
f"relative tolerance for timedelta must be a "
598+
f"timedelta, got {type(rel).__name__}"
599+
)
600+
tolerance = max(t for t in (abs, rel) if t is not None)
601+
super().__init__(expected, rel=None, abs=tolerance, nan_ok=False)
596602

597603
def __repr__(self) -> str:
598604
return f"{self.expected} ± {self.abs}"

testing/python/approx.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,25 +1167,48 @@ def test_timedelta_outside_tolerance(self):
11671167
td2 = timedelta(seconds=102)
11681168
assert td1 != approx(td2, abs=timedelta(seconds=1))
11691169

1170-
def test_requires_abs(self):
1170+
def test_timedelta_rel_within_tolerance(self):
1171+
from datetime import timedelta
1172+
1173+
td1 = timedelta(seconds=100)
1174+
td2 = timedelta(seconds=100.5)
1175+
assert td1 == approx(td2, rel=timedelta(seconds=1))
1176+
1177+
def test_timedelta_rel_outside_tolerance(self):
1178+
from datetime import timedelta
1179+
1180+
td1 = timedelta(seconds=100)
1181+
td2 = timedelta(seconds=102)
1182+
assert td1 != approx(td2, rel=timedelta(seconds=1))
1183+
1184+
def test_requires_tolerance(self):
11711185
from datetime import datetime
11721186

1173-
with pytest.raises(TypeError, match="requires an absolute tolerance"):
1187+
with pytest.raises(TypeError, match="requires an explicit tolerance"):
11741188
approx(datetime(2024, 1, 1))
11751189

1176-
def test_rejects_rel(self):
1190+
def test_datetime_rejects_rel(self):
11771191
from datetime import datetime
11781192
from datetime import timedelta
11791193

11801194
with pytest.raises(TypeError, match="does not support relative tolerance"):
11811195
approx(datetime(2024, 1, 1), rel=0.1, abs=timedelta(seconds=1))
11821196

1197+
with pytest.raises(TypeError, match="does not support relative tolerance"):
1198+
approx(datetime(2024, 1, 1), rel=timedelta(seconds=1))
1199+
11831200
def test_abs_must_be_timedelta(self):
11841201
from datetime import datetime
11851202

11861203
with pytest.raises(TypeError, match="must be a timedelta"):
11871204
approx(datetime(2024, 1, 1), abs=1.0)
11881205

1206+
def test_timedelta_rel_must_be_timedelta(self):
1207+
from datetime import timedelta
1208+
1209+
with pytest.raises(TypeError, match="must be a timedelta"):
1210+
approx(timedelta(seconds=1), rel=0.1)
1211+
11891212
def test_rejects_nan_ok(self):
11901213
from datetime import datetime
11911214
from datetime import timedelta

0 commit comments

Comments
 (0)