dycw-utilities 0.152.0__py3-none-any.whl → 0.153.0__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.152.0.dist-info → dycw_utilities-0.153.0.dist-info}/METADATA +1 -1
- {dycw_utilities-0.152.0.dist-info → dycw_utilities-0.153.0.dist-info}/RECORD +26 -27
- utilities/__init__.py +1 -1
- utilities/asyncio.py +12 -11
- utilities/cryptography.py +3 -3
- utilities/eventkit.py +8 -8
- utilities/functions.py +1 -33
- utilities/logging.py +23 -24
- utilities/pathlib.py +34 -34
- utilities/postgres.py +12 -12
- utilities/pottery.py +5 -5
- utilities/pyinstrument.py +3 -3
- utilities/pytest.py +8 -8
- utilities/pytest_plugins/pytest_randomly.py +1 -1
- utilities/pytest_plugins/pytest_regressions.py +1 -1
- utilities/random.py +8 -6
- utilities/text.py +37 -2
- utilities/traceback.py +15 -18
- utilities/typed_settings.py +3 -3
- utilities/types.py +24 -14
- utilities/uuid.py +42 -5
- utilities/version.py +27 -26
- utilities/whenever.py +431 -37
- utilities/period.py +0 -370
- {dycw_utilities-0.152.0.dist-info → dycw_utilities-0.153.0.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.152.0.dist-info → dycw_utilities-0.153.0.dist-info}/entry_points.txt +0 -0
- {dycw_utilities-0.152.0.dist-info → dycw_utilities-0.153.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=YrB57gAJ2Dww1DzS0Q40Af0w6OpW_7druYx52IhzCcI,60
|
2
2
|
utilities/altair.py,sha256=92E2lCdyHY4Zb-vCw6rEJIsWdKipuu-Tu2ab1ufUfAk,9079
|
3
|
-
utilities/asyncio.py,sha256=
|
3
|
+
utilities/asyncio.py,sha256=QXkTtugXkqtYt7Do23zgYErqzdp6jwzPpV_SP9fJ1gI,16780
|
4
4
|
utilities/atomicwrites.py,sha256=tPo6r-Rypd9u99u66B9z86YBPpnLrlHtwox_8Z7T34Y,5790
|
5
5
|
utilities/atools.py,sha256=6neeCcgXxK2dlsc0xp15Za7nSucbCgFtAJepGI_-WXU,2549
|
6
6
|
utilities/cachetools.py,sha256=v1-9sXHLdOLiwmkq6NB0OUbxeKBuVVN6wmAWefWoaHI,2744
|
@@ -8,15 +8,15 @@ utilities/click.py,sha256=NXGzWoFOJqIblGwFqSGgXEDCKoV8E_JR58Lu2xJJFAo,17309
|
|
8
8
|
utilities/concurrent.py,sha256=fHeW2SZ_TEMfFY0C8pyQI6aPlnecvx9x6SuUwBWj_JY,2853
|
9
9
|
utilities/contextlib.py,sha256=m2D5bwvtCZLJcJ3IwVqyErYODuwJ1gLrT2UfATAQl-w,7435
|
10
10
|
utilities/contextvars.py,sha256=J8OhC7jqozAGYOCe2KUWysbPXNGe5JYz3HfaY_mIs08,883
|
11
|
-
utilities/cryptography.py,sha256=
|
11
|
+
utilities/cryptography.py,sha256=5PFrzsNUGHay91dFgYnDKwYprXxahrBqztmUqViRzBk,956
|
12
12
|
utilities/cvxpy.py,sha256=Rv1-fD-XYerosCavRF8Pohop2DBkU3AlFaGTfD8AEAA,13776
|
13
13
|
utilities/dataclasses.py,sha256=G05UH-fqUbcRPjQ8arK6K0Ap2fRbzEm0SZahJKCqYfY,32643
|
14
14
|
utilities/enum.py,sha256=5l6pwZD1cjSlVW4ss-zBPspWvrbrYrdtJWcg6f5_J5w,5781
|
15
15
|
utilities/errors.py,sha256=mFlDGSM0LI1jZ1pbqwLAH3ttLZ2JVIxyZLojw8tGVZU,1479
|
16
|
-
utilities/eventkit.py,sha256=
|
16
|
+
utilities/eventkit.py,sha256=ddoleSwW9zdc2tjX5Ge0pMKtYwV_JMxhHYOxnWX2AGM,12609
|
17
17
|
utilities/fastapi.py,sha256=3wpd63Tw9paSyy7STpAD7GGe8fLkLaRC6TPCwIGm1BU,1361
|
18
18
|
utilities/fpdf2.py,sha256=776PkEX5xEK-whFOzqaVaQVHPy1Xf01kCSyj7TEp80g,1886
|
19
|
-
utilities/functions.py,sha256=
|
19
|
+
utilities/functions.py,sha256=Teqm7ylOqEcBLHWhRwjKqouUfc6nv_6qO6fILpnOPyA,27937
|
20
20
|
utilities/functools.py,sha256=I00ru2gQPakZw2SHVeKIKXfTv741655s6HI0lUoE0D4,1552
|
21
21
|
utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
|
22
22
|
utilities/gzip.py,sha256=fkGP3KdsBfXlstodT4wtlp-PwNyUsogpbDCVVVGdsm4,781
|
@@ -31,7 +31,7 @@ utilities/json.py,sha256=-WcGtSsCr9Y42wHZzAMnfvU6ihAfVftylFfRUORaDFo,2102
|
|
31
31
|
utilities/jupyter.py,sha256=ft5JA7fBxXKzP-L9W8f2-wbF0QeYc_2uLQNFDVk4Z-M,2917
|
32
32
|
utilities/libcst.py,sha256=TKgKN4bNmtBNEE-TUfhTyd1BrTncfsl_7tTuhpesGYY,5585
|
33
33
|
utilities/lightweight_charts.py,sha256=YM3ojBvJxuCSUBu_KrhFBmaMCvRPvupKC3qkm-UVZq4,2751
|
34
|
-
utilities/logging.py,sha256=
|
34
|
+
utilities/logging.py,sha256=z2wIraTaTfQw-MjhhgIKeyWSyW5gCRX04gsdlknkTTs,19361
|
35
35
|
utilities/math.py,sha256=7ve4RxX3g-FGGVnWV0K9bBeGnKUEjnTbH13VxdvFtGE,26847
|
36
36
|
utilities/memory_profiler.py,sha256=XzN56jDCa5aqXS_DxEjb_K4L6aIWh_5zyKi6OhcIxw0,853
|
37
37
|
utilities/modules.py,sha256=iuvLluJya-hvl1Q25-Jk3dLgx2Es3ck4SjJiEkAlVTs,3195
|
@@ -42,21 +42,20 @@ utilities/optuna.py,sha256=C-fhWYiXHVPo1l8QctYkFJ4DyhbSrGorzP1dJb_qvd8,1933
|
|
42
42
|
utilities/orjson.py,sha256=Gzxn-s55pGFKV8DdsYXpp-Gr3r-5KdacYUd_GHMBogM,40088
|
43
43
|
utilities/os.py,sha256=mFvjydySvjtSXpk7tLStUJcndauAoujxUUmj_CO7LWY,3778
|
44
44
|
utilities/parse.py,sha256=JcJn5yXKhIWXBCwgBdPsyu7Hvcuw6kyEdqvaebCaI9k,17951
|
45
|
-
utilities/pathlib.py,sha256=
|
46
|
-
utilities/period.py,sha256=c4-N8N1GIUyomoHmSO7yPfYgK8tc__dxNSB79knlx2w,12471
|
45
|
+
utilities/pathlib.py,sha256=qGuU8XPmdgGpy8tOMUgelfXx3kxI8h9IaV3TI_06QGE,8428
|
47
46
|
utilities/pickle.py,sha256=MBT2xZCsv0pH868IXLGKnlcqNx2IRVKYNpRcqiQQqxw,653
|
48
47
|
utilities/platform.py,sha256=pTn7gw6N4T6LdKrf0virwarof_mze9WtoQlrGMzhGVI,2798
|
49
48
|
utilities/polars.py,sha256=uxV4liIQoCO92aERDO-AMfa8Es_-xCKgwZoyTO3PazQ,74410
|
50
49
|
utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
|
51
|
-
utilities/postgres.py,sha256=
|
52
|
-
utilities/pottery.py,sha256=
|
50
|
+
utilities/postgres.py,sha256=70BPcb_Na0LqDXJfqu0JwkHkGrwyuKpIVooSm6NlRn8,12467
|
51
|
+
utilities/pottery.py,sha256=HJ96oLRarTP37Vhg0WTyB3yAu2hETeg6HgRmpDIqyUs,6581
|
53
52
|
utilities/pqdm.py,sha256=z8bSMS7QJmWun65FQZruAqT-R3wqPAzNzhWcX9Nvr0A,3087
|
54
53
|
utilities/psutil.py,sha256=KUlu4lrUw9Zg1V7ZGetpWpGb9DB8l_SSDWGbANFNCPU,2104
|
55
54
|
utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
56
|
-
utilities/pyinstrument.py,sha256=
|
57
|
-
utilities/pytest.py,sha256=
|
55
|
+
utilities/pyinstrument.py,sha256=crJeEGOLxVt-tBGETb4E9v33kpaWmKrgXqL4PlFGRtk,850
|
56
|
+
utilities/pytest.py,sha256=2HHfAWkzZeK2OAzL2F49EDKooMkfDoGqg8Ev4cHC_N8,7869
|
58
57
|
utilities/pytest_regressions.py,sha256=ocjHTtfOeiGfQAKIei8pKNd61sxN9dawrJJ9gPt2wzA,4097
|
59
|
-
utilities/random.py,sha256=
|
58
|
+
utilities/random.py,sha256=sXYpWjiSTXdn0RYyBGmydpI1PldwNTG3ocZESBC5o4E,4119
|
60
59
|
utilities/re.py,sha256=S4h-DLL6ScMPqjboZ_uQ1BVTJajrqV06r_81D--_HCE,4573
|
61
60
|
utilities/redis.py,sha256=2fdveFbqL2pEAeyiVuN_Je8nSM_IZHeahPduMHhFRzY,28381
|
62
61
|
utilities/reprlib.py,sha256=ssYTcBW-TeRh3fhCJv57sopTZHF5FrPyyUg9yp5XBlo,3953
|
@@ -70,26 +69,26 @@ utilities/sqlalchemy_polars.py,sha256=Mm-sShZfqqgnzTrupMQdCfSM2akrybXHXAErTs-ofM
|
|
70
69
|
utilities/statsmodels.py,sha256=koyiBHvpMcSiBfh99wFUfSggLNx7cuAw3rwyfAhoKpQ,3410
|
71
70
|
utilities/string.py,sha256=shmBK87zZwzGyixuNuXCiUbqzfeZ9xlrFwz6JTaRvDk,582
|
72
71
|
utilities/tempfile.py,sha256=HxB2BF28CcecDJLQ3Bx2Ej-Pb6RJc6W9ngSpB9CnP4k,2018
|
73
|
-
utilities/text.py,sha256=
|
72
|
+
utilities/text.py,sha256=DVu1Cf2hdOyHbCZv56aPBDyjhT9HVSS8ko32YmqMVxM,12526
|
74
73
|
utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
75
74
|
utilities/timer.py,sha256=oXfTii6ymu57niP0BDGZjFD55LEHi2a19kqZKiTgaFQ,2588
|
76
|
-
utilities/traceback.py,sha256=
|
77
|
-
utilities/typed_settings.py,sha256=
|
78
|
-
utilities/types.py,sha256
|
75
|
+
utilities/traceback.py,sha256=e0BpxNMybVmELHGsYM5N6LVbfmn0jLhefLoa81NjZBg,9100
|
76
|
+
utilities/typed_settings.py,sha256=SFWqS3lAzV7IfNRwqFcTk0YynTcQ7BmrcW2mr_KUnos,4466
|
77
|
+
utilities/types.py,sha256=DMHTom46_vSwM4taulNgw6SEzseVggCVxX7e2JZnij8,18404
|
79
78
|
utilities/typing.py,sha256=Z-_XDaWyT_6wIo3qfNK-hvRlzxP2Jxa9PgXzm5rDYRA,13790
|
80
79
|
utilities/tzdata.py,sha256=fgNVj66yUbCSI_-vrRVzSD3gtf-L_8IEJEPjP_Jel5Y,266
|
81
80
|
utilities/tzlocal.py,sha256=KyCXEgCTjqGFx-389JdTuhMRUaT06U1RCMdWoED-qro,728
|
82
|
-
utilities/uuid.py,sha256=
|
83
|
-
utilities/version.py,sha256=
|
81
|
+
utilities/uuid.py,sha256=nQZs6tFX4mqtc2Ku3KqjloYCqwpTKeTj8eKwQwh3FQI,1572
|
82
|
+
utilities/version.py,sha256=ipBj5-WYY_nelp2uwFlApfWWCzTLzPwpovUi9x_OBMs,5085
|
84
83
|
utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
85
|
-
utilities/whenever.py,sha256=
|
84
|
+
utilities/whenever.py,sha256=_kMiXvmfuVud2i_2X3lO8h0U3KR7HrYqd-vwnShBAIg,57337
|
86
85
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
87
86
|
utilities/zoneinfo.py,sha256=FBMcUQ4662Aq8SsuCL1OAhDQiyANmVjtb-C30DRrWoE,1966
|
88
87
|
utilities/pytest_plugins/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
|
89
|
-
utilities/pytest_plugins/pytest_randomly.py,sha256=
|
90
|
-
utilities/pytest_plugins/pytest_regressions.py,sha256=
|
91
|
-
dycw_utilities-0.
|
92
|
-
dycw_utilities-0.
|
93
|
-
dycw_utilities-0.
|
94
|
-
dycw_utilities-0.
|
95
|
-
dycw_utilities-0.
|
88
|
+
utilities/pytest_plugins/pytest_randomly.py,sha256=B1qYVlExGOxTywq2r1SMi5o7btHLk2PNdY_b1p98dkE,409
|
89
|
+
utilities/pytest_plugins/pytest_regressions.py,sha256=9v8kAXDM2ycIXJBimoiF4EgrwbUvxTycFWJiGR_GHhM,1466
|
90
|
+
dycw_utilities-0.153.0.dist-info/METADATA,sha256=okQ-pxHQYem5BZer9IWNEOcPMJmwknoEv2SFJTHP8Ck,1696
|
91
|
+
dycw_utilities-0.153.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
92
|
+
dycw_utilities-0.153.0.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
|
93
|
+
dycw_utilities-0.153.0.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
94
|
+
dycw_utilities-0.153.0.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/asyncio.py
CHANGED
@@ -37,11 +37,12 @@ from typing import (
|
|
37
37
|
)
|
38
38
|
|
39
39
|
from utilities.errors import ImpossibleCaseError, is_instance_error
|
40
|
-
from utilities.functions import ensure_int, ensure_not_none
|
41
|
-
from utilities.logging import
|
40
|
+
from utilities.functions import ensure_int, ensure_not_none
|
41
|
+
from utilities.logging import to_logger
|
42
42
|
from utilities.random import SYSTEM_RANDOM
|
43
43
|
from utilities.sentinel import Sentinel, sentinel
|
44
44
|
from utilities.shelve import yield_shelf
|
45
|
+
from utilities.text import to_bool
|
45
46
|
from utilities.warnings import suppress_warnings
|
46
47
|
from utilities.whenever import get_now, round_date_or_date_time, to_nanoseconds
|
47
48
|
|
@@ -70,8 +71,8 @@ if TYPE_CHECKING:
|
|
70
71
|
Coro,
|
71
72
|
Delta,
|
72
73
|
ExceptionTypeLike,
|
73
|
-
|
74
|
-
|
74
|
+
LoggerLike,
|
75
|
+
MaybeCallableBoolLike,
|
75
76
|
MaybeType,
|
76
77
|
PathLike,
|
77
78
|
SupportsKeysAndGetItem,
|
@@ -231,7 +232,7 @@ class EnhancedTaskGroup(TaskGroup):
|
|
231
232
|
_semaphore: Semaphore | None
|
232
233
|
_timeout: Delta | None
|
233
234
|
_error: MaybeType[BaseException]
|
234
|
-
_debug:
|
235
|
+
_debug: MaybeCallableBoolLike
|
235
236
|
_stack: AsyncExitStack
|
236
237
|
_timeout_cm: _AsyncGeneratorContextManager[None] | None
|
237
238
|
|
@@ -242,7 +243,7 @@ class EnhancedTaskGroup(TaskGroup):
|
|
242
243
|
max_tasks: int | None = None,
|
243
244
|
timeout: Delta | None = None,
|
244
245
|
error: MaybeType[BaseException] = TimeoutError,
|
245
|
-
debug:
|
246
|
+
debug: MaybeCallableBoolLike = False,
|
246
247
|
) -> None:
|
247
248
|
super().__init__()
|
248
249
|
self._max_tasks = max_tasks
|
@@ -331,7 +332,7 @@ class EnhancedTaskGroup(TaskGroup):
|
|
331
332
|
assert_never(never)
|
332
333
|
|
333
334
|
def _is_debug(self) -> bool:
|
334
|
-
return to_bool(
|
335
|
+
return to_bool(self._debug) or (
|
335
336
|
(self._max_tasks is not None) and (self._max_tasks <= 0)
|
336
337
|
)
|
337
338
|
|
@@ -403,7 +404,7 @@ async def loop_until_succeed(
|
|
403
404
|
func: Callable[[], Coro[None]],
|
404
405
|
/,
|
405
406
|
*,
|
406
|
-
logger:
|
407
|
+
logger: LoggerLike | None = None,
|
407
408
|
errors: ExceptionTypeLike[Exception] | None = None,
|
408
409
|
sleep: Delta | None = None,
|
409
410
|
) -> bool:
|
@@ -414,7 +415,7 @@ async def loop_until_succeed(
|
|
414
415
|
await func()
|
415
416
|
except Exception as error: # noqa: BLE001
|
416
417
|
if logger is not None:
|
417
|
-
|
418
|
+
to_logger(logger).error("Error running %r", name, exc_info=True)
|
418
419
|
exc_type, exc_value, traceback = sys.exc_info()
|
419
420
|
if (exc_type is None) or (exc_value is None): # pragma: no cover
|
420
421
|
raise ImpossibleCaseError(
|
@@ -425,10 +426,10 @@ async def loop_until_succeed(
|
|
425
426
|
return False
|
426
427
|
if sleep is not None:
|
427
428
|
if logger is not None:
|
428
|
-
|
429
|
+
to_logger(logger).info("Sleeping for %s...", sleep)
|
429
430
|
await sleep_td(sleep)
|
430
431
|
if logger is not None:
|
431
|
-
|
432
|
+
to_logger(logger).info("Retrying %r...", name)
|
432
433
|
else:
|
433
434
|
return True
|
434
435
|
|
utilities/cryptography.py
CHANGED
@@ -11,18 +11,18 @@ _ENV_VAR = "FERNET_KEY"
|
|
11
11
|
|
12
12
|
def encrypt(text: str, /, *, env_var: str = _ENV_VAR) -> bytes:
|
13
13
|
"""Encrypt a string."""
|
14
|
-
return get_fernet(env_var
|
14
|
+
return get_fernet(env_var).encrypt(text.encode())
|
15
15
|
|
16
16
|
|
17
17
|
def decrypt(text: bytes, /, *, env_var: str = _ENV_VAR) -> str:
|
18
18
|
"""Encrypt a string."""
|
19
|
-
return get_fernet(env_var
|
19
|
+
return get_fernet(env_var).decrypt(text).decode()
|
20
20
|
|
21
21
|
|
22
22
|
##
|
23
23
|
|
24
24
|
|
25
|
-
def get_fernet(
|
25
|
+
def get_fernet(env_var: str = _ENV_VAR, /) -> Fernet:
|
26
26
|
"""Get the Fernet key."""
|
27
27
|
if (key := getenv(env_var)) is None:
|
28
28
|
raise GetFernetError(env_var=env_var)
|
utilities/eventkit.py
CHANGED
@@ -29,12 +29,12 @@ from eventkit import (
|
|
29
29
|
|
30
30
|
from utilities.functions import apply_decorators
|
31
31
|
from utilities.iterables import always_iterable
|
32
|
-
from utilities.logging import
|
32
|
+
from utilities.logging import to_logger
|
33
33
|
|
34
34
|
if TYPE_CHECKING:
|
35
35
|
from collections.abc import Callable
|
36
36
|
|
37
|
-
from utilities.types import Coro,
|
37
|
+
from utilities.types import Coro, LoggerLike, MaybeCoro, MaybeIterable, TypeLike
|
38
38
|
|
39
39
|
|
40
40
|
##
|
@@ -47,7 +47,7 @@ def add_listener[E: Event, F: Callable](
|
|
47
47
|
*,
|
48
48
|
error: Callable[[Event, BaseException], MaybeCoro[None]] | None = None,
|
49
49
|
ignore: TypeLike[BaseException] | None = None,
|
50
|
-
logger:
|
50
|
+
logger: LoggerLike | None = None,
|
51
51
|
decorators: MaybeIterable[Callable[[F], F]] | None = None,
|
52
52
|
done: Callable[..., MaybeCoro[None]] | None = None,
|
53
53
|
keep_ref: bool = False,
|
@@ -92,7 +92,7 @@ class LiftedEvent[F: Callable[..., MaybeCoro[None]]]:
|
|
92
92
|
*,
|
93
93
|
error: Callable[[Event, BaseException], MaybeCoro[None]] | None = None,
|
94
94
|
ignore: TypeLike[BaseException] | None = None,
|
95
|
-
logger:
|
95
|
+
logger: LoggerLike | None = None,
|
96
96
|
decorators: MaybeIterable[Callable[[F2], F2]] | None = None,
|
97
97
|
done: Callable[..., MaybeCoro[None]] | None = None,
|
98
98
|
keep_ref: bool = False,
|
@@ -255,7 +255,7 @@ class TypedEvent[F: Callable[..., MaybeCoro[None]]](Event):
|
|
255
255
|
keep_ref: bool = False,
|
256
256
|
*,
|
257
257
|
ignore: TypeLike[BaseException] | None = None,
|
258
|
-
logger:
|
258
|
+
logger: LoggerLike | None = None,
|
259
259
|
decorators: MaybeIterable[Callable[[F2], F2]] | None = None,
|
260
260
|
) -> Self:
|
261
261
|
lifted = lift_listener(
|
@@ -283,7 +283,7 @@ def lift_listener[F1: Callable[..., MaybeCoro[None]], F2: Callable](
|
|
283
283
|
*,
|
284
284
|
error: Callable[[Event, BaseException], MaybeCoro[None]] | None = None,
|
285
285
|
ignore: TypeLike[BaseException] | None = None,
|
286
|
-
logger:
|
286
|
+
logger: LoggerLike | None = None,
|
287
287
|
decorators: MaybeIterable[Callable[[F2], F2]] | None = None,
|
288
288
|
) -> F1:
|
289
289
|
match error, bool(iscoroutinefunction(listener)):
|
@@ -297,7 +297,7 @@ def lift_listener[F1: Callable[..., MaybeCoro[None]], F2: Callable](
|
|
297
297
|
except Exception as exc: # noqa: BLE001
|
298
298
|
if (ignore is not None) and isinstance(exc, ignore):
|
299
299
|
return
|
300
|
-
|
300
|
+
to_logger(logger).exception("")
|
301
301
|
|
302
302
|
lifted = listener_no_error_sync
|
303
303
|
|
@@ -311,7 +311,7 @@ def lift_listener[F1: Callable[..., MaybeCoro[None]], F2: Callable](
|
|
311
311
|
except Exception as exc: # noqa: BLE001
|
312
312
|
if (ignore is not None) and isinstance(exc, ignore):
|
313
313
|
return
|
314
|
-
|
314
|
+
to_logger(logger).exception("")
|
315
315
|
|
316
316
|
lifted = listener_no_error_async
|
317
317
|
case _, _:
|
utilities/functions.py
CHANGED
@@ -14,16 +14,7 @@ from types import (
|
|
14
14
|
MethodWrapperType,
|
15
15
|
WrapperDescriptorType,
|
16
16
|
)
|
17
|
-
from typing import
|
18
|
-
TYPE_CHECKING,
|
19
|
-
Any,
|
20
|
-
Literal,
|
21
|
-
TypeGuard,
|
22
|
-
assert_never,
|
23
|
-
cast,
|
24
|
-
overload,
|
25
|
-
override,
|
26
|
-
)
|
17
|
+
from typing import TYPE_CHECKING, Any, Literal, TypeGuard, cast, overload, override
|
27
18
|
|
28
19
|
from whenever import Date, PlainDateTime, Time, TimeDelta, ZonedDateTime
|
29
20
|
|
@@ -31,7 +22,6 @@ from utilities.reprlib import get_repr, get_repr_and_class
|
|
31
22
|
from utilities.sentinel import Sentinel, sentinel
|
32
23
|
from utilities.types import (
|
33
24
|
Dataclass,
|
34
|
-
MaybeCallableBool,
|
35
25
|
Number,
|
36
26
|
StrMapping,
|
37
27
|
SupportsRichComparison,
|
@@ -945,28 +935,6 @@ def second[U](pair: tuple[Any, U], /) -> U:
|
|
945
935
|
##
|
946
936
|
|
947
937
|
|
948
|
-
@overload
|
949
|
-
def to_bool(*, bool_: MaybeCallableBool) -> bool: ...
|
950
|
-
@overload
|
951
|
-
def to_bool(*, bool_: None) -> None: ...
|
952
|
-
@overload
|
953
|
-
def to_bool(*, bool_: Sentinel) -> Sentinel: ...
|
954
|
-
def to_bool(
|
955
|
-
*, bool_: MaybeCallableBool | None | Sentinel = sentinel
|
956
|
-
) -> bool | None | Sentinel:
|
957
|
-
"""Get the bool."""
|
958
|
-
match bool_:
|
959
|
-
case bool() | None | Sentinel():
|
960
|
-
return bool_
|
961
|
-
case Callable() as func:
|
962
|
-
return to_bool(bool_=func())
|
963
|
-
case never:
|
964
|
-
assert_never(never)
|
965
|
-
|
966
|
-
|
967
|
-
##
|
968
|
-
|
969
|
-
|
970
938
|
def yield_object_attributes(
|
971
939
|
obj: Any,
|
972
940
|
/,
|
utilities/logging.py
CHANGED
@@ -37,7 +37,7 @@ from utilities.atomicwrites import move_many
|
|
37
37
|
from utilities.dataclasses import replace_non_sentinel
|
38
38
|
from utilities.errors import ImpossibleCaseError
|
39
39
|
from utilities.iterables import OneEmptyError, always_iterable, one
|
40
|
-
from utilities.pathlib import ensure_suffix,
|
40
|
+
from utilities.pathlib import ensure_suffix, to_path
|
41
41
|
from utilities.re import (
|
42
42
|
ExtractGroupError,
|
43
43
|
ExtractGroupsError,
|
@@ -59,7 +59,7 @@ if TYPE_CHECKING:
|
|
59
59
|
from logging import _FilterType
|
60
60
|
|
61
61
|
from utilities.types import (
|
62
|
-
|
62
|
+
LoggerLike,
|
63
63
|
LogLevel,
|
64
64
|
MaybeCallablePathLike,
|
65
65
|
MaybeIterable,
|
@@ -87,7 +87,7 @@ def add_filters(handler: Handler, /, *filters: _FilterType) -> None:
|
|
87
87
|
|
88
88
|
def basic_config(
|
89
89
|
*,
|
90
|
-
obj:
|
90
|
+
obj: LoggerLike | Handler | None = None,
|
91
91
|
format_: str | None = None,
|
92
92
|
prefix: str | None = None,
|
93
93
|
hostname: bool = False,
|
@@ -121,7 +121,7 @@ def basic_config(
|
|
121
121
|
)
|
122
122
|
case str() as name:
|
123
123
|
basic_config(
|
124
|
-
obj=
|
124
|
+
obj=to_logger(name),
|
125
125
|
format_=format_,
|
126
126
|
prefix=prefix,
|
127
127
|
hostname=hostname,
|
@@ -268,20 +268,6 @@ def _get_plain_formatter(
|
|
268
268
|
##
|
269
269
|
|
270
270
|
|
271
|
-
def get_logger(*, logger: LoggerOrName | None = None) -> Logger:
|
272
|
-
"""Get a logger."""
|
273
|
-
match logger:
|
274
|
-
case Logger():
|
275
|
-
return logger
|
276
|
-
case str() | None:
|
277
|
-
return getLogger(logger)
|
278
|
-
case never:
|
279
|
-
assert_never(never)
|
280
|
-
|
281
|
-
|
282
|
-
##
|
283
|
-
|
284
|
-
|
285
271
|
def get_logging_level_number(level: LogLevel, /) -> int:
|
286
272
|
"""Get the logging level number."""
|
287
273
|
mapping = getLevelNamesMapping()
|
@@ -305,13 +291,13 @@ class GetLoggingLevelNumberError(Exception):
|
|
305
291
|
|
306
292
|
def setup_logging(
|
307
293
|
*,
|
308
|
-
logger:
|
294
|
+
logger: LoggerLike | None = None,
|
309
295
|
format_: str | None = None,
|
310
296
|
datefmt: str = _DEFAULT_DATEFMT,
|
311
297
|
console_level: LogLevel = "INFO",
|
312
298
|
console_prefix: str = "❯", # noqa: RUF001
|
313
299
|
console_filters: MaybeIterable[_FilterType] | None = None,
|
314
|
-
files_dir: MaybeCallablePathLike
|
300
|
+
files_dir: MaybeCallablePathLike = Path.cwd,
|
315
301
|
files_max_bytes: int = _DEFAULT_MAX_BYTES,
|
316
302
|
files_when: _When = _DEFAULT_WHEN,
|
317
303
|
files_interval: int = 1,
|
@@ -327,15 +313,14 @@ def setup_logging(
|
|
327
313
|
level=console_level,
|
328
314
|
filters=console_filters,
|
329
315
|
)
|
330
|
-
logger_use =
|
316
|
+
logger_use = to_logger(logger)
|
331
317
|
name = logger_use.name
|
332
|
-
dir_ = get_path(path=files_dir)
|
333
318
|
levels: list[LogLevel] = ["DEBUG", "INFO", "ERROR"]
|
334
319
|
for level in levels:
|
335
320
|
lower = level.lower()
|
336
321
|
for stem in [lower, f"{name}-{lower}"]:
|
337
322
|
handler = SizeAndTimeRotatingFileHandler(
|
338
|
-
|
323
|
+
to_path(files_dir).joinpath(stem).with_suffix(".txt"),
|
339
324
|
maxBytes=files_max_bytes,
|
340
325
|
when=files_when,
|
341
326
|
interval=files_interval,
|
@@ -626,6 +611,20 @@ class _Rotation:
|
|
626
611
|
return self.file.replace(index=self.index, start=self.start, end=self.end).path
|
627
612
|
|
628
613
|
|
614
|
+
##
|
615
|
+
|
616
|
+
|
617
|
+
def to_logger(logger: LoggerLike | None = None, /) -> Logger:
|
618
|
+
"""Convert to a logger."""
|
619
|
+
match logger:
|
620
|
+
case Logger():
|
621
|
+
return logger
|
622
|
+
case str() | None:
|
623
|
+
return getLogger(logger)
|
624
|
+
case never:
|
625
|
+
assert_never(never)
|
626
|
+
|
627
|
+
|
629
628
|
__all__ = [
|
630
629
|
"FilterForKeyError",
|
631
630
|
"GetLoggingLevelNumberError",
|
@@ -634,7 +633,7 @@ __all__ = [
|
|
634
633
|
"basic_config",
|
635
634
|
"filter_for_key",
|
636
635
|
"get_format_str",
|
637
|
-
"get_logger",
|
638
636
|
"get_logging_level_number",
|
639
637
|
"setup_logging",
|
638
|
+
"to_logger",
|
640
639
|
]
|
utilities/pathlib.py
CHANGED
@@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Literal, assert_never, overload, override
|
|
12
12
|
|
13
13
|
from utilities.contextlib import enhanced_context_manager
|
14
14
|
from utilities.errors import ImpossibleCaseError
|
15
|
-
from utilities.sentinel import Sentinel
|
15
|
+
from utilities.sentinel import Sentinel
|
16
16
|
|
17
17
|
if TYPE_CHECKING:
|
18
18
|
from collections.abc import Iterator, Sequence
|
@@ -52,33 +52,9 @@ def expand_path(path: PathLike, /) -> Path:
|
|
52
52
|
##
|
53
53
|
|
54
54
|
|
55
|
-
|
56
|
-
def get_path(*, path: MaybeCallablePathLike | None) -> Path: ...
|
57
|
-
@overload
|
58
|
-
def get_path(*, path: Sentinel) -> Sentinel: ...
|
59
|
-
def get_path(
|
60
|
-
*, path: MaybeCallablePathLike | None | Sentinel = sentinel
|
61
|
-
) -> Path | None | Sentinel:
|
62
|
-
"""Get the path."""
|
63
|
-
match path:
|
64
|
-
case Path() | Sentinel():
|
65
|
-
return path
|
66
|
-
case str():
|
67
|
-
return Path(path)
|
68
|
-
case None:
|
69
|
-
return Path.cwd()
|
70
|
-
case Callable() as func:
|
71
|
-
return get_path(path=func())
|
72
|
-
case never:
|
73
|
-
assert_never(never)
|
74
|
-
|
75
|
-
|
76
|
-
##
|
77
|
-
|
78
|
-
|
79
|
-
def get_package_root(*, path: MaybeCallablePathLike | None = None) -> Path:
|
55
|
+
def get_package_root(path: MaybeCallablePathLike = Path.cwd, /) -> Path:
|
80
56
|
"""Get the package root."""
|
81
|
-
path =
|
57
|
+
path = to_path(path)
|
82
58
|
path_dir = path.parent if path.is_file() else path
|
83
59
|
all_paths = list(chain([path_dir], path_dir.parents))
|
84
60
|
try:
|
@@ -103,9 +79,9 @@ class GetPackageRootError(Exception):
|
|
103
79
|
##
|
104
80
|
|
105
81
|
|
106
|
-
def get_repo_root(
|
82
|
+
def get_repo_root(path: MaybeCallablePathLike = Path.cwd, /) -> Path:
|
107
83
|
"""Get the repo root."""
|
108
|
-
path =
|
84
|
+
path = to_path(path)
|
109
85
|
path_dir = path.parent if path.is_file() else path
|
110
86
|
try:
|
111
87
|
output = check_output(
|
@@ -136,15 +112,15 @@ class GetRepoRootError(Exception):
|
|
136
112
|
##
|
137
113
|
|
138
114
|
|
139
|
-
def get_root(
|
115
|
+
def get_root(path: MaybeCallablePathLike = Path.cwd, /) -> Path:
|
140
116
|
"""Get the root of a path."""
|
141
|
-
path =
|
117
|
+
path = to_path(path)
|
142
118
|
try:
|
143
|
-
repo = get_repo_root(path
|
119
|
+
repo = get_repo_root(path)
|
144
120
|
except GetRepoRootError:
|
145
121
|
repo = None
|
146
122
|
try:
|
147
|
-
package = get_package_root(path
|
123
|
+
package = get_package_root(path)
|
148
124
|
except GetPackageRootError:
|
149
125
|
package = None
|
150
126
|
match repo, package:
|
@@ -305,6 +281,30 @@ def temp_cwd(path: PathLike, /) -> Iterator[None]:
|
|
305
281
|
chdir(prev)
|
306
282
|
|
307
283
|
|
284
|
+
##
|
285
|
+
|
286
|
+
|
287
|
+
@overload
|
288
|
+
def to_path(path: Sentinel, /) -> Sentinel: ...
|
289
|
+
@overload
|
290
|
+
def to_path(path: MaybeCallablePathLike | None = Path.cwd, /) -> Path: ...
|
291
|
+
def to_path(
|
292
|
+
path: MaybeCallablePathLike | None | Sentinel = Path.cwd, /
|
293
|
+
) -> Path | Sentinel:
|
294
|
+
"""Get the path."""
|
295
|
+
match path:
|
296
|
+
case Path() | Sentinel():
|
297
|
+
return path
|
298
|
+
case None:
|
299
|
+
return Path.cwd()
|
300
|
+
case str():
|
301
|
+
return Path(path)
|
302
|
+
case Callable() as func:
|
303
|
+
return to_path(func())
|
304
|
+
case never:
|
305
|
+
assert_never(never)
|
306
|
+
|
307
|
+
|
308
308
|
__all__ = [
|
309
309
|
"PWD",
|
310
310
|
"GetPackageRootError",
|
@@ -313,11 +313,11 @@ __all__ = [
|
|
313
313
|
"ensure_suffix",
|
314
314
|
"expand_path",
|
315
315
|
"get_package_root",
|
316
|
-
"get_path",
|
317
316
|
"get_repo_root",
|
318
317
|
"get_tail",
|
319
318
|
"is_sub_path",
|
320
319
|
"list_dir",
|
321
320
|
"module_path",
|
322
321
|
"temp_cwd",
|
322
|
+
"to_path",
|
323
323
|
]
|