Skip to content

Commit efa7ebd

Browse files
authored
CLN: tighter typing in libconversion (#35088)
1 parent 5309a5c commit efa7ebd

File tree

4 files changed

+46
-49
lines changed

4 files changed

+46
-49
lines changed

pandas/_libs/tslibs/conversion.pxd

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ cdef class _TSObject:
1313
bint fold
1414

1515

16-
cdef convert_to_tsobject(object ts, object tz, object unit,
16+
cdef convert_to_tsobject(object ts, tzinfo tz, object unit,
1717
bint dayfirst, bint yearfirst,
1818
int32_t nanos=*)
1919

20-
cdef _TSObject convert_datetime_to_tsobject(datetime ts, object tz,
20+
cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
2121
int32_t nanos=*)
2222

2323
cdef int64_t get_datetime64_nanos(object val) except? -1

pandas/_libs/tslibs/conversion.pyx

+14-22
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ cdef class _TSObject:
307307
return self.value
308308

309309

310-
cdef convert_to_tsobject(object ts, object tz, object unit,
310+
cdef convert_to_tsobject(object ts, tzinfo tz, object unit,
311311
bint dayfirst, bint yearfirst, int32_t nanos=0):
312312
"""
313313
Extract datetime and int64 from any of:
@@ -326,13 +326,10 @@ cdef convert_to_tsobject(object ts, object tz, object unit,
326326
cdef:
327327
_TSObject obj
328328

329-
if tz is not None:
330-
tz = maybe_get_tz(tz)
331-
332329
obj = _TSObject()
333330

334331
if isinstance(ts, str):
335-
return convert_str_to_tsobject(ts, tz, unit, dayfirst, yearfirst)
332+
return _convert_str_to_tsobject(ts, tz, unit, dayfirst, yearfirst)
336333

337334
if ts is None or ts is NaT:
338335
obj.value = NPY_NAT
@@ -374,16 +371,16 @@ cdef convert_to_tsobject(object ts, object tz, object unit,
374371
f'Timestamp')
375372

376373
if tz is not None:
377-
localize_tso(obj, tz)
374+
_localize_tso(obj, tz)
378375

379376
if obj.value != NPY_NAT:
380-
# check_overflows needs to run after localize_tso
377+
# check_overflows needs to run after _localize_tso
381378
check_dts_bounds(&obj.dts)
382379
check_overflows(obj)
383380
return obj
384381

385382

386-
cdef _TSObject convert_datetime_to_tsobject(datetime ts, object tz,
383+
cdef _TSObject convert_datetime_to_tsobject(datetime ts, tzinfo tz,
387384
int32_t nanos=0):
388385
"""
389386
Convert a datetime (or Timestamp) input `ts`, along with optional timezone
@@ -446,8 +443,8 @@ cdef _TSObject convert_datetime_to_tsobject(datetime ts, object tz,
446443
return obj
447444

448445

449-
cdef _TSObject create_tsobject_tz_using_offset(npy_datetimestruct dts,
450-
int tzoffset, tzinfo tz=None):
446+
cdef _TSObject _create_tsobject_tz_using_offset(npy_datetimestruct dts,
447+
int tzoffset, tzinfo tz=None):
451448
"""
452449
Convert a datetimestruct `dts`, along with initial timezone offset
453450
`tzoffset` to a _TSObject (with timezone object `tz` - optional).
@@ -500,9 +497,9 @@ cdef _TSObject create_tsobject_tz_using_offset(npy_datetimestruct dts,
500497
return obj
501498

502499

503-
cdef _TSObject convert_str_to_tsobject(object ts, object tz, object unit,
504-
bint dayfirst=False,
505-
bint yearfirst=False):
500+
cdef _TSObject _convert_str_to_tsobject(object ts, tzinfo tz, object unit,
501+
bint dayfirst=False,
502+
bint yearfirst=False):
506503
"""
507504
Convert a string input `ts`, along with optional timezone object`tz`
508505
to a _TSObject.
@@ -532,11 +529,6 @@ cdef _TSObject convert_str_to_tsobject(object ts, object tz, object unit,
532529
int out_local = 0, out_tzoffset = 0
533530
bint do_parse_datetime_string = False
534531

535-
if tz is not None:
536-
tz = maybe_get_tz(tz)
537-
538-
assert isinstance(ts, str)
539-
540532
if len(ts) == 0 or ts in nat_strings:
541533
ts = NaT
542534
elif ts == 'now':
@@ -557,12 +549,12 @@ cdef _TSObject convert_str_to_tsobject(object ts, object tz, object unit,
557549
if not string_to_dts_failed:
558550
check_dts_bounds(&dts)
559551
if out_local == 1:
560-
return create_tsobject_tz_using_offset(dts,
561-
out_tzoffset, tz)
552+
return _create_tsobject_tz_using_offset(dts,
553+
out_tzoffset, tz)
562554
else:
563555
ts = dtstruct_to_dt64(&dts)
564556
if tz is not None:
565-
# shift for localize_tso
557+
# shift for _localize_tso
566558
ts = tz_localize_to_utc(np.array([ts], dtype='i8'), tz,
567559
ambiguous='raise')[0]
568560

@@ -613,7 +605,7 @@ cdef inline check_overflows(_TSObject obj):
613605
# ----------------------------------------------------------------------
614606
# Localization
615607

616-
cdef inline void localize_tso(_TSObject obj, tzinfo tz):
608+
cdef inline void _localize_tso(_TSObject obj, tzinfo tz):
617609
"""
618610
Given the UTC nanosecond timestamp in obj.value, find the wall-clock
619611
representation of that timestamp in the given timezone.

pandas/_libs/tslibs/timestamps.pyx

+26-24
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ from cpython.datetime cimport (
2020
datetime,
2121
time,
2222
tzinfo,
23+
tzinfo as tzinfo_type, # alias bc `tzinfo` is a kwarg below
2324
PyDateTime_Check,
2425
PyDelta_Check,
2526
PyTZInfo_Check,
@@ -932,7 +933,7 @@ class Timestamp(_Timestamp):
932933
second=None,
933934
microsecond=None,
934935
nanosecond=None,
935-
tzinfo=None,
936+
tzinfo_type tzinfo=None,
936937
*,
937938
fold=None
938939
):
@@ -957,18 +958,17 @@ class Timestamp(_Timestamp):
957958
#
958959
# Mixing pydatetime positional and keyword arguments is forbidden!
959960

960-
cdef _TSObject ts
961+
cdef:
962+
_TSObject ts
963+
tzinfo_type tzobj
961964

962965
_date_attributes = [year, month, day, hour, minute, second,
963966
microsecond, nanosecond]
964967

965968
if tzinfo is not None:
966-
if not PyTZInfo_Check(tzinfo):
967-
# tzinfo must be a datetime.tzinfo object, GH#17690
968-
raise TypeError(
969-
f"tzinfo must be a datetime.tzinfo object, not {type(tzinfo)}"
970-
)
971-
elif tz is not None:
969+
# GH#17690 tzinfo must be a datetime.tzinfo object, ensured
970+
# by the cython annotation.
971+
if tz is not None:
972972
raise ValueError('Can provide at most one of tz, tzinfo')
973973

974974
# User passed tzinfo instead of tz; avoid silently ignoring
@@ -1055,7 +1055,8 @@ class Timestamp(_Timestamp):
10551055
raise ValueError("Cannot pass a datetime or Timestamp with tzinfo with "
10561056
"the tz parameter. Use tz_convert instead.")
10571057

1058-
ts = convert_to_tsobject(ts_input, tz, unit, 0, 0, nanosecond or 0)
1058+
tzobj = maybe_get_tz(tz)
1059+
ts = convert_to_tsobject(ts_input, tzobj, unit, 0, 0, nanosecond or 0)
10591060

10601061
if ts.value == NPY_NAT:
10611062
return NaT
@@ -1378,15 +1379,16 @@ default 'raise'
13781379

13791380
cdef:
13801381
npy_datetimestruct dts
1381-
int64_t value, value_tz, offset
1382-
object _tzinfo, result, k, v
1382+
int64_t value, value_tz
1383+
object k, v
13831384
datetime ts_input
1385+
tzinfo_type tzobj
13841386

13851387
# set to naive if needed
1386-
_tzinfo = self.tzinfo
1388+
tzobj = self.tzinfo
13871389
value = self.value
1388-
if _tzinfo is not None:
1389-
value_tz = tz_convert_single(value, _tzinfo, UTC)
1390+
if tzobj is not None:
1391+
value_tz = tz_convert_single(value, tzobj, UTC)
13901392
value += value - value_tz
13911393

13921394
# setup components
@@ -1419,30 +1421,30 @@ default 'raise'
14191421
if nanosecond is not None:
14201422
dts.ps = validate('nanosecond', nanosecond) * 1000
14211423
if tzinfo is not object:
1422-
_tzinfo = tzinfo
1424+
tzobj = tzinfo
14231425

14241426
# reconstruct & check bounds
1425-
if _tzinfo is not None and treat_tz_as_pytz(_tzinfo):
1427+
if tzobj is not None and treat_tz_as_pytz(tzobj):
14261428
# replacing across a DST boundary may induce a new tzinfo object
14271429
# see GH#18319
1428-
ts_input = _tzinfo.localize(datetime(dts.year, dts.month, dts.day,
1429-
dts.hour, dts.min, dts.sec,
1430-
dts.us),
1431-
is_dst=not bool(fold))
1432-
_tzinfo = ts_input.tzinfo
1430+
ts_input = tzobj.localize(datetime(dts.year, dts.month, dts.day,
1431+
dts.hour, dts.min, dts.sec,
1432+
dts.us),
1433+
is_dst=not bool(fold))
1434+
tzobj = ts_input.tzinfo
14331435
else:
14341436
kwargs = {'year': dts.year, 'month': dts.month, 'day': dts.day,
14351437
'hour': dts.hour, 'minute': dts.min, 'second': dts.sec,
1436-
'microsecond': dts.us, 'tzinfo': _tzinfo,
1438+
'microsecond': dts.us, 'tzinfo': tzobj,
14371439
'fold': fold}
14381440
ts_input = datetime(**kwargs)
14391441

1440-
ts = convert_datetime_to_tsobject(ts_input, _tzinfo)
1442+
ts = convert_datetime_to_tsobject(ts_input, tzobj)
14411443
value = ts.value + (dts.ps // 1000)
14421444
if value != NPY_NAT:
14431445
check_dts_bounds(&dts)
14441446

1445-
return create_timestamp_from_ts(value, dts, _tzinfo, self.freq, fold)
1447+
return create_timestamp_from_ts(value, dts, tzobj, self.freq, fold)
14461448

14471449
def to_julian_date(self) -> np.float64:
14481450
"""

pandas/tests/scalar/timestamp/test_constructors.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,10 @@ def test_constructor_invalid(self):
174174

175175
def test_constructor_invalid_tz(self):
176176
# GH#17690
177-
msg = "must be a datetime.tzinfo"
177+
msg = (
178+
"Argument 'tzinfo' has incorrect type "
179+
r"\(expected datetime.tzinfo, got str\)"
180+
)
178181
with pytest.raises(TypeError, match=msg):
179182
Timestamp("2017-10-22", tzinfo="US/Eastern")
180183

0 commit comments

Comments
 (0)