_utilities.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. from __future__ import annotations
  2. import typing as t
  3. from weakref import ref
  4. from blinker._saferef import BoundMethodWeakref
  5. IdentityType = t.Union[t.Tuple[int, int], str, int]
  6. class _symbol:
  7. def __init__(self, name):
  8. """Construct a new named symbol."""
  9. self.__name__ = self.name = name
  10. def __reduce__(self):
  11. return symbol, (self.name,)
  12. def __repr__(self):
  13. return self.name
  14. _symbol.__name__ = "symbol"
  15. class symbol:
  16. """A constant symbol.
  17. >>> symbol('foo') is symbol('foo')
  18. True
  19. >>> symbol('foo')
  20. foo
  21. A slight refinement of the MAGICCOOKIE=object() pattern. The primary
  22. advantage of symbol() is its repr(). They are also singletons.
  23. Repeated calls of symbol('name') will all return the same instance.
  24. """
  25. symbols = {} # type: ignore[var-annotated]
  26. def __new__(cls, name):
  27. try:
  28. return cls.symbols[name]
  29. except KeyError:
  30. return cls.symbols.setdefault(name, _symbol(name))
  31. def hashable_identity(obj: object) -> IdentityType:
  32. if hasattr(obj, "__func__"):
  33. return (id(obj.__func__), id(obj.__self__)) # type: ignore[attr-defined]
  34. elif hasattr(obj, "im_func"):
  35. return (id(obj.im_func), id(obj.im_self)) # type: ignore[attr-defined]
  36. elif isinstance(obj, (int, str)):
  37. return obj
  38. else:
  39. return id(obj)
  40. WeakTypes = (ref, BoundMethodWeakref)
  41. class annotatable_weakref(ref):
  42. """A weakref.ref that supports custom instance attributes."""
  43. receiver_id: t.Optional[IdentityType]
  44. sender_id: t.Optional[IdentityType]
  45. def reference( # type: ignore[no-untyped-def]
  46. object, callback=None, **annotations
  47. ) -> annotatable_weakref:
  48. """Return an annotated weak ref."""
  49. if callable(object):
  50. weak = callable_reference(object, callback)
  51. else:
  52. weak = annotatable_weakref(object, callback)
  53. for key, value in annotations.items():
  54. setattr(weak, key, value)
  55. return weak # type: ignore[no-any-return]
  56. def callable_reference(object, callback=None):
  57. """Return an annotated weak ref, supporting bound instance methods."""
  58. if hasattr(object, "im_self") and object.im_self is not None:
  59. return BoundMethodWeakref(target=object, on_delete=callback)
  60. elif hasattr(object, "__self__") and object.__self__ is not None:
  61. return BoundMethodWeakref(target=object, on_delete=callback)
  62. return annotatable_weakref(object, callback)
  63. class lazy_property:
  64. """A @property that is only evaluated once."""
  65. def __init__(self, deferred):
  66. self._deferred = deferred
  67. self.__doc__ = deferred.__doc__
  68. def __get__(self, obj, cls):
  69. if obj is None:
  70. return self
  71. value = self._deferred(obj)
  72. setattr(obj, self._deferred.__name__, value)
  73. return value