dycw-utilities 0.146.2__py3-none-any.whl → 0.178.1__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.178.1.dist-info/METADATA +34 -0
- dycw_utilities-0.178.1.dist-info/RECORD +105 -0
- dycw_utilities-0.178.1.dist-info/WHEEL +4 -0
- {dycw_utilities-0.146.2.dist-info → dycw_utilities-0.178.1.dist-info}/entry_points.txt +1 -0
- utilities/__init__.py +1 -1
- utilities/altair.py +10 -7
- utilities/asyncio.py +129 -50
- utilities/atomicwrites.py +1 -1
- utilities/atools.py +64 -4
- utilities/cachetools.py +9 -6
- utilities/click.py +144 -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 +387 -0
- utilities/enum.py +2 -2
- utilities/errors.py +17 -3
- 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 +361 -79
- utilities/importlib.py +17 -1
- utilities/inflect.py +1 -1
- utilities/iterables.py +33 -58
- utilities/jinja2.py +148 -0
- utilities/json.py +1 -1
- utilities/libcst.py +7 -7
- utilities/logging.py +131 -93
- 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/packaging.py +115 -0
- utilities/parse.py +2 -2
- utilities/pathlib.py +66 -34
- utilities/permissions.py +298 -0
- utilities/platform.py +5 -4
- utilities/polars.py +934 -420
- utilities/polars_ols.py +1 -1
- utilities/postgres.py +317 -153
- utilities/pottery.py +10 -86
- utilities/pqdm.py +3 -3
- utilities/pwd.py +28 -0
- utilities/pydantic.py +4 -51
- utilities/pydantic_settings.py +240 -0
- utilities/pydantic_settings_sops.py +76 -0
- utilities/pyinstrument.py +5 -5
- utilities/pytest.py +100 -126
- utilities/pytest_plugins/pytest_randomly.py +1 -1
- utilities/pytest_plugins/pytest_regressions.py +7 -3
- utilities/pytest_regressions.py +27 -8
- 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 +9 -4
- utilities/sqlalchemy.py +422 -352
- utilities/sqlalchemy_polars.py +28 -52
- utilities/string.py +1 -1
- utilities/subprocess.py +1977 -0
- utilities/tempfile.py +112 -4
- utilities/testbook.py +50 -0
- utilities/text.py +174 -42
- utilities/throttle.py +158 -0
- utilities/timer.py +2 -2
- utilities/traceback.py +59 -38
- utilities/types.py +68 -22
- utilities/typing.py +479 -19
- utilities/uuid.py +42 -5
- utilities/version.py +27 -26
- utilities/whenever.py +663 -178
- utilities/zoneinfo.py +80 -22
- dycw_utilities-0.146.2.dist-info/METADATA +0 -41
- dycw_utilities-0.146.2.dist-info/RECORD +0 -99
- dycw_utilities-0.146.2.dist-info/WHEEL +0 -4
- dycw_utilities-0.146.2.dist-info/licenses/LICENSE +0 -21
- utilities/aiolimiter.py +0 -25
- utilities/eventkit.py +0 -388
- utilities/period.py +0 -237
- utilities/python_dotenv.py +0 -101
- utilities/streamlit.py +0 -105
- utilities/typed_settings.py +0 -144
utilities/functions.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from collections.abc import Callable, Iterable, Iterator
|
|
4
|
-
from dataclasses import asdict, dataclass
|
|
3
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
4
|
+
from dataclasses import asdict, dataclass
|
|
5
5
|
from functools import _lru_cache_wrapper, cached_property, partial, reduce, wraps
|
|
6
6
|
from inspect import getattr_static
|
|
7
7
|
from pathlib import Path
|
|
@@ -14,33 +14,16 @@ 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
|
|
|
30
21
|
from utilities.reprlib import get_repr, get_repr_and_class
|
|
31
|
-
from utilities.sentinel import Sentinel, sentinel
|
|
32
|
-
from utilities.types import
|
|
33
|
-
Dataclass,
|
|
34
|
-
MaybeCallableBool,
|
|
35
|
-
Number,
|
|
36
|
-
StrMapping,
|
|
37
|
-
SupportsRichComparison,
|
|
38
|
-
TupleOrStrMapping,
|
|
39
|
-
TypeLike,
|
|
40
|
-
)
|
|
22
|
+
from utilities.sentinel import Sentinel, is_sentinel, sentinel
|
|
23
|
+
from utilities.types import Dataclass, Number, SupportsRichComparison, TypeLike
|
|
41
24
|
|
|
42
25
|
if TYPE_CHECKING:
|
|
43
|
-
from collections.abc import Container
|
|
26
|
+
from collections.abc import Container
|
|
44
27
|
|
|
45
28
|
|
|
46
29
|
def apply_decorators[F1: Callable, F2: Callable](
|
|
@@ -240,25 +223,6 @@ class EnsureFloatError(Exception):
|
|
|
240
223
|
##
|
|
241
224
|
|
|
242
225
|
|
|
243
|
-
def ensure_hashable(obj: Any, /) -> Hashable:
|
|
244
|
-
"""Ensure an object is hashable."""
|
|
245
|
-
if is_hashable(obj):
|
|
246
|
-
return obj
|
|
247
|
-
raise EnsureHashableError(obj=obj)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
@dataclass(kw_only=True, slots=True)
|
|
251
|
-
class EnsureHashableError(Exception):
|
|
252
|
-
obj: Any
|
|
253
|
-
|
|
254
|
-
@override
|
|
255
|
-
def __str__(self) -> str:
|
|
256
|
-
return _make_error_msg(self.obj, "hashable")
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
##
|
|
260
|
-
|
|
261
|
-
|
|
262
226
|
@overload
|
|
263
227
|
def ensure_int(obj: Any, /, *, nullable: bool) -> int | None: ...
|
|
264
228
|
@overload
|
|
@@ -415,44 +379,6 @@ class EnsurePlainDateTimeError(Exception):
|
|
|
415
379
|
##
|
|
416
380
|
|
|
417
381
|
|
|
418
|
-
def ensure_sized(obj: Any, /) -> Sized:
|
|
419
|
-
"""Ensure an object is sized."""
|
|
420
|
-
if is_sized(obj):
|
|
421
|
-
return obj
|
|
422
|
-
raise EnsureSizedError(obj=obj)
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
@dataclass(kw_only=True, slots=True)
|
|
426
|
-
class EnsureSizedError(Exception):
|
|
427
|
-
obj: Any
|
|
428
|
-
|
|
429
|
-
@override
|
|
430
|
-
def __str__(self) -> str:
|
|
431
|
-
return _make_error_msg(self.obj, "sized")
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
##
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
def ensure_sized_not_str(obj: Any, /) -> Sized:
|
|
438
|
-
"""Ensure an object is sized, but not a string."""
|
|
439
|
-
if is_sized_not_str(obj):
|
|
440
|
-
return obj
|
|
441
|
-
raise EnsureSizedNotStrError(obj=obj)
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
@dataclass(kw_only=True, slots=True)
|
|
445
|
-
class EnsureSizedNotStrError(Exception):
|
|
446
|
-
obj: Any
|
|
447
|
-
|
|
448
|
-
@override
|
|
449
|
-
def __str__(self) -> str:
|
|
450
|
-
return _make_error_msg(self.obj, "sized and not a string")
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
##
|
|
454
|
-
|
|
455
|
-
|
|
456
382
|
@overload
|
|
457
383
|
def ensure_str(obj: Any, /, *, nullable: bool) -> str | None: ...
|
|
458
384
|
@overload
|
|
@@ -643,67 +569,7 @@ def identity[T](obj: T, /) -> T:
|
|
|
643
569
|
##
|
|
644
570
|
|
|
645
571
|
|
|
646
|
-
def
|
|
647
|
-
"""Check if an object is a dataclass."""
|
|
648
|
-
return isinstance(obj, type) and is_dataclass(obj)
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
##
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
def is_dataclass_instance(obj: Any, /) -> TypeGuard[Dataclass]:
|
|
655
|
-
"""Check if an object is an instance of a dataclass."""
|
|
656
|
-
return (not isinstance(obj, type)) and is_dataclass(obj)
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
##
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
def is_hashable(obj: Any, /) -> TypeGuard[Hashable]:
|
|
663
|
-
"""Check if an object is hashable."""
|
|
664
|
-
try:
|
|
665
|
-
_ = hash(obj)
|
|
666
|
-
except TypeError:
|
|
667
|
-
return False
|
|
668
|
-
return True
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
##
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
@overload
|
|
675
|
-
def is_iterable_of[T](obj: Any, cls: type[T], /) -> TypeGuard[Iterable[T]]: ...
|
|
676
|
-
@overload
|
|
677
|
-
def is_iterable_of[T1](
|
|
678
|
-
obj: Any, cls: tuple[type[T1]], /
|
|
679
|
-
) -> TypeGuard[Iterable[T1]]: ...
|
|
680
|
-
@overload
|
|
681
|
-
def is_iterable_of[T1, T2](
|
|
682
|
-
obj: Any, cls: tuple[type[T1], type[T2]], /
|
|
683
|
-
) -> TypeGuard[Iterable[T1 | T2]]: ...
|
|
684
|
-
@overload
|
|
685
|
-
def is_iterable_of[T1, T2, T3](
|
|
686
|
-
obj: Any, cls: tuple[type[T1], type[T2], type[T3]], /
|
|
687
|
-
) -> TypeGuard[Iterable[T1 | T2 | T3]]: ...
|
|
688
|
-
@overload
|
|
689
|
-
def is_iterable_of[T1, T2, T3, T4](
|
|
690
|
-
obj: Any, cls: tuple[type[T1], type[T2], type[T3], type[T4]], /
|
|
691
|
-
) -> TypeGuard[Iterable[T1 | T2 | T3 | T4]]: ...
|
|
692
|
-
@overload
|
|
693
|
-
def is_iterable_of[T1, T2, T3, T4, T5](
|
|
694
|
-
obj: Any, cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]], /
|
|
695
|
-
) -> TypeGuard[Iterable[T1 | T2 | T3 | T4 | T5]]: ...
|
|
696
|
-
@overload
|
|
697
|
-
def is_iterable_of[T](obj: Any, cls: TypeLike[T], /) -> TypeGuard[Iterable[T]]: ...
|
|
698
|
-
def is_iterable_of[T](obj: Any, cls: TypeLike[T], /) -> TypeGuard[Iterable[T]]:
|
|
699
|
-
"""Check if an object is a iterable of tuple or string mappings."""
|
|
700
|
-
return isinstance(obj, Iterable) and all(map(make_isinstance(cls), obj))
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
##
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
def is_none(obj: Any, /) -> bool:
|
|
572
|
+
def is_none(obj: Any, /) -> TypeGuard[None]:
|
|
707
573
|
"""Check if an object is `None`."""
|
|
708
574
|
return obj is None
|
|
709
575
|
|
|
@@ -719,126 +585,6 @@ def is_not_none(obj: Any, /) -> bool:
|
|
|
719
585
|
##
|
|
720
586
|
|
|
721
587
|
|
|
722
|
-
@overload
|
|
723
|
-
def is_sequence_of[T](obj: Any, cls: type[T], /) -> TypeGuard[Sequence[T]]: ...
|
|
724
|
-
@overload
|
|
725
|
-
def is_sequence_of[T1](
|
|
726
|
-
obj: Any, cls: tuple[type[T1]], /
|
|
727
|
-
) -> TypeGuard[Sequence[T1]]: ...
|
|
728
|
-
@overload
|
|
729
|
-
def is_sequence_of[T1, T2](
|
|
730
|
-
obj: Any, cls: tuple[type[T1], type[T2]], /
|
|
731
|
-
) -> TypeGuard[Sequence[T1 | T2]]: ...
|
|
732
|
-
@overload
|
|
733
|
-
def is_sequence_of[T1, T2, T3](
|
|
734
|
-
obj: Any, cls: tuple[type[T1], type[T2], type[T3]], /
|
|
735
|
-
) -> TypeGuard[Sequence[T1 | T2 | T3]]: ...
|
|
736
|
-
@overload
|
|
737
|
-
def is_sequence_of[T1, T2, T3, T4](
|
|
738
|
-
obj: Any, cls: tuple[type[T1], type[T2], type[T3], type[T4]], /
|
|
739
|
-
) -> TypeGuard[Sequence[T1 | T2 | T3 | T4]]: ...
|
|
740
|
-
@overload
|
|
741
|
-
def is_sequence_of[T1, T2, T3, T4, T5](
|
|
742
|
-
obj: Any, cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]], /
|
|
743
|
-
) -> TypeGuard[Sequence[T1 | T2 | T3 | T4 | T5]]: ...
|
|
744
|
-
@overload
|
|
745
|
-
def is_sequence_of[T](obj: Any, cls: TypeLike[T], /) -> TypeGuard[Sequence[T]]: ...
|
|
746
|
-
def is_sequence_of[T](obj: Any, cls: TypeLike[T], /) -> TypeGuard[Sequence[T]]:
|
|
747
|
-
"""Check if an object is a sequence of tuple or string mappings."""
|
|
748
|
-
return isinstance(obj, Sequence) and is_iterable_of(obj, cls)
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
##
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
def is_sequence_of_tuple_or_str_mapping(
|
|
755
|
-
obj: Any, /
|
|
756
|
-
) -> TypeGuard[Sequence[TupleOrStrMapping]]:
|
|
757
|
-
"""Check if an object is a sequence of tuple or string mappings."""
|
|
758
|
-
return isinstance(obj, Sequence) and all(map(is_tuple_or_str_mapping, obj))
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
##
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
def is_sized(obj: Any, /) -> TypeGuard[Sized]:
|
|
765
|
-
"""Check if an object is sized."""
|
|
766
|
-
try:
|
|
767
|
-
_ = len(obj)
|
|
768
|
-
except TypeError:
|
|
769
|
-
return False
|
|
770
|
-
return True
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
##
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
def is_sized_not_str(obj: Any, /) -> TypeGuard[Sized]:
|
|
777
|
-
"""Check if an object is sized, but not a string."""
|
|
778
|
-
return is_sized(obj) and not isinstance(obj, str)
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
##
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
def is_string_mapping(obj: Any, /) -> TypeGuard[StrMapping]:
|
|
785
|
-
"""Check if an object is a string mapping."""
|
|
786
|
-
return isinstance(obj, dict) and is_iterable_of(obj, str)
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
##
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
def is_tuple(obj: Any, /) -> TypeGuard[tuple[Any, ...]]:
|
|
793
|
-
"""Check if an object is a tuple or string mapping."""
|
|
794
|
-
return make_isinstance(tuple)(obj)
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
##
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
def is_tuple_or_str_mapping(obj: Any, /) -> TypeGuard[TupleOrStrMapping]:
|
|
801
|
-
"""Check if an object is a tuple or string mapping."""
|
|
802
|
-
return is_tuple(obj) or is_string_mapping(obj)
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
##
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
@overload
|
|
809
|
-
def make_isinstance[T](cls: type[T], /) -> Callable[[Any], TypeGuard[T]]: ...
|
|
810
|
-
@overload
|
|
811
|
-
def make_isinstance[T1](cls: tuple[type[T1]], /) -> Callable[[Any], TypeGuard[T1]]: ...
|
|
812
|
-
@overload
|
|
813
|
-
def make_isinstance[T1, T2](
|
|
814
|
-
cls: tuple[type[T1], type[T2]], /
|
|
815
|
-
) -> Callable[[Any], TypeGuard[T1 | T2]]: ...
|
|
816
|
-
@overload
|
|
817
|
-
def make_isinstance[T1, T2, T3](
|
|
818
|
-
cls: tuple[type[T1], type[T2], type[T3]], /
|
|
819
|
-
) -> Callable[[Any], TypeGuard[T1 | T2 | T3]]: ...
|
|
820
|
-
@overload
|
|
821
|
-
def make_isinstance[T1, T2, T3, T4](
|
|
822
|
-
cls: tuple[type[T1], type[T2], type[T3], type[T4]], /
|
|
823
|
-
) -> Callable[[Any], TypeGuard[T1 | T2 | T3 | T4]]: ...
|
|
824
|
-
@overload
|
|
825
|
-
def make_isinstance[T1, T2, T3, T4, T5](
|
|
826
|
-
cls: tuple[type[T1], type[T2], type[T3], type[T4], type[T5]], /
|
|
827
|
-
) -> Callable[[Any], TypeGuard[T1 | T2 | T3 | T4 | T5]]: ...
|
|
828
|
-
@overload
|
|
829
|
-
def make_isinstance[T](cls: TypeLike[T], /) -> Callable[[Any], TypeGuard[T]]: ...
|
|
830
|
-
def make_isinstance[T](cls: TypeLike[T], /) -> Callable[[Any], TypeGuard[T]]:
|
|
831
|
-
"""Make a curried `isinstance` function."""
|
|
832
|
-
return partial(_make_instance_core, cls=cls)
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
def _make_instance_core[T](obj: Any, /, *, cls: TypeLike[T]) -> TypeGuard[T]:
|
|
836
|
-
return isinstance(obj, cls)
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
##
|
|
840
|
-
|
|
841
|
-
|
|
842
588
|
def map_object[T](
|
|
843
589
|
func: Callable[[Any], Any], obj: T, /, *, before: Callable[[Any], Any] | None = None
|
|
844
590
|
) -> T:
|
|
@@ -874,7 +620,7 @@ def min_nullable[T: SupportsRichComparison, U](
|
|
|
874
620
|
) -> T | U:
|
|
875
621
|
"""Compute the minimum of a set of values; ignoring nulls."""
|
|
876
622
|
values = (i for i in iterable if i is not None)
|
|
877
|
-
if
|
|
623
|
+
if is_sentinel(default):
|
|
878
624
|
try:
|
|
879
625
|
return min(values)
|
|
880
626
|
except ValueError:
|
|
@@ -904,7 +650,7 @@ def max_nullable[T: SupportsRichComparison, U](
|
|
|
904
650
|
) -> T | U:
|
|
905
651
|
"""Compute the maximum of a set of values; ignoring nulls."""
|
|
906
652
|
values = (i for i in iterable if i is not None)
|
|
907
|
-
if
|
|
653
|
+
if is_sentinel(default):
|
|
908
654
|
try:
|
|
909
655
|
return max(values)
|
|
910
656
|
except ValueError:
|
|
@@ -945,23 +691,16 @@ def second[U](pair: tuple[Any, U], /) -> U:
|
|
|
945
691
|
##
|
|
946
692
|
|
|
947
693
|
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
def
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
match bool_:
|
|
959
|
-
case bool() | None | Sentinel():
|
|
960
|
-
return bool_
|
|
961
|
-
case Callable() as func:
|
|
962
|
-
return to_bool(bool_=func())
|
|
963
|
-
case _ as never:
|
|
964
|
-
assert_never(never)
|
|
694
|
+
def skip_if_optimize[**P](func: Callable[P, None], /) -> Callable[P, None]:
|
|
695
|
+
"""Skip a function if we are in the optimized mode."""
|
|
696
|
+
if __debug__: # pragma: no cover
|
|
697
|
+
return func
|
|
698
|
+
|
|
699
|
+
@wraps(func)
|
|
700
|
+
def wrapped(*args: P.args, **kwargs: P.kwargs) -> None:
|
|
701
|
+
_ = (args, kwargs)
|
|
702
|
+
|
|
703
|
+
return wrapped
|
|
965
704
|
|
|
966
705
|
|
|
967
706
|
##
|
|
@@ -1017,15 +756,12 @@ __all__ = [
|
|
|
1017
756
|
"EnsureClassError",
|
|
1018
757
|
"EnsureDateError",
|
|
1019
758
|
"EnsureFloatError",
|
|
1020
|
-
"EnsureHashableError",
|
|
1021
759
|
"EnsureIntError",
|
|
1022
760
|
"EnsureMemberError",
|
|
1023
761
|
"EnsureNotNoneError",
|
|
1024
762
|
"EnsureNumberError",
|
|
1025
763
|
"EnsurePathError",
|
|
1026
764
|
"EnsurePlainDateTimeError",
|
|
1027
|
-
"EnsureSizedError",
|
|
1028
|
-
"EnsureSizedNotStrError",
|
|
1029
765
|
"EnsureStrError",
|
|
1030
766
|
"EnsureTimeDeltaError",
|
|
1031
767
|
"EnsureTimeError",
|
|
@@ -1038,15 +774,12 @@ __all__ = [
|
|
|
1038
774
|
"ensure_class",
|
|
1039
775
|
"ensure_date",
|
|
1040
776
|
"ensure_float",
|
|
1041
|
-
"ensure_hashable",
|
|
1042
777
|
"ensure_int",
|
|
1043
778
|
"ensure_member",
|
|
1044
779
|
"ensure_not_none",
|
|
1045
780
|
"ensure_number",
|
|
1046
781
|
"ensure_path",
|
|
1047
782
|
"ensure_plain_date_time",
|
|
1048
|
-
"ensure_sized",
|
|
1049
|
-
"ensure_sized_not_str",
|
|
1050
783
|
"ensure_str",
|
|
1051
784
|
"ensure_time",
|
|
1052
785
|
"ensure_time_delta",
|
|
@@ -1057,24 +790,14 @@ __all__ = [
|
|
|
1057
790
|
"get_func_name",
|
|
1058
791
|
"get_func_qualname",
|
|
1059
792
|
"identity",
|
|
1060
|
-
"is_dataclass_class",
|
|
1061
|
-
"is_dataclass_instance",
|
|
1062
|
-
"is_hashable",
|
|
1063
|
-
"is_iterable_of",
|
|
1064
793
|
"is_none",
|
|
1065
794
|
"is_not_none",
|
|
1066
|
-
"is_sequence_of_tuple_or_str_mapping",
|
|
1067
|
-
"is_sized",
|
|
1068
|
-
"is_sized_not_str",
|
|
1069
|
-
"is_string_mapping",
|
|
1070
|
-
"is_tuple",
|
|
1071
|
-
"is_tuple_or_str_mapping",
|
|
1072
|
-
"make_isinstance",
|
|
1073
795
|
"map_object",
|
|
1074
796
|
"max_nullable",
|
|
1075
797
|
"min_nullable",
|
|
1076
798
|
"not_func",
|
|
1077
799
|
"second",
|
|
800
|
+
"skip_if_optimize",
|
|
1078
801
|
"yield_object_attributes",
|
|
1079
802
|
"yield_object_cached_properties",
|
|
1080
803
|
"yield_object_properties",
|
utilities/git.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from git import Repo
|
|
7
|
+
|
|
8
|
+
from utilities.pathlib import to_path
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from utilities.types import MaybeCallablePathLike
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_repo(path: MaybeCallablePathLike = Path.cwd, /) -> Repo:
|
|
15
|
+
"""Get the repo object."""
|
|
16
|
+
return Repo(to_path(path), search_parent_directories=True)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = ["get_repo"]
|
utilities/grp.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import assert_never
|
|
4
|
+
|
|
5
|
+
from utilities.os import EFFECTIVE_GROUP_ID
|
|
6
|
+
from utilities.platform import SYSTEM
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_gid_name(gid: int, /) -> str | None:
|
|
10
|
+
"""Get the name of a group."""
|
|
11
|
+
match SYSTEM:
|
|
12
|
+
case "windows": # skipif-not-windows
|
|
13
|
+
return None
|
|
14
|
+
case "mac" | "linux":
|
|
15
|
+
from grp import getgrgid
|
|
16
|
+
|
|
17
|
+
return getgrgid(gid).gr_name
|
|
18
|
+
case never:
|
|
19
|
+
assert_never(never)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
ROOT_GROUP_NAME = get_gid_name(0)
|
|
23
|
+
EFFECTIVE_GROUP_NAME = (
|
|
24
|
+
None if EFFECTIVE_GROUP_ID is None else get_gid_name(EFFECTIVE_GROUP_ID)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__all__ = ["EFFECTIVE_GROUP_NAME", "ROOT_GROUP_NAME", "get_gid_name"]
|