dycw-utilities 0.131.18__py3-none-any.whl → 0.131.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dycw_utilities-0.131.18.dist-info → dycw_utilities-0.131.20.dist-info}/METADATA +1 -1
- {dycw_utilities-0.131.18.dist-info → dycw_utilities-0.131.20.dist-info}/RECORD +16 -17
- utilities/__init__.py +1 -1
- utilities/datetime.py +4 -924
- utilities/fastapi.py +5 -7
- utilities/functions.py +78 -45
- utilities/hypothesis.py +20 -269
- utilities/platform.py +18 -0
- utilities/polars.py +6 -3
- utilities/pytest.py +83 -63
- utilities/types.py +3 -24
- utilities/typing.py +2 -15
- utilities/whenever2.py +149 -5
- utilities/zoneinfo.py +4 -0
- utilities/whenever.py +0 -230
- {dycw_utilities-0.131.18.dist-info → dycw_utilities-0.131.20.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.131.18.dist-info → dycw_utilities-0.131.20.dist-info}/licenses/LICENSE +0 -0
utilities/whenever.py
DELETED
@@ -1,230 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import datetime as dt
|
4
|
-
from contextlib import suppress
|
5
|
-
from dataclasses import dataclass
|
6
|
-
from typing import TYPE_CHECKING, override
|
7
|
-
|
8
|
-
from whenever import DateTimeDelta, TimeZoneNotFoundError, ZonedDateTime
|
9
|
-
|
10
|
-
from utilities.datetime import (
|
11
|
-
_MICROSECONDS_PER_DAY,
|
12
|
-
_MICROSECONDS_PER_SECOND,
|
13
|
-
ZERO_TIME,
|
14
|
-
datetime_duration_to_microseconds,
|
15
|
-
)
|
16
|
-
from utilities.math import ParseNumberError, parse_number
|
17
|
-
from utilities.re import (
|
18
|
-
ExtractGroupError,
|
19
|
-
ExtractGroupsError,
|
20
|
-
extract_group,
|
21
|
-
extract_groups,
|
22
|
-
)
|
23
|
-
from utilities.zoneinfo import UTC, ensure_time_zone, get_time_zone_name
|
24
|
-
|
25
|
-
if TYPE_CHECKING:
|
26
|
-
from utilities.types import Duration
|
27
|
-
|
28
|
-
MAX_SERIALIZABLE_TIMEDELTA = dt.timedelta(days=3652060, microseconds=-1)
|
29
|
-
MIN_SERIALIZABLE_TIMEDELTA = -MAX_SERIALIZABLE_TIMEDELTA
|
30
|
-
|
31
|
-
|
32
|
-
##
|
33
|
-
|
34
|
-
|
35
|
-
def check_valid_zoned_datetime(datetime: dt.datetime, /) -> None:
|
36
|
-
"""Check if a zoned datetime is valid."""
|
37
|
-
time_zone = ensure_time_zone(datetime) # skipif-ci-and-windows
|
38
|
-
datetime2 = datetime.replace(tzinfo=time_zone) # skipif-ci-and-windows
|
39
|
-
try: # skipif-ci-and-windows
|
40
|
-
result = (
|
41
|
-
ZonedDateTime.from_py_datetime(datetime2)
|
42
|
-
.to_tz(get_time_zone_name(UTC))
|
43
|
-
.to_tz(get_time_zone_name(time_zone))
|
44
|
-
.py_datetime()
|
45
|
-
)
|
46
|
-
except TimeZoneNotFoundError: # pragma: no cover
|
47
|
-
raise _CheckValidZonedDateTimeInvalidTimeZoneError(datetime=datetime) from None
|
48
|
-
if result != datetime2: # skipif-ci-and-windows
|
49
|
-
raise _CheckValidZonedDateTimeUnequalError(datetime=datetime, result=result)
|
50
|
-
|
51
|
-
|
52
|
-
@dataclass(kw_only=True, slots=True)
|
53
|
-
class CheckValidZonedDateTimeError(Exception):
|
54
|
-
datetime: dt.datetime
|
55
|
-
|
56
|
-
|
57
|
-
@dataclass(kw_only=True, slots=True)
|
58
|
-
class _CheckValidZonedDateTimeInvalidTimeZoneError(CheckValidZonedDateTimeError):
|
59
|
-
@override
|
60
|
-
def __str__(self) -> str:
|
61
|
-
return f"Invalid timezone; got {self.datetime.tzinfo}" # pragma: no cover
|
62
|
-
|
63
|
-
|
64
|
-
@dataclass(kw_only=True, slots=True)
|
65
|
-
class _CheckValidZonedDateTimeUnequalError(CheckValidZonedDateTimeError):
|
66
|
-
result: dt.datetime
|
67
|
-
|
68
|
-
@override
|
69
|
-
def __str__(self) -> str:
|
70
|
-
return f"Zoned datetime must be valid; got {self.datetime} != {self.result}" # skipif-ci-and-windows
|
71
|
-
|
72
|
-
|
73
|
-
##
|
74
|
-
|
75
|
-
|
76
|
-
def parse_duration(duration: str, /) -> Duration:
|
77
|
-
"""Parse a string into a Duration."""
|
78
|
-
with suppress(ParseNumberError):
|
79
|
-
return parse_number(duration)
|
80
|
-
try:
|
81
|
-
return parse_timedelta(duration)
|
82
|
-
except ParseTimedeltaError:
|
83
|
-
raise ParseDurationError(duration=duration) from None
|
84
|
-
|
85
|
-
|
86
|
-
@dataclass(kw_only=True, slots=True)
|
87
|
-
class ParseDurationError(Exception):
|
88
|
-
duration: str
|
89
|
-
|
90
|
-
@override
|
91
|
-
def __str__(self) -> str:
|
92
|
-
return f"Unable to parse duration; got {self.duration!r}"
|
93
|
-
|
94
|
-
|
95
|
-
##
|
96
|
-
|
97
|
-
|
98
|
-
def parse_timedelta(timedelta: str, /) -> dt.timedelta:
|
99
|
-
"""Parse a string into a timedelta."""
|
100
|
-
with suppress(ExtractGroupError):
|
101
|
-
rest = extract_group(r"^-([\w\.]+)$", timedelta)
|
102
|
-
return -parse_timedelta(rest)
|
103
|
-
try:
|
104
|
-
days_str, time_str = extract_groups(r"^P(?:(\d+)D)?(?:T([\w\.]*))?$", timedelta)
|
105
|
-
except ExtractGroupsError:
|
106
|
-
raise _ParseTimedeltaParseError(timedelta=timedelta) from None
|
107
|
-
days = ZERO_TIME if days_str == "" else dt.timedelta(days=int(days_str))
|
108
|
-
if time_str == "":
|
109
|
-
time = ZERO_TIME
|
110
|
-
else:
|
111
|
-
time_part = DateTimeDelta.parse_common_iso(f"PT{time_str}").time_part()
|
112
|
-
_, nanoseconds = divmod(time_part.in_nanoseconds(), 1000)
|
113
|
-
if nanoseconds != 0:
|
114
|
-
raise _ParseTimedeltaNanosecondError(
|
115
|
-
timedelta=timedelta, nanoseconds=nanoseconds
|
116
|
-
)
|
117
|
-
time = dt.timedelta(microseconds=int(time_part.in_microseconds()))
|
118
|
-
return days + time
|
119
|
-
|
120
|
-
|
121
|
-
@dataclass(kw_only=True, slots=True)
|
122
|
-
class ParseTimedeltaError(Exception):
|
123
|
-
timedelta: str
|
124
|
-
|
125
|
-
|
126
|
-
@dataclass(kw_only=True, slots=True)
|
127
|
-
class _ParseTimedeltaParseError(ParseTimedeltaError):
|
128
|
-
@override
|
129
|
-
def __str__(self) -> str:
|
130
|
-
return f"Unable to parse timedelta; got {self.timedelta!r}"
|
131
|
-
|
132
|
-
|
133
|
-
@dataclass(kw_only=True, slots=True)
|
134
|
-
class _ParseTimedeltaNanosecondError(ParseTimedeltaError):
|
135
|
-
nanoseconds: int
|
136
|
-
|
137
|
-
@override
|
138
|
-
def __str__(self) -> str:
|
139
|
-
return f"Unable to parse timedelta; got {self.nanoseconds} nanoseconds"
|
140
|
-
|
141
|
-
|
142
|
-
##
|
143
|
-
|
144
|
-
|
145
|
-
def serialize_duration(duration: Duration, /) -> str:
|
146
|
-
"""Serialize a duration."""
|
147
|
-
if isinstance(duration, int | float):
|
148
|
-
return str(duration)
|
149
|
-
try:
|
150
|
-
return serialize_timedelta(duration)
|
151
|
-
except SerializeTimeDeltaError as error:
|
152
|
-
raise SerializeDurationError(duration=error.timedelta) from None
|
153
|
-
|
154
|
-
|
155
|
-
@dataclass(kw_only=True, slots=True)
|
156
|
-
class SerializeDurationError(Exception):
|
157
|
-
duration: Duration
|
158
|
-
|
159
|
-
@override
|
160
|
-
def __str__(self) -> str:
|
161
|
-
return f"Unable to serialize duration; got {self.duration}"
|
162
|
-
|
163
|
-
|
164
|
-
##
|
165
|
-
|
166
|
-
|
167
|
-
def serialize_timedelta(timedelta: dt.timedelta, /) -> str:
|
168
|
-
"""Serialize a timedelta."""
|
169
|
-
try:
|
170
|
-
dtd = _to_datetime_delta(timedelta)
|
171
|
-
except _ToDateTimeDeltaError as error:
|
172
|
-
raise SerializeTimeDeltaError(timedelta=error.timedelta) from None
|
173
|
-
return dtd.format_common_iso()
|
174
|
-
|
175
|
-
|
176
|
-
@dataclass(kw_only=True, slots=True)
|
177
|
-
class SerializeTimeDeltaError(Exception):
|
178
|
-
timedelta: dt.timedelta
|
179
|
-
|
180
|
-
@override
|
181
|
-
def __str__(self) -> str:
|
182
|
-
return f"Unable to serialize timedelta; got {self.timedelta}"
|
183
|
-
|
184
|
-
|
185
|
-
##
|
186
|
-
|
187
|
-
|
188
|
-
def _to_datetime_delta(timedelta: dt.timedelta, /) -> DateTimeDelta:
|
189
|
-
"""Serialize a timedelta."""
|
190
|
-
total_microseconds = datetime_duration_to_microseconds(timedelta)
|
191
|
-
if total_microseconds == 0:
|
192
|
-
return DateTimeDelta()
|
193
|
-
if total_microseconds >= 1:
|
194
|
-
days, remainder = divmod(total_microseconds, _MICROSECONDS_PER_DAY)
|
195
|
-
seconds, microseconds = divmod(remainder, _MICROSECONDS_PER_SECOND)
|
196
|
-
try:
|
197
|
-
dtd = DateTimeDelta(days=days, seconds=seconds, microseconds=microseconds)
|
198
|
-
except (OverflowError, ValueError):
|
199
|
-
raise _ToDateTimeDeltaError(timedelta=timedelta) from None
|
200
|
-
months, days, seconds, nanoseconds = dtd.in_months_days_secs_nanos()
|
201
|
-
return DateTimeDelta(
|
202
|
-
months=months, days=days, seconds=seconds, nanoseconds=nanoseconds
|
203
|
-
)
|
204
|
-
return -_to_datetime_delta(-timedelta)
|
205
|
-
|
206
|
-
|
207
|
-
@dataclass(kw_only=True, slots=True)
|
208
|
-
class _ToDateTimeDeltaError(Exception):
|
209
|
-
timedelta: dt.timedelta
|
210
|
-
|
211
|
-
@override
|
212
|
-
def __str__(self) -> str:
|
213
|
-
return f"Unable to create DateTimeDelta; got {self.timedelta}"
|
214
|
-
|
215
|
-
|
216
|
-
__all__ = [
|
217
|
-
"MAX_SERIALIZABLE_TIMEDELTA",
|
218
|
-
"MIN_SERIALIZABLE_TIMEDELTA",
|
219
|
-
"CheckValidZonedDateTimeError",
|
220
|
-
"ParseDurationError",
|
221
|
-
"ParseTimedeltaError",
|
222
|
-
"SerializeDurationError",
|
223
|
-
"SerializeTimeDeltaError",
|
224
|
-
"check_valid_zoned_datetime",
|
225
|
-
"check_valid_zoned_datetime",
|
226
|
-
"parse_duration",
|
227
|
-
"parse_timedelta",
|
228
|
-
"serialize_duration",
|
229
|
-
"serialize_timedelta",
|
230
|
-
]
|
File without changes
|
File without changes
|