Skip to content

Commit bae55bb

Browse files
DRY up tests
1 parent b5e62d6 commit bae55bb

File tree

5 files changed

+911
-1188
lines changed

5 files changed

+911
-1188
lines changed

pandas/_libs/tslibs/timedeltas.pyx

+51-41
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,21 @@ cdef convert_to_timedelta64(object ts, str unit):
308308
return ts.astype("timedelta64[ns]")
309309

310310

311+
cpdef to_timedelta64(object value, str unit):
312+
"""
313+
Wrapper around convert_to_timedelta64() that does overflow checks.
314+
TODO: also construct non-nano
315+
TODO: do all overflow-unsafe operations here
316+
TODO: constrain unit to a more specific type
317+
"""
318+
with cython.overflowcheck(True):
319+
try:
320+
return convert_to_timedelta64(value, unit)
321+
except OverflowError as ex:
322+
msg = f"{value} outside allowed range [{TIMEDELTA_MIN_NS}ns, {TIMEDELTA_MAX_NS}ns]"
323+
raise OutOfBoundsTimedelta(msg) from ex
324+
325+
311326
@cython.boundscheck(False)
312327
@cython.wraparound(False)
313328
def array_to_timedelta64(
@@ -1473,49 +1488,44 @@ class Timedelta(_Timedelta):
14731488
)
14741489
if isinstance(value, str) and unit is not None:
14751490
raise ValueError("unit must not be specified if the value is a str")
1476-
elif value is _no_input:
1477-
if not kwargs:
1478-
raise ValueError(
1479-
"cannot construct a Timedelta without a value/unit "
1480-
"or descriptive keywords (days,seconds....)"
1481-
)
1482-
if not kwargs.keys() <= set(cls._allowed_kwargs):
1483-
raise ValueError(
1484-
"cannot construct a Timedelta from the passed arguments, "
1485-
f"allowed keywords are {cls._allowed_kwargs}"
1486-
)
1491+
elif value is _no_input and not kwargs:
1492+
raise ValueError(
1493+
"cannot construct a Timedelta without a value/unit "
1494+
"or descriptive keywords (days,seconds....)"
1495+
)
1496+
if not kwargs.keys() <= set(cls._allowed_kwargs):
1497+
raise ValueError(
1498+
"cannot construct a Timedelta from the passed arguments, "
1499+
f"allowed keywords are {cls._allowed_kwargs}"
1500+
)
14871501

1488-
try:
1489-
# GH43764, convert any input to nanoseconds first, to ensure any potential
1490-
# nanosecond contributions from kwargs parsed as floats are included
1491-
kwargs = collections.defaultdict(int, {key: _to_py_int_float(val) for key, val in kwargs.items()})
1492-
if kwargs:
1493-
value = convert_to_timedelta64(
1494-
sum((
1495-
kwargs["weeks"] * 7 * 24 * 3600 * 1_000_000_000,
1496-
kwargs["days"] * 24 * 3600 * 1_000_000_000,
1497-
kwargs["hours"] * 3600 * 1_000_000_000,
1498-
kwargs["minutes"] * 60 * 1_000_000_000,
1499-
kwargs["seconds"] * 1_000_000_000,
1500-
kwargs["milliseconds"] * 1_000_000,
1501-
kwargs["microseconds"] * 1_000,
1502-
kwargs["nanoseconds"],
1503-
)),
1504-
"ns",
1505-
)
1506-
else:
1507-
if is_integer_object(value) or is_float_object(value):
1508-
unit = parse_timedelta_unit(unit)
1509-
else:
1510-
unit = "ns"
1511-
value = convert_to_timedelta64(value, unit)
1512-
except OverflowError as ex:
1513-
msg = f"outside allowed range [{TIMEDELTA_MIN_NS}ns, {TIMEDELTA_MAX_NS}ns]"
1514-
raise OutOfBoundsTimedelta(msg) from ex
1502+
# GH43764, convert any input to nanoseconds first, to ensure any potential
1503+
# nanosecond contributions from kwargs parsed as floats are included
1504+
kwargs = collections.defaultdict(int, {key: _to_py_int_float(val) for key, val in kwargs.items()})
1505+
if kwargs:
1506+
value = to_timedelta64(
1507+
sum((
1508+
kwargs["weeks"] * 7 * 24 * 3600 * 1_000_000_000,
1509+
kwargs["days"] * 24 * 3600 * 1_000_000_000,
1510+
kwargs["hours"] * 3600 * 1_000_000_000,
1511+
kwargs["minutes"] * 60 * 1_000_000_000,
1512+
kwargs["seconds"] * 1_000_000_000,
1513+
kwargs["milliseconds"] * 1_000_000,
1514+
kwargs["microseconds"] * 1_000,
1515+
kwargs["nanoseconds"],
1516+
)),
1517+
"ns",
1518+
)
15151519
else:
1516-
if is_td64nat(value):
1517-
return NaT
1518-
return _timedelta_from_value_and_reso(value.view("i8"), NPY_FR_ns)
1520+
if is_integer_object(value) or is_float_object(value):
1521+
unit = parse_timedelta_unit(unit)
1522+
else:
1523+
unit = "ns"
1524+
value = to_timedelta64(value, unit)
1525+
1526+
if is_td64nat(value):
1527+
return NaT
1528+
return _timedelta_from_value_and_reso(value.view("i8"), NPY_FR_ns)
15191529

15201530
def __setstate__(self, state):
15211531
if len(state) == 1:

pandas/tests/scalar/timedelta/test_arithmetic.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""
2-
Tests for scalar Timedelta arithmetic ops
2+
Tests for arithmetic ops between a Timedelta scalar and another scalar, or a Timedelta
3+
scalar and a Array/Index/Series/DataFrame.
34
"""
45

56
from __future__ import annotations
@@ -9,7 +10,6 @@
910
timedelta,
1011
)
1112
import operator
12-
from platform import architecture
1313
import re
1414

1515
import numpy as np
@@ -447,11 +447,7 @@ def test_numeric(self, ten_days, mul_op, factor, expected, box_with_array):
447447
)
448448
tm.assert_equal(result, expected)
449449

450-
@pytest.mark.xfail(
451-
condition=architecture()[0] != "32bit",
452-
reason="no overflow check",
453-
raises=AssertionError,
454-
)
450+
@pytest.mark.xfail(reason="no overflow check", raises=AssertionError, strict=True)
455451
@pytest.mark.parametrize("factor", (1.01, 2), ids=("int", "float"))
456452
def test_returns_nat_if_result_overflows(self, mul_op, factor, box_with_array):
457453
numeric_box = tm.box_expected((1, factor), box_with_array, transpose=False)

0 commit comments

Comments
 (0)