jetpytools 1.6.4__py3-none-any.whl → 1.7.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.

Potentially problematic release.


This version of jetpytools might be problematic. Click here for more details.

jetpytools/_metadata.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Collection of stuff that's useful in general python programming"""
2
2
 
3
- __version__ = "1.6.4"
3
+ __version__ = "1.7.0"
4
4
 
5
5
  __author_name__, __author_email__ = "Jaded Encoding Thaumaturgy", "jaded.encoding.thaumaturgy@gmail.com"
6
6
  __maintainer_name__, __maintainer_email__ = __author_name__, __author_email__
@@ -175,7 +175,7 @@ class CustomError(ExceptionError, metaclass=CustomErrorMeta):
175
175
 
176
176
  return out
177
177
 
178
- if sys.version_info < (3, 13):
178
+ if sys.version_info < (3, 11):
179
179
  __notes__: list[str]
180
180
 
181
181
  def add_note(self, note: str, /) -> None:
jetpytools/types/utils.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from functools import partial, wraps
3
+ from contextlib import suppress
4
+ from functools import wraps
4
5
  from inspect import Signature
5
6
  from inspect import _empty as empty_param
6
7
  from typing import (
@@ -21,9 +22,9 @@ from typing import (
21
22
  overload,
22
23
  )
23
24
 
24
- from typing_extensions import Self
25
+ from typing_extensions import Self, deprecated
25
26
 
26
- from .builtins import F0, F1, P0, P1, R0, R1, T0, T1, T2, KwargsT, P, R, R0_co, R1_co, R_co, T, T0_co, T1_co, T_co
27
+ from .builtins import F0, F1, P0, P1, R0, R1, T0, KwargsT, P, R, R0_co, R1_co, R_co, T, T0_co, T1_co, T_co
27
28
 
28
29
  __all__ = [
29
30
  "KwargsNotNone",
@@ -400,17 +401,15 @@ def get_subclasses(family: type[T], exclude: Sequence[type[T]] = []) -> list[typ
400
401
  return list(set(_subclasses(family)))
401
402
 
402
403
 
403
- class classproperty(Generic[T, R]):
404
- """
405
- Make a class property. A combination between classmethod and property.
406
- """
407
-
404
+ class classproperty_base(Generic[T, R_co]):
408
405
  __isabstractmethod__: bool = False
409
406
 
410
407
  def __init__(
411
408
  self,
412
- fget: Callable[[type[T]], R] | classmethod[T, ..., R],
413
- fset: Callable[[type[T], R], None] | classmethod[T, Concatenate[R, ...], None] | None = None,
409
+ fget: Callable[[type[T]], R_co] | classmethod[T, ..., R_co],
410
+ fset: Callable[Concatenate[type[T], Any, ...], None]
411
+ | classmethod[T, Concatenate[Any, ...], None]
412
+ | None = None,
414
413
  fdel: Callable[[type[T]], None] | classmethod[T, ..., None] | None = None,
415
414
  doc: str | None = None,
416
415
  ) -> None:
@@ -419,6 +418,7 @@ class classproperty(Generic[T, R]):
419
418
  self.fdel = self._wrap(fdel) if fdel is not None else fdel
420
419
 
421
420
  self.__doc__ = doc
421
+ self.__name__ = self.fget.__name__
422
422
 
423
423
  def _wrap(self, func: Callable[..., R1] | classmethod[T, P1, R1]) -> classmethod[T, P1, R1]:
424
424
  if not isinstance(func, classmethod):
@@ -426,11 +426,30 @@ class classproperty(Generic[T, R]):
426
426
 
427
427
  return func
428
428
 
429
- def __get__(self, obj: T | None, type_: type | None = None) -> R:
429
+ def __set_name__(self, owner: object, name: str) -> None:
430
+ self.__name__ = name
431
+
432
+ def _get_cache(self, type_: type) -> dict[str, Any]:
433
+ cache_key = getattr(self, "cache_key")
434
+
435
+ if not hasattr(type_, cache_key):
436
+ setattr(type_, cache_key, {})
437
+
438
+ return getattr(type_, cache_key)
439
+
440
+ def __get__(self, obj: T | None, type_: type | None = None) -> R_co:
430
441
  if type_ is None:
431
442
  type_ = type(obj)
432
443
 
433
- return self.fget.__get__(obj, type_)()
444
+ if not isinstance(self, classproperty.cached):
445
+ return self.fget.__get__(obj, type_)()
446
+
447
+ if self.__name__ in (cache := self._get_cache(type_)):
448
+ return cache[self.__name__]
449
+
450
+ value = self.fget.__get__(obj, type_)()
451
+ cache[self.__name__] = value
452
+ return value
434
453
 
435
454
  def __set__(self, obj: T, value: Any) -> None:
436
455
  if not self.fset:
@@ -438,7 +457,12 @@ class classproperty(Generic[T, R]):
438
457
  f'classproperty with getter "{self.__name__}" of "{obj.__class__.__name__}" object has no setter.'
439
458
  )
440
459
 
441
- self.fset.__get__(None, type(obj))(value)
460
+ type_ = type(obj)
461
+
462
+ if self.__name__ in (cache := self._get_cache(type_)):
463
+ del cache[self.__name__]
464
+
465
+ self.fset.__get__(None, type_)(value)
442
466
 
443
467
  def __delete__(self, obj: T) -> None:
444
468
  if not self.fdel:
@@ -446,88 +470,133 @@ class classproperty(Generic[T, R]):
446
470
  f'classproperty with getter "{self.__name__}" of "{obj.__class__.__name__}" object has no deleter.'
447
471
  )
448
472
 
449
- self.fdel.__get__(None, type(obj))()
473
+ type_ = type(obj)
450
474
 
451
- @property
452
- def __name__(self) -> str:
453
- return self.fget.__name__
475
+ if self.__name__ in (cache := self._get_cache(type_)):
476
+ del cache[self.__name__]
477
+
478
+ self.fdel.__get__(None, type_)()
454
479
 
455
480
 
456
- class cachedproperty(property, Generic[P, R_co, T, T0, P0]):
481
+ class classproperty(classproperty_base[T, R_co]):
457
482
  """
458
- Wrapper for a one-time get property, that will be cached.
483
+ A combination of `classmethod` and `property`.
484
+ """
485
+
486
+ class cached(classproperty_base[T0, R0_co]):
487
+ """
488
+ A combination of `classmethod` and `property`.
489
+
490
+ The value is computed once and then cached in a dictionary (under `cache_key`)
491
+ attached to the class type. If a setter or deleter is defined and invoked,
492
+ the cache is cleared.
493
+ """
459
494
 
460
- Keep in mind two things:
495
+ cache_key = "_jetpt_classproperty_cached"
496
+
497
+ @classmethod
498
+ def clear_cache(cls, type_: type, names: str | Iterable[str] | None = None) -> None:
499
+ """
500
+ Clear cached properties of an type instance.
461
501
 
462
- * The cache is per-object. Don't hold a reference to itself or it will never get garbage collected.
463
- * Your class has to either manually set __dict__[cachedproperty.cache_key]
464
- or inherit from cachedproperty.baseclass.
502
+ :param type_: The type whose cache should be cleared.
503
+ :param names: Specific property names to clear. If None, all cached properties are cleared.
504
+ """
505
+ if names is None:
506
+ with suppress(AttributeError):
507
+ getattr(type_, cls.cache_key).clear()
508
+ return None
509
+
510
+ from ..functions import to_arr
511
+
512
+ cache = getattr(type_, cls.cache_key, {})
513
+
514
+ for name in to_arr(names):
515
+ with suppress(KeyError):
516
+ del cache[name]
517
+
518
+
519
+ class cachedproperty(property, Generic[R_co]):
520
+ """
521
+ Wrapper for a one-time get property, that will be cached.
522
+
523
+ You shouldn't hold a reference to itself or it will never get garbage collected.
465
524
  """
466
525
 
467
526
  __isabstractmethod__: bool = False
468
527
 
469
528
  cache_key = "_jetpt_cachedproperty_cache"
470
529
 
530
+ @deprecated(
531
+ "The cache dict is now set automatically. You no longer need to inherit from it", category=DeprecationWarning
532
+ )
471
533
  class baseclass:
472
534
  """Inherit from this class to automatically set the cache dict."""
473
535
 
474
- if not TYPE_CHECKING:
475
-
476
- def __new__(cls, *args: Any, **kwargs: Any) -> None:
477
- try:
478
- self = super().__new__(cls, *args, **kwargs)
479
- except TypeError:
480
- self = super().__new__(cls)
481
- self.__dict__.__setitem__(cachedproperty.cache_key, dict[str, Any]())
482
- return self
483
-
484
536
  if TYPE_CHECKING:
485
537
 
486
538
  def __init__(
487
539
  self,
488
- fget: Callable[P, R_co],
489
- fset: Callable[[T, T0], None] | None = None,
490
- fdel: Callable[P0, None] | None = None,
540
+ fget: Callable[[Any], R_co],
541
+ fset: Callable[[Any, Any], None] | None = None,
542
+ fdel: Callable[[Any], None] | None = None,
491
543
  doc: str | None = None,
492
544
  ) -> None: ...
493
545
 
494
- def getter(self, fget: Callable[P1, R0_co]) -> cachedproperty[P1, R0_co, T, T0, P0]: ...
546
+ def getter(self, fget: Callable[..., R_co]) -> cachedproperty[R_co]: ...
495
547
 
496
- def setter(self, fset: Callable[[T1, T2], None]) -> cachedproperty[P, R_co, T1, T2, P0]: ...
548
+ def setter(self, fset: Callable[[Any, Any], None]) -> cachedproperty[R_co]: ...
497
549
 
498
- def deleter(self, fdel: Callable[P1, None]) -> cachedproperty[P, R_co, T, T0, P1]: ...
550
+ def deleter(self, fdel: Callable[..., None]) -> cachedproperty[R_co]: ...
499
551
 
500
552
  @overload
501
- def __get__(self, obj: None, type_: type | None = None) -> Self: ...
553
+ def __get__(self, instance: None, owner: type | None = None) -> Self: ...
502
554
 
503
555
  @overload
504
- def __get__(self, obj: object, type_: type | None = None) -> R_co: ...
556
+ def __get__(self, instance: Any, owner: type | None = None) -> R_co: ...
505
557
 
506
- def __get__(self, obj: Any, type_: type | None = None) -> Any:
507
- if isinstance(self.fget, classproperty):
508
- function = partial(self.fget.__get__, obj, type_) # type: ignore
509
- obj = type_
558
+ def __get__(self, instance: Any, owner: type | None = None) -> Any:
559
+ if instance is None:
560
+ return self
510
561
 
511
- if not hasattr(obj, cachedproperty.cache_key):
512
- setattr(obj, cachedproperty.cache_key, dict[str, Any]())
562
+ if self.__name__ in (cache := instance.__dict__.setdefault(self.cache_key, {})):
563
+ return cache[self.__name__]
513
564
 
514
- cache = getattr(obj, cachedproperty.cache_key)
515
- name = self.fget.__name__
516
- else:
517
- assert self.fget
518
- function = self.fget.__get__(obj, type_)
519
- cache = obj.__dict__.get(cachedproperty.cache_key)
520
- name = function.__name__
565
+ value = super().__get__(instance, owner)
566
+ cache[self.__name__] = value
567
+ return value
521
568
 
522
- if name not in cache:
523
- cache[name] = function()
569
+ def __set__(self, instance: Any, value: Any) -> None:
570
+ if self.__name__ in (cache := instance.__dict__.setdefault(self.cache_key, {})):
571
+ del cache[self.__name__]
524
572
 
525
- return cache[name]
573
+ return super().__set__(instance, value)
526
574
 
527
- if TYPE_CHECKING:
575
+ def __delete__(self, instance: Any) -> None:
576
+ if self.__name__ in (cache := instance.__dict__.setdefault(self.cache_key, {})):
577
+ del cache[self.__name__]
578
+
579
+ return super().__delete__(instance)
580
+
581
+ @classmethod
582
+ def clear_cache(cls, obj: object, names: str | Iterable[str] | None = None) -> None:
583
+ """
584
+ Clear cached properties of an object instance.
585
+
586
+ :param obj: The object whose cache should be cleared.
587
+ :param names: Specific property names to clear. If None, all cached properties are cleared.
588
+ """
589
+ if names is None:
590
+ obj.__dict__.get(cls.cache_key, {}).clear()
591
+ return None
592
+
593
+ from ..functions import to_arr
594
+
595
+ cache = obj.__dict__.get(cls.cache_key, {})
528
596
 
529
- def __set__(self, obj: Any, value: R_co, /) -> None: # type: ignore[misc]
530
- ...
597
+ for name in to_arr(names):
598
+ with suppress(KeyError):
599
+ del cache[name]
531
600
 
532
601
 
533
602
  class KwargsNotNone(KwargsT):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jetpytools
3
- Version: 1.6.4
3
+ Version: 1.7.0
4
4
  Summary: Collection of stuff that's useful in general python programming
5
5
  Project-URL: Source Code, https://github.com/Jaded-Encoding-Thaumaturgy/jetpytools
6
6
  Project-URL: Contact, https://discord.gg/XTpc6Fa9eB
@@ -1,11 +1,11 @@
1
1
  jetpytools/__init__.py,sha256=ha_pCOMqfeIbipDRrtqKOqH3NQEpX4KwN2SskpsCGb4,114
2
- jetpytools/_metadata.py,sha256=Kz2pkpjUD7YTrr5arStAyNIRsjl0u-lpfclBzyrM6ss,414
2
+ jetpytools/_metadata.py,sha256=3W5CtWRlEtbGoTDF01K0zcMyjXfrii4o8Rdz1tDvRKE,414
3
3
  jetpytools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  jetpytools/enums/__init__.py,sha256=TvEt3TWmnzf2TWS_Gd6llyyXAGDKxdhdJsDgSvT7xys,41
5
5
  jetpytools/enums/base.py,sha256=L5dk6UAsQ3V6GVj0vulKaiJsholnh0Ftyh1FZFM9zwI,2034
6
6
  jetpytools/enums/other.py,sha256=9GGRS4P-P589C1HqNpDDBhLdphID1trwCJi7pbXT0iM,1317
7
7
  jetpytools/exceptions/__init__.py,sha256=0rNEfnOuoSRUyKsutGzHFWQkgiFaK7diOT4VbRVKt9c,105
8
- jetpytools/exceptions/base.py,sha256=FHP1jNG6es0hFqHFGedJUdx8s5DAItdyHgczD8Bi0po,7649
8
+ jetpytools/exceptions/base.py,sha256=cCVT-v-mJoWzLvOxB5SG5dkeGAM_allXAuRgjfI9H6E,7649
9
9
  jetpytools/exceptions/enum.py,sha256=euR6cyMetVWSfTgO5rkblhQyEgusdwhx6Rd9I5ZoFbA,293
10
10
  jetpytools/exceptions/file.py,sha256=7Eh4aPo4r1W-ppnf6XLwWaStOSjIxw9JfzOCa4W7m8E,1105
11
11
  jetpytools/exceptions/generic.py,sha256=5LYQHdSLM2wu5fv40PTrjkW9v0iq8R-QlQzhVsiLLpE,1509
@@ -21,13 +21,13 @@ jetpytools/types/file.py,sha256=LbOwMAwDALWxAZZ2i7ZNexoN3GHWD-uUoIhVvUy95Vo,6539
21
21
  jetpytools/types/funcs.py,sha256=U5tBmTtLS5CLp3ZtOiA5101PQiCWQOBsmf0bj1pRwgY,2778
22
22
  jetpytools/types/generic.py,sha256=SFnUqDAVehKZ36HmLrs9PN4Ze0BLcabPjwC7hJsGp4w,1102
23
23
  jetpytools/types/supports.py,sha256=woMTv62HpcRiC5TG18U8NU5v2Q6iqcrHzQgXl7QYEws,3516
24
- jetpytools/types/utils.py,sha256=eEypP1hh3bubw4J_Rr8InEQnr0XG1myLmXnmwYPLM-c,20410
24
+ jetpytools/types/utils.py,sha256=oISv8o8xD1BS-MjsEDpDBitDQPfkGou1FoyN4EsHYa8,22614
25
25
  jetpytools/utils/__init__.py,sha256=_rJ-mY5PsGjBfy8Fihx_FYJfAIGSrYAYzI6Td9oFh9A,83
26
26
  jetpytools/utils/file.py,sha256=um4ow7_UEH1UxOV74w6F1u4s0wC4zh98LrcwBYjed50,10578
27
27
  jetpytools/utils/funcs.py,sha256=2qhFyLD0OLvenjzOu2wu1ZWoZGzQ8aPmvbkAI1i9Gvk,914
28
28
  jetpytools/utils/math.py,sha256=I56OeHDDJl3X8EFXMWVEiXGAD16AKcn8KVnFuP5fFes,3445
29
29
  jetpytools/utils/ranges.py,sha256=glxypgmuzauV6KCSNujNHOdWkNEUNylOUAPS618jnIg,2559
30
- jetpytools-1.6.4.dist-info/METADATA,sha256=iqiVQW2Y0BuI-LNOfzLNcFW9vtUix24loH4qbvz39Yw,1198
31
- jetpytools-1.6.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
32
- jetpytools-1.6.4.dist-info/licenses/LICENSE,sha256=l0PN-qDtXcgOB5aXP_nSUsvCK5V3o9pQCGsTzyZhKL0,1071
33
- jetpytools-1.6.4.dist-info/RECORD,,
30
+ jetpytools-1.7.0.dist-info/METADATA,sha256=iB6mPlOU_lgtyaIwSz1RM-NSe77g-P9r3FepRPCgrI8,1198
31
+ jetpytools-1.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
32
+ jetpytools-1.7.0.dist-info/licenses/LICENSE,sha256=l0PN-qDtXcgOB5aXP_nSUsvCK5V3o9pQCGsTzyZhKL0,1071
33
+ jetpytools-1.7.0.dist-info/RECORD,,