dycw-utilities 0.148.4__py3-none-any.whl → 0.174.12__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.
Potentially problematic release.
This version of dycw-utilities might be problematic. Click here for more details.
- dycw_utilities-0.174.12.dist-info/METADATA +41 -0
- dycw_utilities-0.174.12.dist-info/RECORD +104 -0
- dycw_utilities-0.174.12.dist-info/WHEEL +4 -0
- {dycw_utilities-0.148.4.dist-info → dycw_utilities-0.174.12.dist-info}/entry_points.txt +3 -0
- utilities/__init__.py +1 -1
- utilities/{eventkit.py → aeventkit.py} +12 -11
- utilities/altair.py +7 -6
- utilities/asyncio.py +113 -64
- utilities/atomicwrites.py +1 -1
- utilities/atools.py +64 -4
- utilities/cachetools.py +9 -6
- utilities/click.py +145 -49
- utilities/concurrent.py +1 -1
- utilities/contextlib.py +4 -2
- utilities/contextvars.py +20 -1
- utilities/cryptography.py +3 -3
- utilities/dataclasses.py +15 -28
- utilities/docker.py +292 -0
- utilities/enum.py +2 -2
- utilities/errors.py +1 -1
- utilities/fastapi.py +8 -3
- utilities/fpdf2.py +2 -2
- utilities/functions.py +20 -297
- utilities/git.py +19 -0
- utilities/grp.py +28 -0
- utilities/hypothesis.py +360 -78
- utilities/inflect.py +1 -1
- utilities/iterables.py +12 -58
- utilities/jinja2.py +148 -0
- utilities/json.py +1 -1
- utilities/libcst.py +7 -7
- utilities/logging.py +74 -85
- utilities/math.py +8 -4
- utilities/more_itertools.py +4 -6
- utilities/operator.py +1 -1
- utilities/orjson.py +86 -34
- utilities/os.py +49 -2
- utilities/parse.py +2 -2
- utilities/pathlib.py +66 -34
- utilities/permissions.py +297 -0
- utilities/platform.py +5 -5
- utilities/polars.py +932 -420
- utilities/polars_ols.py +1 -1
- utilities/postgres.py +299 -174
- utilities/pottery.py +8 -73
- utilities/pqdm.py +3 -3
- utilities/pwd.py +28 -0
- utilities/pydantic.py +11 -0
- utilities/pydantic_settings.py +240 -0
- utilities/pydantic_settings_sops.py +76 -0
- utilities/pyinstrument.py +5 -5
- utilities/pytest.py +155 -46
- utilities/pytest_plugins/pytest_randomly.py +1 -1
- utilities/pytest_plugins/pytest_regressions.py +7 -3
- utilities/pytest_regressions.py +2 -3
- utilities/random.py +11 -6
- utilities/re.py +1 -1
- utilities/redis.py +101 -64
- utilities/sentinel.py +10 -0
- utilities/shelve.py +4 -1
- utilities/shutil.py +25 -0
- utilities/slack_sdk.py +8 -3
- utilities/sqlalchemy.py +422 -352
- utilities/sqlalchemy_polars.py +28 -52
- utilities/string.py +1 -1
- utilities/subprocess.py +864 -0
- utilities/tempfile.py +62 -4
- utilities/testbook.py +50 -0
- utilities/text.py +165 -42
- utilities/timer.py +2 -2
- utilities/traceback.py +46 -36
- utilities/types.py +62 -23
- utilities/typing.py +479 -19
- utilities/uuid.py +42 -5
- utilities/version.py +27 -26
- utilities/whenever.py +661 -151
- utilities/zoneinfo.py +80 -22
- dycw_utilities-0.148.4.dist-info/METADATA +0 -41
- dycw_utilities-0.148.4.dist-info/RECORD +0 -95
- dycw_utilities-0.148.4.dist-info/WHEEL +0 -4
- dycw_utilities-0.148.4.dist-info/licenses/LICENSE +0 -21
- utilities/period.py +0 -237
- utilities/typed_settings.py +0 -144
utilities/logging.py
CHANGED
|
@@ -8,6 +8,7 @@ from logging import (
|
|
|
8
8
|
Formatter,
|
|
9
9
|
Handler,
|
|
10
10
|
Logger,
|
|
11
|
+
LoggerAdapter,
|
|
11
12
|
LogRecord,
|
|
12
13
|
StreamHandler,
|
|
13
14
|
basicConfig,
|
|
@@ -22,6 +23,7 @@ from socket import gethostname
|
|
|
22
23
|
from typing import (
|
|
23
24
|
TYPE_CHECKING,
|
|
24
25
|
Any,
|
|
26
|
+
Concatenate,
|
|
25
27
|
Literal,
|
|
26
28
|
NotRequired,
|
|
27
29
|
Self,
|
|
@@ -31,13 +33,13 @@ from typing import (
|
|
|
31
33
|
override,
|
|
32
34
|
)
|
|
33
35
|
|
|
34
|
-
from whenever import
|
|
36
|
+
from whenever import ZonedDateTime
|
|
35
37
|
|
|
36
38
|
from utilities.atomicwrites import move_many
|
|
37
39
|
from utilities.dataclasses import replace_non_sentinel
|
|
38
40
|
from utilities.errors import ImpossibleCaseError
|
|
39
41
|
from utilities.iterables import OneEmptyError, always_iterable, one
|
|
40
|
-
from utilities.pathlib import ensure_suffix,
|
|
42
|
+
from utilities.pathlib import ensure_suffix, to_path
|
|
41
43
|
from utilities.re import (
|
|
42
44
|
ExtractGroupError,
|
|
43
45
|
ExtractGroupsError,
|
|
@@ -45,25 +47,25 @@ from utilities.re import (
|
|
|
45
47
|
extract_groups,
|
|
46
48
|
)
|
|
47
49
|
from utilities.sentinel import Sentinel, sentinel
|
|
48
|
-
from utilities.tzlocal import LOCAL_TIME_ZONE_NAME
|
|
49
50
|
from utilities.whenever import (
|
|
50
51
|
WheneverLogRecord,
|
|
51
52
|
format_compact,
|
|
52
53
|
get_now_local,
|
|
53
|
-
|
|
54
|
+
to_zoned_date_time,
|
|
54
55
|
)
|
|
55
56
|
|
|
56
57
|
if TYPE_CHECKING:
|
|
57
|
-
from collections.abc import Callable, Iterable, Mapping
|
|
58
|
+
from collections.abc import Callable, Iterable, Mapping, MutableMapping
|
|
58
59
|
from datetime import time
|
|
59
60
|
from logging import _FilterType
|
|
60
61
|
|
|
61
62
|
from utilities.types import (
|
|
62
|
-
|
|
63
|
+
LoggerLike,
|
|
63
64
|
LogLevel,
|
|
64
65
|
MaybeCallablePathLike,
|
|
65
66
|
MaybeIterable,
|
|
66
67
|
PathLike,
|
|
68
|
+
StrMapping,
|
|
67
69
|
)
|
|
68
70
|
|
|
69
71
|
|
|
@@ -76,6 +78,35 @@ _DEFAULT_WHEN: _When = "D"
|
|
|
76
78
|
##
|
|
77
79
|
|
|
78
80
|
|
|
81
|
+
def add_adapter[**P](
|
|
82
|
+
logger: Logger,
|
|
83
|
+
process: Callable[Concatenate[str, P], str],
|
|
84
|
+
/,
|
|
85
|
+
*args: P.args,
|
|
86
|
+
**kwargs: P.kwargs,
|
|
87
|
+
) -> LoggerAdapter:
|
|
88
|
+
"""Add an adapter to a logger."""
|
|
89
|
+
|
|
90
|
+
class CustomAdapter(LoggerAdapter):
|
|
91
|
+
@override
|
|
92
|
+
def process(
|
|
93
|
+
self, msg: str, kwargs: MutableMapping[str, Any]
|
|
94
|
+
) -> tuple[str, MutableMapping[str, Any]]:
|
|
95
|
+
extra = cast("_ArgsAndKwargs", self.extra)
|
|
96
|
+
new_msg = process(msg, *extra["args"], **extra["kwargs"])
|
|
97
|
+
return new_msg, kwargs
|
|
98
|
+
|
|
99
|
+
return CustomAdapter(logger, extra=_ArgsAndKwargs(args=args, kwargs=kwargs))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class _ArgsAndKwargs(TypedDict):
|
|
103
|
+
args: tuple[Any, ...]
|
|
104
|
+
kwargs: StrMapping
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
|
|
109
|
+
|
|
79
110
|
def add_filters(handler: Handler, /, *filters: _FilterType) -> None:
|
|
80
111
|
"""Add a set of filters to a handler."""
|
|
81
112
|
for filter_i in filters:
|
|
@@ -87,7 +118,7 @@ def add_filters(handler: Handler, /, *filters: _FilterType) -> None:
|
|
|
87
118
|
|
|
88
119
|
def basic_config(
|
|
89
120
|
*,
|
|
90
|
-
obj:
|
|
121
|
+
obj: LoggerLike | Handler | None = None,
|
|
91
122
|
format_: str | None = None,
|
|
92
123
|
prefix: str | None = None,
|
|
93
124
|
hostname: bool = False,
|
|
@@ -121,7 +152,7 @@ def basic_config(
|
|
|
121
152
|
)
|
|
122
153
|
case str() as name:
|
|
123
154
|
basic_config(
|
|
124
|
-
obj=
|
|
155
|
+
obj=to_logger(name),
|
|
125
156
|
format_=format_,
|
|
126
157
|
prefix=prefix,
|
|
127
158
|
hostname=hostname,
|
|
@@ -144,49 +175,13 @@ def basic_config(
|
|
|
144
175
|
color_field_styles=color_field_styles,
|
|
145
176
|
)
|
|
146
177
|
handler.setFormatter(formatter)
|
|
147
|
-
case
|
|
178
|
+
case never:
|
|
148
179
|
assert_never(never)
|
|
149
180
|
|
|
150
181
|
|
|
151
182
|
##
|
|
152
183
|
|
|
153
184
|
|
|
154
|
-
def filter_for_key(
|
|
155
|
-
key: str, /, *, default: bool = False
|
|
156
|
-
) -> Callable[[LogRecord], bool]:
|
|
157
|
-
"""Make a filter for a given attribute."""
|
|
158
|
-
if (key in _FILTER_FOR_KEY_BLACKLIST) or key.startswith("__"):
|
|
159
|
-
raise FilterForKeyError(key=key)
|
|
160
|
-
|
|
161
|
-
def filter_(record: LogRecord, /) -> bool:
|
|
162
|
-
try:
|
|
163
|
-
value = getattr(record, key)
|
|
164
|
-
except AttributeError:
|
|
165
|
-
return default
|
|
166
|
-
return bool(value)
|
|
167
|
-
|
|
168
|
-
return filter_
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
# fmt: off
|
|
172
|
-
_FILTER_FOR_KEY_BLACKLIST = {
|
|
173
|
-
"args", "created", "exc_info", "exc_text", "filename", "funcName", "getMessage", "levelname", "levelno", "lineno", "module", "msecs", "msg", "name", "pathname", "process", "processName", "relativeCreated", "stack_info", "taskName", "thread", "threadName"
|
|
174
|
-
}
|
|
175
|
-
# fmt: on
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
@dataclass(kw_only=True, slots=True)
|
|
179
|
-
class FilterForKeyError(Exception):
|
|
180
|
-
key: str
|
|
181
|
-
|
|
182
|
-
@override
|
|
183
|
-
def __str__(self) -> str:
|
|
184
|
-
return f"Invalid key: {self.key!r}"
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
##
|
|
188
|
-
|
|
189
|
-
|
|
190
185
|
def get_format_str(*, prefix: str | None = None, hostname: bool = False) -> str:
|
|
191
186
|
"""Generate a format string."""
|
|
192
187
|
parts: list[str] = [
|
|
@@ -268,20 +263,6 @@ def _get_plain_formatter(
|
|
|
268
263
|
##
|
|
269
264
|
|
|
270
265
|
|
|
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 _ as never:
|
|
279
|
-
assert_never(never)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
##
|
|
283
|
-
|
|
284
|
-
|
|
285
266
|
def get_logging_level_number(level: LogLevel, /) -> int:
|
|
286
267
|
"""Get the logging level number."""
|
|
287
268
|
mapping = getLevelNamesMapping()
|
|
@@ -305,13 +286,13 @@ class GetLoggingLevelNumberError(Exception):
|
|
|
305
286
|
|
|
306
287
|
def setup_logging(
|
|
307
288
|
*,
|
|
308
|
-
logger:
|
|
289
|
+
logger: LoggerLike | None = None,
|
|
309
290
|
format_: str | None = None,
|
|
310
291
|
datefmt: str = _DEFAULT_DATEFMT,
|
|
311
292
|
console_level: LogLevel = "INFO",
|
|
312
293
|
console_prefix: str = "❯", # noqa: RUF001
|
|
313
294
|
console_filters: MaybeIterable[_FilterType] | None = None,
|
|
314
|
-
files_dir: MaybeCallablePathLike
|
|
295
|
+
files_dir: MaybeCallablePathLike = Path.cwd,
|
|
315
296
|
files_max_bytes: int = _DEFAULT_MAX_BYTES,
|
|
316
297
|
files_when: _When = _DEFAULT_WHEN,
|
|
317
298
|
files_interval: int = 1,
|
|
@@ -327,15 +308,14 @@ def setup_logging(
|
|
|
327
308
|
level=console_level,
|
|
328
309
|
filters=console_filters,
|
|
329
310
|
)
|
|
330
|
-
logger_use =
|
|
311
|
+
logger_use = to_logger(logger)
|
|
331
312
|
name = logger_use.name
|
|
332
|
-
dir_ = get_path(path=files_dir)
|
|
333
313
|
levels: list[LogLevel] = ["DEBUG", "INFO", "ERROR"]
|
|
334
314
|
for level in levels:
|
|
335
315
|
lower = level.lower()
|
|
336
316
|
for stem in [lower, f"{name}-{lower}"]:
|
|
337
317
|
handler = SizeAndTimeRotatingFileHandler(
|
|
338
|
-
|
|
318
|
+
to_path(files_dir).joinpath(stem).with_suffix(".txt"),
|
|
339
319
|
maxBytes=files_max_bytes,
|
|
340
320
|
when=files_when,
|
|
341
321
|
interval=files_interval,
|
|
@@ -407,9 +387,7 @@ class SizeAndTimeRotatingFileHandler(BaseRotatingHandler):
|
|
|
407
387
|
def emit(self, record: LogRecord) -> None:
|
|
408
388
|
try:
|
|
409
389
|
if (self._backup_count is not None) and self._should_rollover(record):
|
|
410
|
-
self._do_rollover(
|
|
411
|
-
backup_count=self._backup_count
|
|
412
|
-
)
|
|
390
|
+
self._do_rollover(backup_count=self._backup_count)
|
|
413
391
|
FileHandler.emit(self, record)
|
|
414
392
|
except Exception: # noqa: BLE001 # pragma: no cover
|
|
415
393
|
self.handleError(record)
|
|
@@ -419,23 +397,23 @@ class SizeAndTimeRotatingFileHandler(BaseRotatingHandler):
|
|
|
419
397
|
self.stream.close()
|
|
420
398
|
self.stream = None
|
|
421
399
|
|
|
422
|
-
actions = _compute_rollover_actions(
|
|
400
|
+
actions = _compute_rollover_actions(
|
|
423
401
|
self._directory,
|
|
424
402
|
self._stem,
|
|
425
403
|
self._suffix,
|
|
426
404
|
patterns=self._patterns,
|
|
427
405
|
backup_count=backup_count,
|
|
428
406
|
)
|
|
429
|
-
actions.do()
|
|
407
|
+
actions.do()
|
|
430
408
|
|
|
431
409
|
if not self.delay: # pragma: no cover
|
|
432
410
|
self.stream = self._open()
|
|
433
|
-
self._time_handler.rolloverAt = (
|
|
434
|
-
|
|
411
|
+
self._time_handler.rolloverAt = self._time_handler.computeRollover(
|
|
412
|
+
get_now_local().timestamp()
|
|
435
413
|
)
|
|
436
414
|
|
|
437
415
|
def _should_rollover(self, record: LogRecord, /) -> bool:
|
|
438
|
-
if self._max_bytes is not None:
|
|
416
|
+
if self._max_bytes is not None:
|
|
439
417
|
try:
|
|
440
418
|
size = self._filename.stat().st_size
|
|
441
419
|
except FileNotFoundError:
|
|
@@ -443,14 +421,14 @@ class SizeAndTimeRotatingFileHandler(BaseRotatingHandler):
|
|
|
443
421
|
else:
|
|
444
422
|
if size >= self._max_bytes:
|
|
445
423
|
return True
|
|
446
|
-
return bool(self._time_handler.shouldRollover(record))
|
|
424
|
+
return bool(self._time_handler.shouldRollover(record))
|
|
447
425
|
|
|
448
426
|
|
|
449
427
|
def _compute_rollover_patterns(stem: str, suffix: str, /) -> _RolloverPatterns:
|
|
450
428
|
return _RolloverPatterns(
|
|
451
429
|
pattern1=re.compile(rf"^{stem}\.(\d+){suffix}$"),
|
|
452
|
-
pattern2=re.compile(rf"^{stem}\.(\d+)__(
|
|
453
|
-
pattern3=re.compile(rf"^{stem}\.(\d+)__(
|
|
430
|
+
pattern2=re.compile(rf"^{stem}\.(\d+)__(.+?){suffix}$"),
|
|
431
|
+
pattern3=re.compile(rf"^{stem}\.(\d+)__(.+?)__(.+?){suffix}$"),
|
|
454
432
|
)
|
|
455
433
|
|
|
456
434
|
|
|
@@ -550,10 +528,8 @@ class _RotatingLogFile:
|
|
|
550
528
|
stem=stem,
|
|
551
529
|
suffix=suffix,
|
|
552
530
|
index=int(index),
|
|
553
|
-
start=
|
|
554
|
-
|
|
555
|
-
),
|
|
556
|
-
end=PlainDateTime.parse_common_iso(end).assume_tz(LOCAL_TIME_ZONE_NAME),
|
|
531
|
+
start=to_zoned_date_time(start),
|
|
532
|
+
end=to_zoned_date_time(end),
|
|
557
533
|
)
|
|
558
534
|
try:
|
|
559
535
|
index, end = extract_groups(patterns.pattern2, path.name)
|
|
@@ -565,7 +541,7 @@ class _RotatingLogFile:
|
|
|
565
541
|
stem=stem,
|
|
566
542
|
suffix=suffix,
|
|
567
543
|
index=int(index),
|
|
568
|
-
end=
|
|
544
|
+
end=to_zoned_date_time(end),
|
|
569
545
|
)
|
|
570
546
|
try:
|
|
571
547
|
index = extract_group(patterns.pattern1, path.name)
|
|
@@ -586,9 +562,9 @@ class _RotatingLogFile:
|
|
|
586
562
|
case int() as index, None, None:
|
|
587
563
|
tail = str(index)
|
|
588
564
|
case int() as index, None, ZonedDateTime() as end:
|
|
589
|
-
tail = f"{index}__{format_compact(
|
|
565
|
+
tail = f"{index}__{format_compact(end, path=True)}"
|
|
590
566
|
case int() as index, ZonedDateTime() as start, ZonedDateTime() as end:
|
|
591
|
-
tail = f"{index}__{format_compact(
|
|
567
|
+
tail = f"{index}__{format_compact(start, path=True)}__{format_compact(end, path=True)}"
|
|
592
568
|
case _: # pragma: no cover
|
|
593
569
|
raise ImpossibleCaseError(
|
|
594
570
|
case=[f"{self.index=}", f"{self.start=}", f"{self.end=}"]
|
|
@@ -626,15 +602,28 @@ class _Rotation:
|
|
|
626
602
|
return self.file.replace(index=self.index, start=self.start, end=self.end).path
|
|
627
603
|
|
|
628
604
|
|
|
605
|
+
##
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def to_logger(logger: LoggerLike | None = None, /) -> Logger:
|
|
609
|
+
"""Convert to a logger."""
|
|
610
|
+
match logger:
|
|
611
|
+
case Logger():
|
|
612
|
+
return logger
|
|
613
|
+
case str() | None:
|
|
614
|
+
return getLogger(logger)
|
|
615
|
+
case never:
|
|
616
|
+
assert_never(never)
|
|
617
|
+
|
|
618
|
+
|
|
629
619
|
__all__ = [
|
|
630
|
-
"FilterForKeyError",
|
|
631
620
|
"GetLoggingLevelNumberError",
|
|
632
621
|
"SizeAndTimeRotatingFileHandler",
|
|
622
|
+
"add_adapter",
|
|
633
623
|
"add_filters",
|
|
634
624
|
"basic_config",
|
|
635
|
-
"filter_for_key",
|
|
636
625
|
"get_format_str",
|
|
637
|
-
"get_logger",
|
|
638
626
|
"get_logging_level_number",
|
|
639
627
|
"setup_logging",
|
|
628
|
+
"to_logger",
|
|
640
629
|
]
|
utilities/math.py
CHANGED
|
@@ -641,7 +641,10 @@ def _is_close(
|
|
|
641
641
|
##
|
|
642
642
|
|
|
643
643
|
|
|
644
|
-
|
|
644
|
+
MAX_DECIMALS = 10
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def number_of_decimals(x: float, /, *, max_decimals: int = MAX_DECIMALS) -> int:
|
|
645
648
|
"""Get the number of decimals."""
|
|
646
649
|
_, frac = divmod(x, 1)
|
|
647
650
|
results = (
|
|
@@ -731,7 +734,7 @@ def round_(
|
|
|
731
734
|
return 0
|
|
732
735
|
case -1:
|
|
733
736
|
return floor(x)
|
|
734
|
-
case
|
|
737
|
+
case never:
|
|
735
738
|
assert_never(never)
|
|
736
739
|
case "standard-tie-floor":
|
|
737
740
|
return _round_tie_standard(x, "floor", rel_tol=rel_tol, abs_tol=abs_tol)
|
|
@@ -743,7 +746,7 @@ def round_(
|
|
|
743
746
|
)
|
|
744
747
|
case "standard-tie-away-zero":
|
|
745
748
|
return _round_tie_standard(x, "away-zero", rel_tol=rel_tol, abs_tol=abs_tol)
|
|
746
|
-
case
|
|
749
|
+
case never:
|
|
747
750
|
assert_never(never)
|
|
748
751
|
|
|
749
752
|
|
|
@@ -876,7 +879,7 @@ def sign(
|
|
|
876
879
|
if is_negative(x, rel_tol=rel_tol, abs_tol=abs_tol):
|
|
877
880
|
return -1
|
|
878
881
|
return 0
|
|
879
|
-
case
|
|
882
|
+
case never:
|
|
880
883
|
assert_never(never)
|
|
881
884
|
|
|
882
885
|
|
|
@@ -889,6 +892,7 @@ def significant_figures(x: float, /, *, n: int = 2) -> str:
|
|
|
889
892
|
|
|
890
893
|
|
|
891
894
|
__all__ = [
|
|
895
|
+
"MAX_DECIMALS",
|
|
892
896
|
"MAX_FLOAT32",
|
|
893
897
|
"MAX_FLOAT64",
|
|
894
898
|
"MAX_INT8",
|
utilities/more_itertools.py
CHANGED
|
@@ -21,7 +21,7 @@ from more_itertools import peekable as _peekable
|
|
|
21
21
|
from utilities.functions import get_class_name
|
|
22
22
|
from utilities.iterables import OneNonUniqueError, one
|
|
23
23
|
from utilities.reprlib import get_repr
|
|
24
|
-
from utilities.sentinel import Sentinel, sentinel
|
|
24
|
+
from utilities.sentinel import Sentinel, is_sentinel, sentinel
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
27
|
from collections.abc import Iterable, Iterator, Mapping, Sequence
|
|
@@ -206,7 +206,7 @@ def bucket_mapping[T, U, UH: Hashable](
|
|
|
206
206
|
return {k: frozenset(map(pre, v)) for k, v in mapping.items()}
|
|
207
207
|
case Callable(), "unique":
|
|
208
208
|
return _bucket_mapping_unique({k: map(pre, v) for k, v in mapping.items()})
|
|
209
|
-
case
|
|
209
|
+
case never:
|
|
210
210
|
assert_never(never)
|
|
211
211
|
|
|
212
212
|
|
|
@@ -290,9 +290,7 @@ class peekable[T](_peekable): # noqa: N801
|
|
|
290
290
|
def peek[U](self, *, default: U) -> T | U: ...
|
|
291
291
|
@override
|
|
292
292
|
def peek(self, *, default: Any = sentinel) -> Any: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
293
|
-
if
|
|
294
|
-
return super().peek()
|
|
295
|
-
return super().peek(default=default)
|
|
293
|
+
return super().peek() if is_sentinel(default) else super().peek(default=default)
|
|
296
294
|
|
|
297
295
|
def takewhile(self, predicate: Callable[[T], bool], /) -> Iterator[T]:
|
|
298
296
|
while bool(self) and predicate(self.peek()):
|
|
@@ -374,7 +372,7 @@ def _yield_splits2[T](
|
|
|
374
372
|
len_tail = max(len_win - head, 0)
|
|
375
373
|
if len_tail >= 1:
|
|
376
374
|
yield window, head, len_tail
|
|
377
|
-
case
|
|
375
|
+
case never:
|
|
378
376
|
assert_never(never)
|
|
379
377
|
|
|
380
378
|
|
utilities/operator.py
CHANGED
|
@@ -6,9 +6,9 @@ from dataclasses import asdict, dataclass
|
|
|
6
6
|
from typing import TYPE_CHECKING, Any, cast, override
|
|
7
7
|
|
|
8
8
|
import utilities.math
|
|
9
|
-
from utilities.functions import is_dataclass_instance
|
|
10
9
|
from utilities.iterables import SortIterableError, sort_iterable
|
|
11
10
|
from utilities.reprlib import get_repr
|
|
11
|
+
from utilities.typing import is_dataclass_instance
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from utilities.types import Dataclass, Number
|