1from asyncutils._internal.compat import Placeholder, partial
2from asyncutils._internal.helpers import LoopMixinBase, subscriptable
3from asyncutils._internal.submodules import properties_all as __all__
4from _collections import deque
5from weakref import WeakKeyDictionary as W
6import asyncutils as A, abc
7n = 'None'
8class D(W):
9 def __init__(self, d, /): super().__init__(); self.c, self.v = bool, d
10 def __set_name__(self, o, n, /, _=frozenset(('__doc__', '__module__', '__name__'))):
11 if n not in _: raise TypeError
12 self.c = o
13 def __get__(self, o, t=None, /):
14 if not (t is None or issubclass(t, self.c)): raise TypeError
15 return self.v if o is None else self.get(o)
16 def __str__(self, _=n, /): return _ if (v := self.v) is None else v
17 def __repr__(self, _=n, /): return _ if (v := self.v) is None else repr(v)
18 __set__, __delete__ = W.__setitem__, W.__delitem__
[docs]
19@subscriptable
20class AsyncPropertyBase(LoopMixinBase, metaclass=type('AsyncPropertyMeta', (abc.ABCMeta,), {'__prepare__': classmethod(lambda c, n, b, _=D, /, **k: (p := c.__base__.__prepare__(n, b, **k)).update(__doc__=_(None), __name__=_(n)) or p), '__new__': lambda m, n, b, d, _=D, /, **k: d.__setitem__('__module__', _('asyncutils.properties')) or m.__base__.__new__(m, n, b, d, **k)})):
21 __slots__ = '__cls', '__deleted', '__hide', '__lock', '__queue', '__strict', '__weakref__', 'fdel', 'fget', 'fset'
22 def __new__(cls, fget=None, *a, **k): # ty: ignore[invalid-assignment]
23 if fget is None: return partial(cls, Placeholder, *a, **k) if a else partial(cls, **k)
24 (_ := object.__new__(cls))._setup(fget, *a, **k); return _
25 _repr_helper = staticmethod(repr)
[docs]
26 def _setup(self, f, /, fset=None, fdel=None, *, _='<unknown>', doc=None, strict=True, hide=False): self.fget, self.fset, self.fdel, self.__doc__, self.__module__, self.__deleted, self.__strict, self.__hide, self.__queue, self.__lock = f, fset, fdel, getattr(f, '__doc__', None) if doc is None else doc, getattr(f, '__module__', _), set(), strict, hide, deque(), self._lock_factory()
[docs]
27 def __get__(self, o, _=None, /):
28 if o is None: self.__check_hidden(); return self
29 self.__check_unbound()
30 if self.fget is None: return self.__get_helper('asyncutils.properties.AsyncPropertyBase: unreadable attribute')
31 if o in self.__deleted: return self.__get_helper('asyncutils.properties.AsyncPropertyBase: cannot get deleted attribute')
32 return self.__get(o)
[docs]
33 def __set__(self, o, v, /):
34 if o is None: return self.__check_hidden()
35 self.__check_unbound()
36 if (f := self.fset) is None: return self.__set_helper('asyncutils.properties.AsyncPropertyBase: immutable attribute', v)
37 if o in self.__deleted: return self.__set_helper('asyncutils.properties.AsyncPropertyBase: cannot set deleted attribute', v)
38 self.__queue.append(self.__helper(f, 'set', o, v))
[docs]
39 def __delete__(self, o, /):
40 if o is None: return self.__check_hidden()
41 self.__check_unbound()
42 if (f := self.fdel) is None:
43 if self.__strict: raise AttributeError('asyncutils.properties.AsyncPropertyBase: undeletable attribute', name=self.__name__)
44 return self.__deleted.add(o)
45 self.__queue.append(self.__helper(f, 'delete', o))
[docs]
46 def __set_name__(self, /, *_): self.__cls, self.__name__ = _
47 def __repr__(self): return f'asyncutils.properties.{type(self).__name__}({", ".join(map(self._repr_helper, (self.fget, self.fset, self.fdel)))}, doc={self.__doc__!r}, strict={self.__strict}, hide={self.__hide})'
[docs]
48 def __reduce__(self):
49 if self.__hide: raise TypeError('asyncutils.properties.AsyncPropertyBase: cannot pickle hidden property')
50 return f'{self.__cls.__name__}.{self.__name__}'
51 def __check_hidden(self):
52 if self.__hide: raise AttributeError(name=self.__name__, obj=self.__cls)
53 def __check_unbound(self):
54 if not hasattr(self, '_AsyncPropertyBase__cls'): raise TypeError(f'{self!r} is not bound to a class')
55 def __get_helper(self, m, /):
56 if self.__strict or self.__hide: raise AttributeError(m, name=self.__name__)
57 return self
[docs]
58 @abc.abstractmethod
59 def _wrap_aw(self, _, /): raise NotImplementedError
60 async def __get(self, o, /):
61 p = (q := self.__queue).popleft
62 async with self.__lock:
63 while q:
64 if await p() is not None and self.__strict: raise TypeError('setter or deleter returned non-None value')
65 return await self.fget(o)
66 def __helper(self, f, c, /, *a):
67 try: return self._wrap_aw(f(*a))
68 except A.CRITICAL: raise A.Critical
69 except BaseException as e: raise AttributeError(f'failed to {c} attribute {self.__name__}') from e
70 def __set_helper(self, m, v, /):
71 if self.__strict: raise AttributeError(m, name=self.__name__)
72 setattr(self.__cls, self.__name__, v)
[docs]
73 def getter(self, f, /): return type(self)(f, self.fset, self.fdel, doc=self.__doc__, strict=self.__strict, hide=self.__hide)
[docs]
74 def setter(self, f, /): return type(self)(self.fget, f, self.fdel, doc=self.__doc__, strict=self.__strict, hide=self.__hide)
[docs]
75 def deleter(self, f, /): return type(self)(self.fget, self.fset, f, doc=self.__doc__, strict=self.__strict, hide=self.__hide)
[docs]
76 def __getattr__(self, n, /):
77 if (f := self.fget) is None: raise AttributeError(name=n, obj=self)
78 return getattr(f, n)
[docs]
79 def __init_subclass__(cls, /, *, lock_factory=None, **_):
80 if not isinstance(cls.__dict__.get('__slots__'), tuple): raise TypeError('subclass of asyncutils.properties.AsyncPropertyBase must define tuple __slots__')
81 if lock_factory is not None: cls._lock_factory = lock_factory
82 elif getattr(cls, '_lock_factory', None) is None: raise TypeError('asyncutils.properties.AsyncPropertyBase subclasses must specify lock_factory')
83 super().__init_subclass__(**_)
[docs]
84class LazyAsyncProperty(AsyncPropertyBase, lock_factory=__import__('asyncio').Lock): __slots__, _wrap_aw = (), staticmethod(A.wrap_in_coro)
[docs]
85class ConcurrentAsyncProperty(AsyncPropertyBase, lock_factory=staticmethod(lambda _=A.anullcontext, /: _)): __slots__, _wrap_aw = (), LoopMixinBase.make
[docs]
86class RWLockedAsyncProperty(ConcurrentAsyncProperty):
87 __slots__, _repr_helper = (), staticmethod(lambda v, _=n, /: _ if v is None else repr(v.__wrapped__))
[docs]
88 def _setup(self, f, /, fset=None, fdel=None, *, policy=A.RWLock, **k): w = (f := policy.lock(f)).writer; super()._setup(f, None if fset is None else w(fset), None if fdel is None else w(fdel), **k)
89del abc, n, W, D