"""Implements thread-local stack using :class:`ContextVar` (:pep:`567`).
This is a reimplementation of the local stack as used by Flask, Werkzeug,
Celery, and other libraries to keep a thread-local stack of objects.
- Supports typing:
.. sourcecode:: python
request_stack: LocalStack[Request] = LocalStack()
Proxies
=======
Proxy objects are lazy and pass all method calls and attribute accesses
to an underlying object.
There are mixins/roles for many of the generic classes, and
these can be combined to create proxies.
For example to create a proxy to a class that both implements
the mutable mapping interface and is an async context manager:
.. sourcecode:: python
def create_real():
print('CREATING X')
return X()
class XProxy(MutableMappingRole, AsyncContextManagerRole):
...
x = XProxy(create_real)
Evaluation
==========
By default the callable passed to :class:`Proxy` will be evaluated
every time it is needed, so in the example above a new
X will be created every time you access the underlying object:
.. sourcecode:: pycon
>>> x['foo'] = 'value'
CREATING X
>>> x['foo']
CREATING X
'value'
>>> X['foo']
CREATING X
'value'
>>> # evaluates twice, once for async with then for __getitem__:
>>> async with x:
... x['foo']
CREATING X
CREATING X
'value'
If you want the creation of the object to be lazy (created
when first needed), you can pass the `cache=True` argument to :class:`Proxy`:
.. sourcecode:: pycon
>>> x = XProxy(create_real, cache=True)
>>> # Now only evaluates the first time it is needed.
>>> x['foo'] = 'value'
CREATING X
>>> x['foo']
'value'
>>> X['foo']
'value'
>>> async with x:
... x['foo']
'value'
"""
import sys
import typing
from collections import deque
from contextlib import contextmanager
from contextvars import ContextVar
from functools import wraps
from types import GetSetDescriptorType, TracebackType
from typing import (
AbstractSet,
Any,
AsyncIterable,
AsyncIterator,
Awaitable,
Callable,
ContextManager,
Coroutine,
Dict,
Generator,
Generic,
Iterable,
Iterator,
List,
Mapping,
MutableMapping,
MutableSequence,
MutableSet,
Optional,
Sequence,
Tuple,
Type,
TypeVar,
Union,
ValuesView,
cast,
no_type_check,
overload,
)
if typing.TYPE_CHECKING: # pragma: no cover
from typing import AsyncContextManager, AsyncGenerator
else:
from .utils.typing import AsyncContextManager, AsyncGenerator # noqa
__all__ = [
'LocalStack',
'Proxy',
'AwaitableRole',
'AwaitableProxy',
'CoroutineRole',
'CoroutineProxy',
'AsyncIterableRole',
'AsyncIterableProxy',
'AsyncIteratorRole',
'AsyncIteratorProxy',
'AsyncGeneratorRole',
'AsyncGeneratorProxy',
'SequenceRole',
'SequenceProxy',
'MutableSequenceRole',
'MutableSequenceProxy',
'SetRole',
'SetProxy',
'MutableSetRole',
'MutableSetProxy',
'ContextManagerRole',
'ContextManagerProxy',
'AsyncContextManagerRole',
'AsyncContextManagerProxy',
'MappingRole',
'MappingProxy',
'MutableMappingRole',
'MutableMappingProxy',
'CallableRole',
'CallableProxy',
'maybe_evaluate',
]
PYPY = hasattr(sys, 'pypy_version_info')
SLOTS_ISSUE_PRESENT = sys.version_info < (3, 7)
# LocalStack is a generic type,
# so for a stack keeping track of web requests you may define:
#
# requests: LocalStack[Request]
#
# If the stack is a ``List[T]``, then the type variable T denotes the
# type this stack contains.
T = TypeVar('T')
S = TypeVar('S')
T_co = TypeVar('T_co', covariant=True)
V_co = TypeVar('V_co', covariant=True)
VT_co = TypeVar('VT_co', covariant=True)
T_contra = TypeVar('T_contra', contravariant=True)
KT = TypeVar('KT')
VT = TypeVar('VT')
[docs]class LocalStack(Generic[T]):
"""LocalStack.
Manage state per coroutine (also thread safe).
Most famously used probably in Flask to keep track of the current
request object.
"""
_stack: ContextVar[Optional[List[T]]]
def __init__(self) -> None:
self._stack = ContextVar('_stack')
# XXX mypy bug; when fixed type Generator, should be ContextManager.
[docs] @contextmanager
def push(self, obj: T) -> Generator[None, None, None]:
"""Push a new item to the stack."""
self.push_without_automatic_cleanup(obj)
try:
yield
finally:
self.pop()
[docs] def push_without_automatic_cleanup(self, obj: T) -> None:
stack = self._stack.get(None)
if stack is None:
stack = []
self._stack.set(stack)
stack.append(obj)
[docs] def pop(self) -> Optional[T]:
"""Remove the topmost item from the stack.
Note:
Will return the old value or `None` if the stack was already empty.
"""
stack = self._stack.get(None)
if stack is None:
return None
else:
size = len(stack)
if not size:
self._stack.set(None)
return None
elif size == 1:
item = stack[-1]
self._stack.set(None)
else:
item = stack.pop()
return item
def __len__(self) -> int:
stack = self._stack.get(None)
return len(stack) if stack else 0
@property
def stack(self) -> Sequence[T]:
# read-only version, do not modify
return self._stack.get(None) or []
@property
def top(self) -> Optional[T]:
"""Return the topmost item on the stack.
Does not remove it from the stack.
Note:
If the stack is empty, :const:`None` is returned.
"""
stack = self._stack.get(None)
return stack[-1] if stack else None
def _default_cls_attr(
name: str,
type_: Type,
cls_value: Any) -> Callable[[Type], GetSetDescriptorType]:
# Proxy uses properties to forward the standard
# class attributes __module__, __name__ and __doc__ to the real
# object, but these needs to be a string when accessed from
# the Proxy class directly. This is a hack to make that work.
# -- See Issue #1087.
def __new__(cls: Type, getter: Callable) -> Any:
instance = type_.__new__(cls, cls_value)
instance.__getter = getter # type: ignore
return instance
def __get__(self: Type, obj: Any, cls: Type = None) -> Any:
return self.__getter(obj) if obj is not None else self
return type(name, (type_,), {
'__new__': __new__,
'__get__': __get__,
})
[docs]class Proxy(Generic[T]):
"""Proxy to another object."""
# Code initially stolen from werkzeug.local.Proxy.
if not SLOTS_ISSUE_PRESENT and not PYPY:
__slots__ = (
'__local',
'__args',
'__kwargs',
'__finalizers',
'__dict__',
)
def __init_subclass__(self, source: Type[T] = None) -> None:
super().__init_subclass__()
if source is not None:
self._init_from_source(source)
@classmethod
def _init_from_source(cls, source: Type[T]) -> None:
# source must have metaclass ABCMeta
abstractmethods = getattr(source, '__abstractmethods__', None)
if abstractmethods is None:
raise TypeError('class is not using metaclass ABCMeta')
for method_name in abstractmethods:
setattr(cls, method_name,
cls._generate_proxy_method(source, method_name))
@classmethod
def _generate_proxy_method(
cls, source: Type[T], method_name: str) -> Callable:
@wraps(getattr(source, method_name))
def _classmethod(self: Proxy[T], *args: Any, **kwargs: Any) -> Any:
obj = self._get_current_object()
return getattr(obj, method_name)(*args, **kwargs)
_classmethod.__isabstractmethod__ = False # type: ignore
return _classmethod
def __init__(self,
local: Callable[..., T],
args: Tuple = None,
kwargs: Dict = None,
name: str = None,
cache: bool = False,
__doc__: str = None) -> None:
object.__setattr__(self, '_Proxy__local', local)
object.__setattr__(self, '_Proxy__args', args or ())
object.__setattr__(self, '_Proxy__kwargs', kwargs or {})
object.__setattr__(self, '_Proxy__cached', cache)
object.__setattr__(self, '_Proxy__finalizers', deque())
if name is not None:
object.__setattr__(self, '__custom_name__', name)
if __doc__ is not None:
object.__setattr__(self, '__doc__', __doc__)
def _add_proxy_finalizer(self, fun: 'Proxy') -> None:
finalizers = object.__getattribute__(self, '_Proxy__finalizers')
finalizers.append(fun)
def _call_proxy_finalizers(self) -> None:
finalizers = object.__getattribute__(self, '_Proxy__finalizers')
while finalizers:
finalizer = finalizers.popleft()
finalizer._get_current_object() # evaluate
@_default_cls_attr('name', str, __name__)
@no_type_check
def __name__(self) -> str:
try:
return self.__custom_name__
except AttributeError:
return self._get_current_object().__name__
@_default_cls_attr('module', str, __name__)
@no_type_check
def __module__(self) -> str:
return self._get_current_object().__module__
@_default_cls_attr('doc', str, __doc__)
@no_type_check
def __doc__(self) -> Optional[str]:
return cast(str, self._get_current_object().__doc__)
def _get_class(self) -> Type[T]:
return self._get_current_object().__class__
@property
def __class__(self) -> Any:
return self._get_class()
@__class__.setter # noqa: F811
def __class__(self, t: Type[T]) -> None: # pragma: no cover
raise NotImplementedError()
def _get_current_object(self) -> T:
"""Get current object.
This is useful if you want the real
object behind the proxy at a time for performance reasons or because
you want to pass the object into a different context.
"""
try:
return cast(T, object.__getattribute__(self, '__cache'))
except AttributeError:
return self.__evaluate__()
def __evaluate__(self,
_clean: Tuple[str, ...] = ('_Proxy__local',
'_Proxy__args',
'_Proxy__kwargs')) -> T:
thing = self._evaluate_proxy()
cached = object.__getattribute__(self, '_Proxy__cached')
if cached:
object.__setattr__(self, '__cache', thing)
for attr in _clean:
try:
object.__delattr__(self, attr)
except AttributeError: # pragma: no cover
# May mask errors so ignore
pass
return thing
def _evaluate_proxy(self) -> T:
self._call_proxy_finalizers()
loc = object.__getattribute__(self, '_Proxy__local')
if not hasattr(loc, '__release_local__'):
return cast(T, loc(*self.__args, **self.__kwargs))
try: # pragma: no cover
# not sure what this is about
return cast(T, getattr(loc, self.__name__))
except AttributeError: # pragma: no cover
raise RuntimeError('no object bound to {0.__name__}'.format(self))
def __evaluated__(self) -> bool:
try:
object.__getattribute__(self, '__cache')
except AttributeError:
return False
return True
def __maybe_evaluate__(self) -> T:
return self._get_current_object()
@property
def __dict__(self) -> Dict[str, Any]: # type: ignore
try:
return self._get_current_object().__dict__
except RuntimeError: # pragma: no cover
raise AttributeError('__dict__')
def __repr__(self) -> str:
try:
obj = self._get_current_object()
except RuntimeError: # pragma: no cover
return '<{0} unbound>'.format(self.__class__.__name__)
return repr(obj)
def __bool__(self) -> bool:
try:
return bool(self._get_current_object())
except RuntimeError: # pragma: no cover
return False
__nonzero__ = __bool__ # Py2
def __dir__(self) -> List[str]:
try:
return dir(self._get_current_object())
except RuntimeError: # pragma: no cover
return []
def __getattr__(self, name: str) -> Any:
if name == '__members__':
return dir(self._get_current_object())
return getattr(self._get_current_object(), name)
def __eq__(self, other: Any) -> Any:
return self._get_current_object() == other
def __ne__(self, other: Any) -> Any:
return self._get_current_object() != other
def __setattr__(self, name: str, value: Any) -> None:
setattr(self._get_current_object(), name, value)
def __delattr__(self, name: str) -> None:
delattr(self._get_current_object(), name)
def __str__(self) -> str:
return str(self._get_current_object())
def __hash__(self) -> int:
return hash(self._get_current_object())
def __reduce__(self) -> Tuple:
return self._get_current_object().__reduce__()
[docs]class AwaitableRole(Awaitable[T]):
"""Role/Mixin for :class:`typing.Awaitable` proxy methods."""
def _get_awaitable(self) -> Awaitable[T]:
obj = self._get_current_object() # type: ignore
return cast(Awaitable[T], obj)
def __await__(self) -> Generator[Any, None, T]:
return self._get_awaitable().__await__()
[docs]class AwaitableProxy(Proxy[T], AwaitableRole[T]):
"""Proxy to :class:`typing.Awaitable` object."""
[docs]class CoroutineRole(Coroutine[T_co, T_contra, V_co]):
"""Role/Mixin for :class:`typing.Coroutine` proxy methods."""
def _get_coroutine(self) -> Coroutine[T_co, T_contra, V_co]:
obj = self._get_current_object() # type: ignore
return cast(Coroutine[T_co, T_contra, V_co], obj)
def __await__(self) -> Generator[Any, None, V_co]:
return self._get_coroutine().__await__()
[docs] def send(self, value: T_contra) -> T_co:
return self._get_coroutine().send(value)
[docs] def throw(self,
typ: Type[BaseException],
val: Optional[BaseException] = None,
tb: TracebackType = None) -> T_co:
return self._get_coroutine().throw(typ, val, tb)
[docs] def close(self) -> None:
return self._get_coroutine().close()
[docs]class CoroutineProxy(Proxy[Coroutine[T_co, T_contra, V_co]],
CoroutineRole[T_co, T_contra, V_co]):
"""Proxy to :class:`typing.Coroutine` object."""
[docs]class AsyncIterableRole(AsyncIterable[T_co]):
"""Role/Mixin for :class:`typing.AsyncIterable` proxy methods."""
def _get_iterable(self) -> AsyncIterable[T_co]:
obj = self._get_current_object() # type: ignore
return cast(AsyncIterable[T_co], obj)
def __aiter__(self) -> AsyncIterator[T_co]:
return self._get_iterable().__aiter__()
[docs]class AsyncIterableProxy(Proxy[AsyncIterable[T_co]],
AsyncIterableRole[T_co]):
"""Proxy to :class:`typing.AsyncIterable` object."""
[docs]class AsyncIteratorRole(AsyncIterator[T_co]):
"""Role/Mixin for :class:`typing.AsyncIterator` proxy methods."""
def _get_iterator(self) -> AsyncIterator[T_co]:
obj = self._get_current_object() # type: ignore
return cast(AsyncIterator[T_co], obj)
def __aiter__(self) -> AsyncIterator[T_co]:
return self._get_iterator().__aiter__()
def __anext__(self) -> Awaitable[T_co]:
return self._get_iterator().__anext__()
[docs]class AsyncIteratorProxy(Proxy[AsyncIterator[T_co]],
AsyncIteratorRole[T_co]):
"""Proxy to :class:`typing.AsyncIterator` object."""
[docs]class AsyncGeneratorRole(AsyncGenerator[T_co, T_contra]):
"""Role/Mixin for :class:`typing.AsyncGenerator` proxy methods."""
def _get_generator(self) -> AsyncGenerator[T_co, T_contra]:
obj = self._get_current_object() # type: ignore
return cast(AsyncGenerator[T_co, T_contra], obj)
def __anext__(self) -> Awaitable[T_co]:
return self._get_generator().__anext__()
[docs] def asend(self, value: T_contra) -> Awaitable[T_co]:
return self._get_generator().asend(value)
[docs] def athrow(self,
typ: Type[BaseException],
val: Optional[BaseException] = None,
tb: TracebackType = None) -> Awaitable[T_co]:
return self._get_generator().athrow(typ, val, tb)
[docs] def aclose(self) -> Awaitable[None]:
return self._get_generator().aclose()
def __aiter__(self) -> AsyncGenerator[T_co, T_contra]:
return self._get_generator().__aiter__()
[docs]class AsyncGeneratorProxy(Proxy[AsyncGenerator[T_co, T_contra]],
AsyncGeneratorRole[T_co, T_contra]):
"""Proxy to :class:`typing.AsyncGenerator` object."""
[docs]class SequenceRole(Sequence[T_co]):
"""Role/Mixin for :class:`typing.Sequence` proxy methods."""
def _get_sequence(self) -> Sequence[T_co]:
obj = self._get_current_object() # type: ignore
return cast(Sequence[T_co], obj)
@overload
def __getitem__(self, i: int) -> T_co:
...
@overload # noqa: F811
def __getitem__(self, s: slice) -> MutableSequence[T_co]:
...
def __getitem__(self, s: Any) -> Any: # noqa: F811
return self._get_sequence().__getitem__(s)
[docs] def index(self, x: Any, *args: Any, **kwargs: Any) -> int:
return self._get_sequence().index(x, *args, **kwargs)
[docs] def count(self, x: Any) -> int:
return self._get_sequence().count(x)
def __contains__(self, x: Any) -> bool:
return self._get_sequence().__contains__(x)
def __iter__(self) -> Iterator[T_co]:
return self._get_sequence().__iter__()
def __reversed__(self) -> Iterator[T_co]:
return self._get_sequence().__reversed__()
def __len__(self) -> int:
return self._get_sequence().__len__()
[docs]class SequenceProxy(Proxy[Sequence[T_co]],
SequenceRole[T_co]):
"""Proxy to :class:`typing.Sequence` object."""
[docs]class MutableSequenceRole(SequenceRole[T], MutableSequence[T]):
"""Role/Mixin for :class:`typing.MutableSequence` proxy methods."""
def _get_sequence(self) -> MutableSequence[T]:
obj = self._get_current_object() # type: ignore
return cast(MutableSequence[T], obj)
[docs] def insert(self, index: int, object: T) -> None:
self._get_sequence().insert(index, object)
@overload
def __setitem__(self, i: int, o: T) -> None:
...
@overload # noqa: F811
def __setitem__(self, s: slice, o: Iterable[T]) -> None:
...
def __setitem__(self, index_or_slice: Any, o: Any) -> None: # noqa: F811
self._get_sequence().__setitem__(index_or_slice, o)
@overload
def __delitem__(self, i: int) -> None:
...
@overload # noqa: F811
def __delitem__(self, i: slice) -> None:
...
def __delitem__(self, i: Any) -> None: # noqa: F811
self._get_sequence().__delitem__(i)
[docs] def append(self, obj: T) -> None:
self._get_sequence().append(obj)
[docs] def extend(self, iterable: Iterable[T]) -> None:
self._get_sequence().extend(iterable)
[docs] def reverse(self) -> None:
self._get_sequence().reverse()
[docs] def pop(self, *args: Any) -> Any:
return self._get_sequence().pop(*args)
[docs] def remove(self, object: T) -> None:
self._get_sequence().remove(object)
def __iadd__(self, x: Iterable[T]) -> MutableSequence[T]:
return self._get_sequence().__iadd__(x)
[docs]class MutableSequenceProxy(Proxy[MutableSequence[T_co]],
MutableSequenceRole[T_co]):
"""Proxy to :class:`typing.MutableSequence` object."""
[docs]class SetRole(AbstractSet[T_co]):
"""Role/Mixin for :class:`typing.AbstractSet` proxy methods."""
def _get_set(self) -> AbstractSet[T_co]:
obj = self._get_current_object() # type: ignore
return cast(AbstractSet[T_co], obj)
def __le__(self, s: AbstractSet[Any]) -> bool:
return self._get_set().__le__(s)
def __lt__(self, s: AbstractSet[Any]) -> bool:
return self._get_set().__lt__(s)
def __gt__(self, s: AbstractSet[Any]) -> bool:
return self._get_set().__gt__(s)
def __ge__(self, s: AbstractSet[Any]) -> bool:
return self._get_set().__ge__(s)
def __and__(self, s: AbstractSet[Any]) -> AbstractSet[T_co]:
return self._get_set().__and__(s)
def __or__(self, s: AbstractSet[T]) -> AbstractSet[Union[T_co, T]]:
return self._get_set().__or__(s)
def __sub__(self, s: AbstractSet[Any]) -> AbstractSet[T_co]:
return self._get_set().__sub__(s)
def __xor__(self, s: AbstractSet[T]) -> AbstractSet[Union[T_co, T]]:
return self._get_set().__xor__(s)
[docs] def isdisjoint(self, s: Iterable[Any]) -> bool:
return self._get_set().isdisjoint(s)
def __contains__(self, x: Any) -> bool:
return self._get_set().__contains__(x)
def __iter__(self) -> Iterator[T_co]:
return self._get_set().__iter__()
def __len__(self) -> int:
return self._get_set().__len__()
[docs]class SetProxy(Proxy[AbstractSet[T_co]],
SetRole[T_co]):
"""Proxy to :class:`typing.AbstractSet` object."""
[docs]class MutableSetRole(SetRole[T], MutableSet[T]):
"""Role/Mixin for :class:`typing.MutableSet` proxy methods."""
def _get_set(self) -> MutableSet[T]:
obj = self._get_current_object() # type: ignore
return cast(MutableSet[T], obj)
[docs] def add(self, x: T) -> None:
self._get_set().add(x)
[docs] def discard(self, x: T) -> None:
self._get_set().discard(x)
[docs] def clear(self) -> None:
self._get_set().clear()
[docs] def pop(self) -> T:
return self._get_set().pop()
[docs] def remove(self, element: T) -> None:
self._get_set().remove(element)
def __ior__(self, s: AbstractSet[S]) -> MutableSet[Union[T, S]]:
return self._get_set().__ior__(s)
def __iand__(self, s: AbstractSet[Any]) -> MutableSet[T]:
return self._get_set().__iand__(s)
def __ixor__(self, s: AbstractSet[S]) -> MutableSet[Union[T, S]]:
return self._get_set().__ixor__(s)
def __isub__(self, s: AbstractSet[Any]) -> MutableSet[T]:
return self._get_set().__isub__(s)
[docs]class MutableSetProxy(Proxy[MutableSet[T_co]],
MutableSetRole[T_co]):
"""Proxy to :class:`typing.MutableSet` object."""
[docs]class ContextManagerRole(ContextManager[T]):
"""Role/Mixin for :class:`typing.ContextManager` proxy methods."""
def _get_context(self) -> ContextManager[T]:
obj = self._get_current_object() # type: ignore
return cast(ContextManager[T], obj)
def __enter__(self) -> Any:
return self._get_context().__enter__()
def __exit__(self, *exc_info: Any) -> Any:
return self._get_context().__exit__(*exc_info)
[docs]class ContextManagerProxy(Proxy[ContextManager[T]],
ContextManagerRole[T]):
"""Proxy to :class:`typing.ContextManager` object."""
[docs]class AsyncContextManagerRole(AsyncContextManager[T_co]):
"""Role/Mixin for :class:`typing.AsyncContextManager` proxy methods."""
def __aenter__(self) -> Awaitable[T_co]:
obj = self._get_current_object() # type: ignore
return cast(Awaitable[T_co], obj.__aenter__())
def __aexit__(
self,
exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType]) -> Awaitable[Optional[bool]]:
obj = self._get_current_object() # type: ignore
val = obj.__aexit__(exc_type, exc_value, traceback)
return cast(Awaitable[Optional[bool]], val)
[docs]class AsyncContextManagerProxy(Proxy[AsyncContextManager[T_co]],
AsyncContextManagerRole[T_co]):
"""Proxy to :class:`typing.AsyncContextManager` object."""
[docs]class MappingRole(Mapping[KT, VT_co]):
"""Role/Mixin for :class:`typing.Mapping` proxy methods."""
def _get_mapping(self) -> Mapping[KT, VT_co]:
obj = self._get_current_object() # type: ignore
return cast(Mapping[KT, VT_co], obj)
def __getitem__(self, key: KT) -> VT_co:
return self._get_mapping().__getitem__(key)
@overload
def get(self, k: KT) -> Optional[VT_co]:
...
@overload # noqa: F811
def get(self, k: KT,
default: Union[VT_co, T]) -> Union[VT_co, T]:
...
[docs] def get(self, *args: Any, **kwargs: Any) -> Any: # noqa: F811
return self._get_mapping().get(*args, **kwargs)
[docs] def items(self) -> AbstractSet[Tuple[KT, VT_co]]:
return self._get_mapping().items()
[docs] def keys(self) -> AbstractSet[KT]:
return self._get_mapping().keys()
[docs] def values(self) -> ValuesView[VT_co]:
return self._get_mapping().values()
def __contains__(self, o: Any) -> bool:
return self._get_mapping().__contains__(o)
def __iter__(self) -> Iterator[KT]:
return self._get_mapping().__iter__()
def __len__(self) -> int:
return self._get_mapping().__len__()
[docs]class MappingProxy(Proxy[Mapping[KT, VT_co]],
MappingRole[KT, VT_co]):
"""Proxy to :class:`typing.Mapping` object."""
[docs]class MutableMappingRole(MappingRole[KT, VT], MutableMapping[KT, VT]):
"""Role/Mixin for :class:`typing.MutableMapping` proxy methods."""
def _get_mapping(self) -> MutableMapping[KT, VT]:
obj = self._get_current_object() # type: ignore
return cast(MutableMapping[KT, VT], obj)
def __setitem__(self, key: KT, value: VT) -> None:
self._get_mapping().__setitem__(key, value)
def __delitem__(self, key: KT) -> None:
self._get_mapping().__delitem__(key)
[docs] def clear(self) -> None:
self._get_mapping().clear()
@overload
def pop(self, k: KT) -> VT:
...
@overload # noqa: F811
def pop(self, k: KT, default: Union[VT, T] = ...) -> Union[VT, T]:
...
[docs] def pop(self, *args: Any, **kwargs: Any) -> Any: # noqa: F811
return self._get_mapping().pop(*args, **kwargs)
[docs] def popitem(self) -> Tuple[KT, VT]:
return self._get_mapping().popitem()
[docs] def setdefault(self, k: KT, *args: Any) -> VT:
return self._get_mapping().setdefault(k, *args)
@overload
def update(self, __m: Mapping[KT, VT], **kwargs: VT) -> None:
...
@overload # noqa: F811
def update(self, __m: Iterable[Tuple[KT, VT]], **kwargs: VT) -> None:
...
@overload # noqa: F811
def update(self, **kwargs: VT) -> None:
...
[docs] def update(self, *args: Any, **kwargs: Any) -> None: # noqa: F811
self._get_mapping().update(*args, **kwargs)
[docs]class MutableMappingProxy(Proxy[MutableMapping[KT, VT]],
MutableMappingRole[KT, VT]):
"""Proxy to :class:`typing.MutableMapping` object."""
[docs]class CallableRole:
"""Role/Mixin for :class:`typing.Callable` proxy methods."""
def _get_callable(self) -> Callable:
obj = self._get_current_object() # type: ignore
return cast(Callable, obj)
def __call__(self, *args: Any, **kwargs: Any) -> Any:
return self._get_callable()(*args, **kwargs)
[docs]class CallableProxy(Proxy[Callable], CallableRole):
"""Proxy to :class:`typing.Callable` object."""
[docs]def maybe_evaluate(obj: Any) -> Any:
"""Attempt to evaluate promise, even if obj is not a promise."""
try:
return obj.__maybe_evaluate__()
except AttributeError:
return obj