dycw-utilities 0.175.17__py3-none-any.whl → 0.185.8__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.185.8.dist-info/METADATA +33 -0
- dycw_utilities-0.185.8.dist-info/RECORD +90 -0
- {dycw_utilities-0.175.17.dist-info → dycw_utilities-0.185.8.dist-info}/WHEEL +2 -2
- utilities/__init__.py +1 -1
- utilities/altair.py +8 -6
- utilities/asyncio.py +40 -56
- utilities/atools.py +9 -11
- utilities/cachetools.py +8 -6
- utilities/click.py +4 -3
- utilities/concurrent.py +1 -1
- utilities/constants.py +492 -0
- utilities/contextlib.py +23 -30
- utilities/contextvars.py +1 -23
- utilities/core.py +2581 -0
- utilities/dataclasses.py +16 -119
- utilities/docker.py +139 -45
- utilities/enum.py +1 -1
- utilities/errors.py +2 -16
- utilities/fastapi.py +5 -5
- utilities/fpdf2.py +2 -1
- utilities/functions.py +33 -264
- utilities/http.py +2 -3
- utilities/hypothesis.py +48 -25
- utilities/iterables.py +39 -575
- utilities/jinja2.py +3 -6
- utilities/jupyter.py +5 -3
- utilities/libcst.py +1 -1
- utilities/lightweight_charts.py +4 -6
- utilities/logging.py +17 -15
- utilities/math.py +1 -36
- utilities/more_itertools.py +4 -6
- utilities/numpy.py +2 -1
- utilities/operator.py +2 -2
- utilities/orjson.py +24 -25
- utilities/os.py +4 -185
- utilities/packaging.py +129 -0
- utilities/parse.py +33 -13
- utilities/pathlib.py +2 -136
- utilities/platform.py +8 -90
- utilities/polars.py +34 -31
- utilities/postgres.py +9 -4
- utilities/pottery.py +20 -18
- utilities/pqdm.py +3 -4
- utilities/psutil.py +2 -3
- utilities/pydantic.py +18 -4
- utilities/pydantic_settings.py +7 -9
- utilities/pydantic_settings_sops.py +3 -3
- utilities/pyinstrument.py +4 -4
- utilities/pytest.py +49 -108
- utilities/pytest_plugins/pytest_regressions.py +2 -2
- utilities/pytest_regressions.py +8 -6
- utilities/random.py +2 -8
- utilities/redis.py +98 -94
- utilities/reprlib.py +11 -118
- utilities/shellingham.py +66 -0
- utilities/slack_sdk.py +13 -12
- utilities/sqlalchemy.py +42 -30
- utilities/sqlalchemy_polars.py +16 -25
- utilities/subprocess.py +1166 -148
- utilities/tabulate.py +32 -0
- utilities/testbook.py +8 -8
- utilities/text.py +24 -115
- utilities/throttle.py +159 -0
- utilities/time.py +18 -0
- utilities/timer.py +29 -12
- utilities/traceback.py +15 -22
- utilities/types.py +38 -3
- utilities/typing.py +18 -12
- utilities/uuid.py +1 -1
- utilities/version.py +202 -45
- utilities/whenever.py +22 -150
- dycw_utilities-0.175.17.dist-info/METADATA +0 -34
- dycw_utilities-0.175.17.dist-info/RECORD +0 -103
- utilities/atomicwrites.py +0 -182
- utilities/cryptography.py +0 -41
- utilities/getpass.py +0 -8
- utilities/git.py +0 -19
- utilities/grp.py +0 -28
- utilities/gzip.py +0 -31
- utilities/json.py +0 -70
- utilities/permissions.py +0 -298
- utilities/pickle.py +0 -25
- utilities/pwd.py +0 -28
- utilities/re.py +0 -156
- utilities/sentinel.py +0 -73
- utilities/socket.py +0 -8
- utilities/string.py +0 -20
- utilities/tempfile.py +0 -136
- utilities/tzdata.py +0 -11
- utilities/tzlocal.py +0 -28
- utilities/warnings.py +0 -65
- utilities/zipfile.py +0 -25
- utilities/zoneinfo.py +0 -133
- {dycw_utilities-0.175.17.dist-info → dycw_utilities-0.185.8.dist-info}/entry_points.txt +0 -0
utilities/constants.py
ADDED
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from getpass import getuser
|
|
6
|
+
from logging import getLogger
|
|
7
|
+
from os import cpu_count, environ
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from platform import system
|
|
10
|
+
from random import SystemRandom
|
|
11
|
+
from re import IGNORECASE
|
|
12
|
+
from socket import gethostname
|
|
13
|
+
from tempfile import gettempdir
|
|
14
|
+
from typing import TYPE_CHECKING, Any, assert_never, cast, override
|
|
15
|
+
from zoneinfo import ZoneInfo
|
|
16
|
+
|
|
17
|
+
from tzlocal import get_localzone
|
|
18
|
+
from whenever import (
|
|
19
|
+
Date,
|
|
20
|
+
DateDelta,
|
|
21
|
+
DateTimeDelta,
|
|
22
|
+
PlainDateTime,
|
|
23
|
+
Time,
|
|
24
|
+
TimeDelta,
|
|
25
|
+
ZonedDateTime,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from utilities.types import System, TimeZone
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# getpass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
USER: str = getuser()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# math
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
MIN_FLOAT32, MAX_FLOAT32 = -3.4028234663852886e38, 3.4028234663852886e38
|
|
42
|
+
MIN_FLOAT64, MAX_FLOAT64 = -1.7976931348623157e308, 1.7976931348623157e308
|
|
43
|
+
MIN_INT8, MAX_INT8 = -(2 ** (8 - 1)), 2 ** (8 - 1) - 1
|
|
44
|
+
MIN_INT16, MAX_INT16 = -(2 ** (16 - 1)), 2 ** (16 - 1) - 1
|
|
45
|
+
MIN_INT32, MAX_INT32 = -(2 ** (32 - 1)), 2 ** (32 - 1) - 1
|
|
46
|
+
MIN_INT64, MAX_INT64 = -(2 ** (64 - 1)), 2 ** (64 - 1) - 1
|
|
47
|
+
MIN_UINT8, MAX_UINT8 = 0, 2**8 - 1
|
|
48
|
+
MIN_UINT16, MAX_UINT16 = 0, 2**16 - 1
|
|
49
|
+
MIN_UINT32, MAX_UINT32 = 0, 2**32 - 1
|
|
50
|
+
MIN_UINT64, MAX_UINT64 = 0, 2**64 - 1
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# os
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
IS_CI: bool = "CI" in environ
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _get_cpu_count() -> int:
|
|
60
|
+
"""Get the CPU count."""
|
|
61
|
+
count = cpu_count()
|
|
62
|
+
if count is None: # pragma: no cover
|
|
63
|
+
raise ValueError(count)
|
|
64
|
+
return count
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
CPU_COUNT: int = _get_cpu_count()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# platform
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _get_system() -> System:
|
|
74
|
+
"""Get the system/OS name."""
|
|
75
|
+
sys = system()
|
|
76
|
+
if sys == "Windows": # skipif-not-windows
|
|
77
|
+
return "windows"
|
|
78
|
+
if sys == "Darwin": # skipif-not-macos
|
|
79
|
+
return "mac"
|
|
80
|
+
if sys == "Linux": # skipif-not-linux
|
|
81
|
+
return "linux"
|
|
82
|
+
raise ValueError(sys) # pragma: no cover
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
SYSTEM: System = _get_system()
|
|
86
|
+
IS_WINDOWS: bool = SYSTEM == "windows"
|
|
87
|
+
IS_MAC: bool = SYSTEM == "mac"
|
|
88
|
+
IS_LINUX: bool = SYSTEM == "linux"
|
|
89
|
+
IS_NOT_WINDOWS: bool = not IS_WINDOWS
|
|
90
|
+
IS_NOT_MAC: bool = not IS_MAC
|
|
91
|
+
IS_NOT_LINUX: bool = not IS_LINUX
|
|
92
|
+
IS_CI_AND_WINDOWS: bool = IS_CI and IS_WINDOWS
|
|
93
|
+
IS_CI_AND_MAC: bool = IS_CI and IS_MAC
|
|
94
|
+
IS_CI_AND_LINUX: bool = IS_CI and IS_LINUX
|
|
95
|
+
IS_CI_AND_NOT_WINDOWS: bool = IS_CI and IS_NOT_WINDOWS
|
|
96
|
+
IS_CI_AND_NOT_MAC: bool = IS_CI and IS_NOT_MAC
|
|
97
|
+
IS_CI_AND_NOT_LINUX: bool = IS_CI and IS_NOT_LINUX
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _get_max_pid() -> int | None:
|
|
101
|
+
"""Get the system max process ID."""
|
|
102
|
+
match SYSTEM:
|
|
103
|
+
case "windows": # skipif-not-windows
|
|
104
|
+
return None
|
|
105
|
+
case "mac": # skipif-not-macos
|
|
106
|
+
return 99999
|
|
107
|
+
case "linux": # skipif-not-linux
|
|
108
|
+
path = Path("/proc/sys/kernel/pid_max")
|
|
109
|
+
try:
|
|
110
|
+
return int(path.read_text())
|
|
111
|
+
except FileNotFoundError: # pragma: no cover
|
|
112
|
+
return None
|
|
113
|
+
case never:
|
|
114
|
+
assert_never(never)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
MAX_PID: int | None = _get_max_pid()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# pathlib
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
HOME: Path = Path.home()
|
|
124
|
+
PWD: Path = Path.cwd()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# platform -> os
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _get_effective_group_id() -> int | None:
|
|
131
|
+
"""Get the effective group ID."""
|
|
132
|
+
match SYSTEM:
|
|
133
|
+
case "windows": # skipif-not-windows
|
|
134
|
+
return None
|
|
135
|
+
case "mac" | "linux": # skipif-windows
|
|
136
|
+
from os import getegid
|
|
137
|
+
|
|
138
|
+
return getegid()
|
|
139
|
+
case never:
|
|
140
|
+
assert_never(never)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
EFFECTIVE_GROUP_ID: int | None = _get_effective_group_id()
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _get_effective_user_id() -> int | None:
|
|
147
|
+
"""Get the effective user ID."""
|
|
148
|
+
match SYSTEM:
|
|
149
|
+
case "windows": # skipif-not-windows
|
|
150
|
+
return None
|
|
151
|
+
case "mac" | "linux": # skipif-windows
|
|
152
|
+
from os import geteuid
|
|
153
|
+
|
|
154
|
+
return geteuid()
|
|
155
|
+
case never:
|
|
156
|
+
assert_never(never)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
EFFECTIVE_USER_ID: int | None = _get_effective_user_id()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# platform -> os -> grp
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _get_gid_name(gid: int, /) -> str | None:
|
|
166
|
+
"""Get the name of a group ID."""
|
|
167
|
+
match SYSTEM:
|
|
168
|
+
case "windows": # skipif-not-windows
|
|
169
|
+
return None
|
|
170
|
+
case "mac" | "linux":
|
|
171
|
+
from grp import getgrgid
|
|
172
|
+
|
|
173
|
+
return getgrgid(gid).gr_name
|
|
174
|
+
case never:
|
|
175
|
+
assert_never(never)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
ROOT_GROUP_NAME: str | None = _get_gid_name(0)
|
|
179
|
+
EFFECTIVE_GROUP_NAME: str | None = (
|
|
180
|
+
None if EFFECTIVE_GROUP_ID is None else _get_gid_name(EFFECTIVE_GROUP_ID)
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# platform -> os -> pwd
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _get_uid_name(uid: int, /) -> str | None:
|
|
188
|
+
"""Get the name of a user ID."""
|
|
189
|
+
match SYSTEM:
|
|
190
|
+
case "windows": # skipif-not-windows
|
|
191
|
+
return None
|
|
192
|
+
case "mac" | "linux": # skipif-windows
|
|
193
|
+
from pwd import getpwuid
|
|
194
|
+
|
|
195
|
+
return getpwuid(uid).pw_name
|
|
196
|
+
case never:
|
|
197
|
+
assert_never(never)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
ROOT_USER_NAME: str | None = _get_uid_name(0)
|
|
201
|
+
EFFECTIVE_USER_NAME: str | None = (
|
|
202
|
+
None if EFFECTIVE_USER_ID is None else _get_uid_name(EFFECTIVE_USER_ID)
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# random
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
SYSTEM_RANDOM: SystemRandom = SystemRandom()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# reprlib
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
RICH_MAX_WIDTH: int = 80
|
|
216
|
+
RICH_INDENT_SIZE: int = 4
|
|
217
|
+
RICH_MAX_LENGTH: int | None = 20
|
|
218
|
+
RICH_MAX_STRING: int | None = None
|
|
219
|
+
RICH_MAX_DEPTH: int | None = None
|
|
220
|
+
RICH_EXPAND_ALL: bool = False
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# sentinel
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class _Meta(type):
|
|
227
|
+
"""Metaclass for the sentinel."""
|
|
228
|
+
|
|
229
|
+
instance: Any = None
|
|
230
|
+
|
|
231
|
+
@override
|
|
232
|
+
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
|
|
233
|
+
if cls.instance is None:
|
|
234
|
+
cls.instance = super().__call__(*args, **kwargs)
|
|
235
|
+
return cls.instance
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class Sentinel(metaclass=_Meta):
|
|
239
|
+
"""Base class for the sentinel object."""
|
|
240
|
+
|
|
241
|
+
@override
|
|
242
|
+
def __repr__(self) -> str:
|
|
243
|
+
return _SENTINEL_REPR
|
|
244
|
+
|
|
245
|
+
@override
|
|
246
|
+
def __str__(self) -> str:
|
|
247
|
+
return repr(self)
|
|
248
|
+
|
|
249
|
+
@classmethod
|
|
250
|
+
def parse(cls, text: str, /) -> Sentinel:
|
|
251
|
+
"""Parse a string into the Sentinel value."""
|
|
252
|
+
if _SENTINEL_PATTERN.search(text):
|
|
253
|
+
return sentinel
|
|
254
|
+
raise SentinelParseError(text=text)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
_SENTINEL_PATTERN = re.compile("^(|sentinel|<sentinel>)$", flags=IGNORECASE)
|
|
258
|
+
_SENTINEL_REPR = "<sentinel>"
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@dataclass(kw_only=True, slots=True)
|
|
262
|
+
class SentinelParseError(Exception):
|
|
263
|
+
text: str
|
|
264
|
+
|
|
265
|
+
@override
|
|
266
|
+
def __str__(self) -> str:
|
|
267
|
+
return f"Unable to parse sentinel; got {self.text!r}"
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
sentinel = Sentinel()
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
# socket
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
HOSTNAME = gethostname()
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
# tempfile
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
TEMP_DIR: Path = Path(gettempdir())
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
# text
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
LIST_SEPARATOR: str = ","
|
|
289
|
+
PAIR_SEPARATOR: str = "="
|
|
290
|
+
BRACKETS: set[tuple[str, str]] = {("(", ")"), ("[", "]"), ("{", "}")}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# tzlocal
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _get_local_time_zone() -> ZoneInfo:
|
|
297
|
+
"""Get the local time zone, with the logging disabled."""
|
|
298
|
+
logger = getLogger("tzlocal") # avoid import cycle
|
|
299
|
+
init_disabled = logger.disabled
|
|
300
|
+
logger.disabled = True
|
|
301
|
+
time_zone = get_localzone()
|
|
302
|
+
logger.disabled = init_disabled
|
|
303
|
+
return time_zone
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
LOCAL_TIME_ZONE: ZoneInfo = _get_local_time_zone()
|
|
307
|
+
LOCAL_TIME_ZONE_NAME: TimeZone = cast("TimeZone", LOCAL_TIME_ZONE.key)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
# tzlocal -> whenever
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _get_now_local() -> ZonedDateTime:
|
|
314
|
+
"""Get the current zoned date-time in the local time-zone."""
|
|
315
|
+
return ZonedDateTime.now(LOCAL_TIME_ZONE_NAME)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
NOW_LOCAL: ZonedDateTime = _get_now_local()
|
|
319
|
+
TODAY_LOCAL: Date = NOW_LOCAL.date()
|
|
320
|
+
TIME_LOCAL: Time = NOW_LOCAL.time()
|
|
321
|
+
NOW_LOCAL_PLAIN: PlainDateTime = NOW_LOCAL.to_plain()
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
# whenever
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
ZERO_DAYS: DateDelta = DateDelta()
|
|
328
|
+
ZERO_TIME: TimeDelta = TimeDelta()
|
|
329
|
+
NANOSECOND: TimeDelta = TimeDelta(nanoseconds=1)
|
|
330
|
+
MICROSECOND: TimeDelta = TimeDelta(microseconds=1)
|
|
331
|
+
MILLISECOND: TimeDelta = TimeDelta(milliseconds=1)
|
|
332
|
+
SECOND: TimeDelta = TimeDelta(seconds=1)
|
|
333
|
+
MINUTE: TimeDelta = TimeDelta(minutes=1)
|
|
334
|
+
HOUR: TimeDelta = TimeDelta(hours=1)
|
|
335
|
+
DAY: DateDelta = DateDelta(days=1)
|
|
336
|
+
WEEK: DateDelta = DateDelta(weeks=1)
|
|
337
|
+
MONTH: DateDelta = DateDelta(months=1)
|
|
338
|
+
YEAR: DateDelta = DateDelta(years=1)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
DATE_DELTA_MIN: DateDelta = DateDelta(weeks=-521722, days=-5)
|
|
342
|
+
DATE_DELTA_MAX: DateDelta = DateDelta(weeks=521722, days=5)
|
|
343
|
+
TIME_DELTA_MIN: TimeDelta = TimeDelta(hours=-87831216)
|
|
344
|
+
TIME_DELTA_MAX: TimeDelta = TimeDelta(hours=87831216)
|
|
345
|
+
DATE_TIME_DELTA_MIN: DateTimeDelta = DateTimeDelta(
|
|
346
|
+
weeks=-521722,
|
|
347
|
+
days=-5,
|
|
348
|
+
hours=-23,
|
|
349
|
+
minutes=-59,
|
|
350
|
+
seconds=-59,
|
|
351
|
+
milliseconds=-999,
|
|
352
|
+
microseconds=-999,
|
|
353
|
+
nanoseconds=-999,
|
|
354
|
+
)
|
|
355
|
+
DATE_TIME_DELTA_MAX: DateTimeDelta = DateTimeDelta(
|
|
356
|
+
weeks=521722,
|
|
357
|
+
days=5,
|
|
358
|
+
hours=23,
|
|
359
|
+
minutes=59,
|
|
360
|
+
seconds=59,
|
|
361
|
+
milliseconds=999,
|
|
362
|
+
microseconds=999,
|
|
363
|
+
nanoseconds=999,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
SECONDS_PER_DAY: int = 24 * 60 * 60
|
|
368
|
+
NANOSECONDS_PER_SECOND: int = 1_000_000_000
|
|
369
|
+
NANOSECONDS_PER_DAY: int = SECONDS_PER_DAY * NANOSECONDS_PER_SECOND
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
# zoneinfo
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
UTC: ZoneInfo = ZoneInfo("UTC")
|
|
376
|
+
HongKong: ZoneInfo = ZoneInfo("Asia/Hong_Kong")
|
|
377
|
+
Tokyo: ZoneInfo = ZoneInfo("Asia/Tokyo")
|
|
378
|
+
USCentral: ZoneInfo = ZoneInfo("US/Central")
|
|
379
|
+
USEastern: ZoneInfo = ZoneInfo("US/Eastern")
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
# zoneinfo -> whenever
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
ZONED_DATE_TIME_MIN: ZonedDateTime = PlainDateTime.MIN.assume_tz(UTC.key)
|
|
386
|
+
ZONED_DATE_TIME_MAX: ZonedDateTime = PlainDateTime.MAX.assume_tz(UTC.key)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _get_now(time_zone: str = UTC.key, /) -> ZonedDateTime:
|
|
390
|
+
"""Get the current zoned date-time."""
|
|
391
|
+
return ZonedDateTime.now(time_zone)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
NOW_UTC: ZonedDateTime = _get_now()
|
|
395
|
+
TODAY_UTC: Date = NOW_UTC.date()
|
|
396
|
+
TIME_UTC: Time = NOW_UTC.time()
|
|
397
|
+
NOW_UTC_PLAIN: PlainDateTime = NOW_UTC.to_plain()
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
__all__ = [
|
|
401
|
+
"BRACKETS",
|
|
402
|
+
"CPU_COUNT",
|
|
403
|
+
"DATE_DELTA_MAX",
|
|
404
|
+
"DATE_DELTA_MIN",
|
|
405
|
+
"DATE_TIME_DELTA_MAX",
|
|
406
|
+
"DATE_TIME_DELTA_MIN",
|
|
407
|
+
"DAY",
|
|
408
|
+
"EFFECTIVE_GROUP_ID",
|
|
409
|
+
"EFFECTIVE_GROUP_NAME",
|
|
410
|
+
"EFFECTIVE_USER_ID",
|
|
411
|
+
"EFFECTIVE_USER_NAME",
|
|
412
|
+
"HOME",
|
|
413
|
+
"HOSTNAME",
|
|
414
|
+
"HOUR",
|
|
415
|
+
"IS_CI",
|
|
416
|
+
"IS_CI_AND_LINUX",
|
|
417
|
+
"IS_CI_AND_MAC",
|
|
418
|
+
"IS_CI_AND_NOT_LINUX",
|
|
419
|
+
"IS_CI_AND_NOT_MAC",
|
|
420
|
+
"IS_CI_AND_NOT_WINDOWS",
|
|
421
|
+
"IS_CI_AND_WINDOWS",
|
|
422
|
+
"IS_LINUX",
|
|
423
|
+
"IS_MAC",
|
|
424
|
+
"IS_NOT_LINUX",
|
|
425
|
+
"IS_NOT_MAC",
|
|
426
|
+
"IS_NOT_WINDOWS",
|
|
427
|
+
"IS_WINDOWS",
|
|
428
|
+
"LIST_SEPARATOR",
|
|
429
|
+
"LOCAL_TIME_ZONE",
|
|
430
|
+
"LOCAL_TIME_ZONE_NAME",
|
|
431
|
+
"MAX_FLOAT32",
|
|
432
|
+
"MAX_FLOAT64",
|
|
433
|
+
"MAX_INT8",
|
|
434
|
+
"MAX_INT16",
|
|
435
|
+
"MAX_INT32",
|
|
436
|
+
"MAX_INT64",
|
|
437
|
+
"MAX_PID",
|
|
438
|
+
"MAX_UINT8",
|
|
439
|
+
"MAX_UINT16",
|
|
440
|
+
"MAX_UINT32",
|
|
441
|
+
"MAX_UINT64",
|
|
442
|
+
"MICROSECOND",
|
|
443
|
+
"MILLISECOND",
|
|
444
|
+
"MINUTE",
|
|
445
|
+
"MIN_FLOAT32",
|
|
446
|
+
"MIN_FLOAT64",
|
|
447
|
+
"MIN_INT8",
|
|
448
|
+
"MIN_INT16",
|
|
449
|
+
"MIN_INT32",
|
|
450
|
+
"MIN_INT64",
|
|
451
|
+
"MIN_UINT8",
|
|
452
|
+
"MIN_UINT16",
|
|
453
|
+
"MIN_UINT32",
|
|
454
|
+
"MIN_UINT64",
|
|
455
|
+
"MONTH",
|
|
456
|
+
"NANOSECOND",
|
|
457
|
+
"NANOSECONDS_PER_DAY",
|
|
458
|
+
"NANOSECONDS_PER_SECOND",
|
|
459
|
+
"NOW_LOCAL",
|
|
460
|
+
"NOW_LOCAL_PLAIN",
|
|
461
|
+
"NOW_UTC",
|
|
462
|
+
"NOW_UTC_PLAIN",
|
|
463
|
+
"PAIR_SEPARATOR",
|
|
464
|
+
"PWD",
|
|
465
|
+
"ROOT_GROUP_NAME",
|
|
466
|
+
"ROOT_USER_NAME",
|
|
467
|
+
"SECOND",
|
|
468
|
+
"SECONDS_PER_DAY",
|
|
469
|
+
"SYSTEM",
|
|
470
|
+
"SYSTEM_RANDOM",
|
|
471
|
+
"TEMP_DIR",
|
|
472
|
+
"TIME_DELTA_MAX",
|
|
473
|
+
"TIME_DELTA_MIN",
|
|
474
|
+
"TIME_LOCAL",
|
|
475
|
+
"TIME_UTC",
|
|
476
|
+
"TODAY_LOCAL",
|
|
477
|
+
"TODAY_UTC",
|
|
478
|
+
"USER",
|
|
479
|
+
"UTC",
|
|
480
|
+
"WEEK",
|
|
481
|
+
"YEAR",
|
|
482
|
+
"ZERO_DAYS",
|
|
483
|
+
"ZERO_TIME",
|
|
484
|
+
"ZONED_DATE_TIME_MAX",
|
|
485
|
+
"ZONED_DATE_TIME_MIN",
|
|
486
|
+
"HongKong",
|
|
487
|
+
"Sentinel",
|
|
488
|
+
"Tokyo",
|
|
489
|
+
"USCentral",
|
|
490
|
+
"USEastern",
|
|
491
|
+
"sentinel",
|
|
492
|
+
]
|
utilities/contextlib.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import re
|
|
4
3
|
from asyncio import create_task, get_event_loop
|
|
5
4
|
from contextlib import (
|
|
6
5
|
_AsyncGeneratorContextManager,
|
|
@@ -90,12 +89,13 @@ def enhanced_context_manager[**P, T_co](
|
|
|
90
89
|
with gcm as value:
|
|
91
90
|
yield value
|
|
92
91
|
finally:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
with _suppress_signal_error():
|
|
93
|
+
_ = signal(SIGABRT, sigabrt0) if sigabrt else None
|
|
94
|
+
_ = signal(SIGFPE, sigfpe0) if sigfpe else None
|
|
95
|
+
_ = signal(SIGILL, sigill0) if sigill else None
|
|
96
|
+
_ = signal(SIGINT, sigint0) if sigint else None
|
|
97
|
+
_ = signal(SIGSEGV, sigsegv0) if sigsegv else None
|
|
98
|
+
_ = signal(SIGTERM, sigterm0) if sigterm else None
|
|
99
99
|
|
|
100
100
|
return wrapped
|
|
101
101
|
|
|
@@ -173,12 +173,13 @@ def enhanced_async_context_manager[**P, T_co](
|
|
|
173
173
|
async with agcm as value:
|
|
174
174
|
yield value
|
|
175
175
|
finally:
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
with _suppress_signal_error():
|
|
177
|
+
_ = signal(SIGABRT, sigabrt0) if sigabrt else None
|
|
178
|
+
_ = signal(SIGFPE, sigfpe0) if sigfpe else None
|
|
179
|
+
_ = signal(SIGILL, sigill0) if sigill else None
|
|
180
|
+
_ = signal(SIGINT, sigint0) if sigint else None
|
|
181
|
+
_ = signal(SIGSEGV, sigsegv0) if sigsegv else None
|
|
182
|
+
_ = signal(SIGTERM, sigterm0) if sigterm else None
|
|
182
183
|
|
|
183
184
|
return wrapped
|
|
184
185
|
|
|
@@ -191,7 +192,8 @@ def _swap_handler(
|
|
|
191
192
|
) -> _HANDLER:
|
|
192
193
|
orig_handler = getsignal(signum)
|
|
193
194
|
new_handler = _make_handler(signum, obj)
|
|
194
|
-
|
|
195
|
+
with _suppress_signal_error():
|
|
196
|
+
_ = signal(signum, new_handler)
|
|
195
197
|
return orig_handler
|
|
196
198
|
|
|
197
199
|
|
|
@@ -220,24 +222,15 @@ def _make_handler(
|
|
|
220
222
|
return new_handler
|
|
221
223
|
|
|
222
224
|
|
|
223
|
-
##
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
_SUPER_OBJECT_HAS_NO_ATTRIBUTE = re.compile(r"'super' object has no attribute '\w+'")
|
|
227
|
-
|
|
228
|
-
|
|
229
225
|
@contextmanager
|
|
230
|
-
def
|
|
231
|
-
"""Suppress the super() attribute error, for mix-ins."""
|
|
226
|
+
def _suppress_signal_error() -> Iterator[None]:
|
|
232
227
|
try:
|
|
233
228
|
yield
|
|
234
|
-
except
|
|
235
|
-
|
|
236
|
-
|
|
229
|
+
except ValueError as error:
|
|
230
|
+
(msg,) = error.args
|
|
231
|
+
if msg == "signal only works in main thread of the main interpreter":
|
|
232
|
+
return
|
|
233
|
+
raise # pragma: no cover
|
|
237
234
|
|
|
238
235
|
|
|
239
|
-
__all__ = [
|
|
240
|
-
"enhanced_async_context_manager",
|
|
241
|
-
"enhanced_context_manager",
|
|
242
|
-
"suppress_super_object_attribute_error",
|
|
243
|
-
]
|
|
236
|
+
__all__ = ["enhanced_async_context_manager", "enhanced_context_manager"]
|
utilities/contextvars.py
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from contextlib import contextmanager
|
|
4
3
|
from contextvars import ContextVar
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from collections.abc import Iterator
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
##
|
|
12
|
-
|
|
13
4
|
|
|
14
5
|
_GLOBAL_BREAKPOINT = ContextVar("GLOBAL_BREAKPOINT", default=False)
|
|
15
6
|
|
|
@@ -25,17 +16,4 @@ def set_global_breakpoint() -> None:
|
|
|
25
16
|
_ = _GLOBAL_BREAKPOINT.set(True)
|
|
26
17
|
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@contextmanager
|
|
32
|
-
def yield_set_context(var: ContextVar[bool], /) -> Iterator[None]:
|
|
33
|
-
"""Yield a context var as being set."""
|
|
34
|
-
token = var.set(True)
|
|
35
|
-
try:
|
|
36
|
-
yield
|
|
37
|
-
finally:
|
|
38
|
-
_ = var.reset(token)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
__all__ = ["global_breakpoint", "set_global_breakpoint", "yield_set_context"]
|
|
19
|
+
__all__ = ["global_breakpoint", "set_global_breakpoint"]
|