tests versuch 2
This commit is contained in:
parent
fdf385fe06
commit
c88f7df83a
2363 changed files with 408191 additions and 0 deletions
19
venv/lib/python3.11/site-packages/blinker/__init__.py
Normal file
19
venv/lib/python3.11/site-packages/blinker/__init__.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
from blinker.base import ANY
|
||||
from blinker.base import NamedSignal
|
||||
from blinker.base import Namespace
|
||||
from blinker.base import receiver_connected
|
||||
from blinker.base import Signal
|
||||
from blinker.base import signal
|
||||
from blinker.base import WeakNamespace
|
||||
|
||||
__all__ = [
|
||||
"ANY",
|
||||
"NamedSignal",
|
||||
"Namespace",
|
||||
"Signal",
|
||||
"WeakNamespace",
|
||||
"receiver_connected",
|
||||
"signal",
|
||||
]
|
||||
|
||||
__version__ = "1.6.2"
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
230
venv/lib/python3.11/site-packages/blinker/_saferef.py
Normal file
230
venv/lib/python3.11/site-packages/blinker/_saferef.py
Normal file
|
@ -0,0 +1,230 @@
|
|||
# extracted from Louie, http://pylouie.org/
|
||||
# updated for Python 3
|
||||
#
|
||||
# Copyright (c) 2006 Patrick K. O'Brien, Mike C. Fletcher,
|
||||
# Matthew R. Scott
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# * Neither the name of the <ORGANIZATION> nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
"""Refactored 'safe reference from dispatcher.py"""
|
||||
import operator
|
||||
import sys
|
||||
import traceback
|
||||
import weakref
|
||||
|
||||
|
||||
get_self = operator.attrgetter("__self__")
|
||||
get_func = operator.attrgetter("__func__")
|
||||
|
||||
|
||||
def safe_ref(target, on_delete=None):
|
||||
"""Return a *safe* weak reference to a callable target.
|
||||
|
||||
- ``target``: The object to be weakly referenced, if it's a bound
|
||||
method reference, will create a BoundMethodWeakref, otherwise
|
||||
creates a simple weakref.
|
||||
|
||||
- ``on_delete``: If provided, will have a hard reference stored to
|
||||
the callable to be called after the safe reference goes out of
|
||||
scope with the reference object, (either a weakref or a
|
||||
BoundMethodWeakref) as argument.
|
||||
"""
|
||||
try:
|
||||
im_self = get_self(target)
|
||||
except AttributeError:
|
||||
if callable(on_delete):
|
||||
return weakref.ref(target, on_delete)
|
||||
else:
|
||||
return weakref.ref(target)
|
||||
else:
|
||||
if im_self is not None:
|
||||
# Turn a bound method into a BoundMethodWeakref instance.
|
||||
# Keep track of these instances for lookup by disconnect().
|
||||
assert hasattr(target, "im_func") or hasattr(target, "__func__"), (
|
||||
f"safe_ref target {target!r} has im_self, but no im_func, "
|
||||
"don't know how to create reference"
|
||||
)
|
||||
reference = BoundMethodWeakref(target=target, on_delete=on_delete)
|
||||
return reference
|
||||
|
||||
|
||||
class BoundMethodWeakref:
|
||||
"""'Safe' and reusable weak references to instance methods.
|
||||
|
||||
BoundMethodWeakref objects provide a mechanism for referencing a
|
||||
bound method without requiring that the method object itself
|
||||
(which is normally a transient object) is kept alive. Instead,
|
||||
the BoundMethodWeakref object keeps weak references to both the
|
||||
object and the function which together define the instance method.
|
||||
|
||||
Attributes:
|
||||
|
||||
- ``key``: The identity key for the reference, calculated by the
|
||||
class's calculate_key method applied to the target instance method.
|
||||
|
||||
- ``deletion_methods``: Sequence of callable objects taking single
|
||||
argument, a reference to this object which will be called when
|
||||
*either* the target object or target function is garbage
|
||||
collected (i.e. when this object becomes invalid). These are
|
||||
specified as the on_delete parameters of safe_ref calls.
|
||||
|
||||
- ``weak_self``: Weak reference to the target object.
|
||||
|
||||
- ``weak_func``: Weak reference to the target function.
|
||||
|
||||
Class Attributes:
|
||||
|
||||
- ``_all_instances``: Class attribute pointing to all live
|
||||
BoundMethodWeakref objects indexed by the class's
|
||||
calculate_key(target) method applied to the target objects.
|
||||
This weak value dictionary is used to short-circuit creation so
|
||||
that multiple references to the same (object, function) pair
|
||||
produce the same BoundMethodWeakref instance.
|
||||
"""
|
||||
|
||||
_all_instances = weakref.WeakValueDictionary() # type: ignore[var-annotated]
|
||||
|
||||
def __new__(cls, target, on_delete=None, *arguments, **named):
|
||||
"""Create new instance or return current instance.
|
||||
|
||||
Basically this method of construction allows us to
|
||||
short-circuit creation of references to already-referenced
|
||||
instance methods. The key corresponding to the target is
|
||||
calculated, and if there is already an existing reference,
|
||||
that is returned, with its deletion_methods attribute updated.
|
||||
Otherwise the new instance is created and registered in the
|
||||
table of already-referenced methods.
|
||||
"""
|
||||
key = cls.calculate_key(target)
|
||||
current = cls._all_instances.get(key)
|
||||
if current is not None:
|
||||
current.deletion_methods.append(on_delete)
|
||||
return current
|
||||
else:
|
||||
base = super().__new__(cls)
|
||||
cls._all_instances[key] = base
|
||||
base.__init__(target, on_delete, *arguments, **named)
|
||||
return base
|
||||
|
||||
def __init__(self, target, on_delete=None):
|
||||
"""Return a weak-reference-like instance for a bound method.
|
||||
|
||||
- ``target``: The instance-method target for the weak reference,
|
||||
must have im_self and im_func attributes and be
|
||||
reconstructable via the following, which is true of built-in
|
||||
instance methods::
|
||||
|
||||
target.im_func.__get__( target.im_self )
|
||||
|
||||
- ``on_delete``: Optional callback which will be called when
|
||||
this weak reference ceases to be valid (i.e. either the
|
||||
object or the function is garbage collected). Should take a
|
||||
single argument, which will be passed a pointer to this
|
||||
object.
|
||||
"""
|
||||
|
||||
def remove(weak, self=self):
|
||||
"""Set self.isDead to True when method or instance is destroyed."""
|
||||
methods = self.deletion_methods[:]
|
||||
del self.deletion_methods[:]
|
||||
try:
|
||||
del self.__class__._all_instances[self.key]
|
||||
except KeyError:
|
||||
pass
|
||||
for function in methods:
|
||||
try:
|
||||
if callable(function):
|
||||
function(self)
|
||||
except Exception:
|
||||
try:
|
||||
traceback.print_exc()
|
||||
except AttributeError:
|
||||
e = sys.exc_info()[1]
|
||||
print(
|
||||
f"Exception during saferef {self} "
|
||||
f"cleanup function {function}: {e}"
|
||||
)
|
||||
|
||||
self.deletion_methods = [on_delete]
|
||||
self.key = self.calculate_key(target)
|
||||
im_self = get_self(target)
|
||||
im_func = get_func(target)
|
||||
self.weak_self = weakref.ref(im_self, remove)
|
||||
self.weak_func = weakref.ref(im_func, remove)
|
||||
self.self_name = str(im_self)
|
||||
self.func_name = str(im_func.__name__)
|
||||
|
||||
@classmethod
|
||||
def calculate_key(cls, target):
|
||||
"""Calculate the reference key for this reference.
|
||||
|
||||
Currently this is a two-tuple of the id()'s of the target
|
||||
object and the target function respectively.
|
||||
"""
|
||||
return (id(get_self(target)), id(get_func(target)))
|
||||
|
||||
def __str__(self):
|
||||
"""Give a friendly representation of the object."""
|
||||
return "{}({}.{})".format(
|
||||
self.__class__.__name__,
|
||||
self.self_name,
|
||||
self.func_name,
|
||||
)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.self_name, self.key))
|
||||
|
||||
def __nonzero__(self):
|
||||
"""Whether we are still a valid reference."""
|
||||
return self() is not None
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Compare with another reference."""
|
||||
if not isinstance(other, self.__class__):
|
||||
return operator.eq(self.__class__, type(other))
|
||||
return operator.eq(self.key, other.key)
|
||||
|
||||
def __call__(self):
|
||||
"""Return a strong reference to the bound method.
|
||||
|
||||
If the target cannot be retrieved, then will return None,
|
||||
otherwise returns a bound instance method for our object and
|
||||
function.
|
||||
|
||||
Note: You may call this method any number of times, as it does
|
||||
not invalidate the reference.
|
||||
"""
|
||||
target = self.weak_self()
|
||||
if target is not None:
|
||||
function = self.weak_func()
|
||||
if function is not None:
|
||||
return function.__get__(target)
|
||||
return None
|
142
venv/lib/python3.11/site-packages/blinker/_utilities.py
Normal file
142
venv/lib/python3.11/site-packages/blinker/_utilities.py
Normal file
|
@ -0,0 +1,142 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import inspect
|
||||
import sys
|
||||
import typing as t
|
||||
from functools import partial
|
||||
from weakref import ref
|
||||
|
||||
from blinker._saferef import BoundMethodWeakref
|
||||
|
||||
IdentityType = t.Union[t.Tuple[int, int], str, int]
|
||||
|
||||
|
||||
class _symbol:
|
||||
def __init__(self, name):
|
||||
"""Construct a new named symbol."""
|
||||
self.__name__ = self.name = name
|
||||
|
||||
def __reduce__(self):
|
||||
return symbol, (self.name,)
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
_symbol.__name__ = "symbol"
|
||||
|
||||
|
||||
class symbol:
|
||||
"""A constant symbol.
|
||||
|
||||
>>> symbol('foo') is symbol('foo')
|
||||
True
|
||||
>>> symbol('foo')
|
||||
foo
|
||||
|
||||
A slight refinement of the MAGICCOOKIE=object() pattern. The primary
|
||||
advantage of symbol() is its repr(). They are also singletons.
|
||||
|
||||
Repeated calls of symbol('name') will all return the same instance.
|
||||
|
||||
"""
|
||||
|
||||
symbols = {} # type: ignore[var-annotated]
|
||||
|
||||
def __new__(cls, name):
|
||||
try:
|
||||
return cls.symbols[name]
|
||||
except KeyError:
|
||||
return cls.symbols.setdefault(name, _symbol(name))
|
||||
|
||||
|
||||
def hashable_identity(obj: object) -> IdentityType:
|
||||
if hasattr(obj, "__func__"):
|
||||
return (id(obj.__func__), id(obj.__self__)) # type: ignore[attr-defined]
|
||||
elif hasattr(obj, "im_func"):
|
||||
return (id(obj.im_func), id(obj.im_self)) # type: ignore[attr-defined]
|
||||
elif isinstance(obj, (int, str)):
|
||||
return obj
|
||||
else:
|
||||
return id(obj)
|
||||
|
||||
|
||||
WeakTypes = (ref, BoundMethodWeakref)
|
||||
|
||||
|
||||
class annotatable_weakref(ref):
|
||||
"""A weakref.ref that supports custom instance attributes."""
|
||||
|
||||
receiver_id: t.Optional[IdentityType]
|
||||
sender_id: t.Optional[IdentityType]
|
||||
|
||||
|
||||
def reference( # type: ignore[no-untyped-def]
|
||||
object, callback=None, **annotations
|
||||
) -> annotatable_weakref:
|
||||
"""Return an annotated weak ref."""
|
||||
if callable(object):
|
||||
weak = callable_reference(object, callback)
|
||||
else:
|
||||
weak = annotatable_weakref(object, callback)
|
||||
for key, value in annotations.items():
|
||||
setattr(weak, key, value)
|
||||
return weak # type: ignore[no-any-return]
|
||||
|
||||
|
||||
def callable_reference(object, callback=None):
|
||||
"""Return an annotated weak ref, supporting bound instance methods."""
|
||||
if hasattr(object, "im_self") and object.im_self is not None:
|
||||
return BoundMethodWeakref(target=object, on_delete=callback)
|
||||
elif hasattr(object, "__self__") and object.__self__ is not None:
|
||||
return BoundMethodWeakref(target=object, on_delete=callback)
|
||||
return annotatable_weakref(object, callback)
|
||||
|
||||
|
||||
class lazy_property:
|
||||
"""A @property that is only evaluated once."""
|
||||
|
||||
def __init__(self, deferred):
|
||||
self._deferred = deferred
|
||||
self.__doc__ = deferred.__doc__
|
||||
|
||||
def __get__(self, obj, cls):
|
||||
if obj is None:
|
||||
return self
|
||||
value = self._deferred(obj)
|
||||
setattr(obj, self._deferred.__name__, value)
|
||||
return value
|
||||
|
||||
|
||||
def is_coroutine_function(func: t.Any) -> bool:
|
||||
# Python < 3.8 does not correctly determine partially wrapped
|
||||
# coroutine functions are coroutine functions, hence the need for
|
||||
# this to exist. Code taken from CPython.
|
||||
if sys.version_info >= (3, 8):
|
||||
return asyncio.iscoroutinefunction(func)
|
||||
else:
|
||||
# Note that there is something special about the AsyncMock
|
||||
# such that it isn't determined as a coroutine function
|
||||
# without an explicit check.
|
||||
try:
|
||||
from unittest.mock import AsyncMock # type: ignore[attr-defined]
|
||||
|
||||
if isinstance(func, AsyncMock):
|
||||
return True
|
||||
except ImportError:
|
||||
# Not testing, no asynctest to import
|
||||
pass
|
||||
|
||||
while inspect.ismethod(func):
|
||||
func = func.__func__
|
||||
while isinstance(func, partial):
|
||||
func = func.func
|
||||
if not inspect.isfunction(func):
|
||||
return False
|
||||
|
||||
if func.__code__.co_flags & inspect.CO_COROUTINE:
|
||||
return True
|
||||
|
||||
acic = asyncio.coroutines._is_coroutine # type: ignore[attr-defined]
|
||||
return getattr(func, "_is_coroutine", None) is acic
|
551
venv/lib/python3.11/site-packages/blinker/base.py
Normal file
551
venv/lib/python3.11/site-packages/blinker/base.py
Normal file
|
@ -0,0 +1,551 @@
|
|||
"""Signals and events.
|
||||
|
||||
A small implementation of signals, inspired by a snippet of Django signal
|
||||
API client code seen in a blog post. Signals are first-class objects and
|
||||
each manages its own receivers and message emission.
|
||||
|
||||
The :func:`signal` function provides singleton behavior for named signals.
|
||||
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import typing as t
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from warnings import warn
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
from blinker._utilities import annotatable_weakref
|
||||
from blinker._utilities import hashable_identity
|
||||
from blinker._utilities import IdentityType
|
||||
from blinker._utilities import is_coroutine_function
|
||||
from blinker._utilities import lazy_property
|
||||
from blinker._utilities import reference
|
||||
from blinker._utilities import symbol
|
||||
from blinker._utilities import WeakTypes
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
import typing_extensions as te
|
||||
|
||||
T_callable = t.TypeVar("T_callable", bound=t.Callable[..., t.Any])
|
||||
|
||||
T = t.TypeVar("T")
|
||||
P = te.ParamSpec("P")
|
||||
|
||||
AsyncWrapperType = t.Callable[[t.Callable[P, T]], t.Callable[P, t.Awaitable[T]]]
|
||||
SyncWrapperType = t.Callable[[t.Callable[P, t.Awaitable[T]]], t.Callable[P, T]]
|
||||
|
||||
ANY = symbol("ANY")
|
||||
ANY.__doc__ = 'Token for "any sender".'
|
||||
ANY_ID = 0
|
||||
|
||||
|
||||
class Signal:
|
||||
"""A notification emitter."""
|
||||
|
||||
#: An :obj:`ANY` convenience synonym, allows ``Signal.ANY``
|
||||
#: without an additional import.
|
||||
ANY = ANY
|
||||
|
||||
@lazy_property
|
||||
def receiver_connected(self) -> Signal:
|
||||
"""Emitted after each :meth:`connect`.
|
||||
|
||||
The signal sender is the signal instance, and the :meth:`connect`
|
||||
arguments are passed through: *receiver*, *sender*, and *weak*.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
"""
|
||||
return Signal(doc="Emitted after a receiver connects.")
|
||||
|
||||
@lazy_property
|
||||
def receiver_disconnected(self) -> Signal:
|
||||
"""Emitted after :meth:`disconnect`.
|
||||
|
||||
The sender is the signal instance, and the :meth:`disconnect` arguments
|
||||
are passed through: *receiver* and *sender*.
|
||||
|
||||
Note, this signal is emitted **only** when :meth:`disconnect` is
|
||||
called explicitly.
|
||||
|
||||
The disconnect signal can not be emitted by an automatic disconnect
|
||||
(due to a weakly referenced receiver or sender going out of scope),
|
||||
as the receiver and/or sender instances are no longer available for
|
||||
use at the time this signal would be emitted.
|
||||
|
||||
An alternative approach is available by subscribing to
|
||||
:attr:`receiver_connected` and setting up a custom weakref cleanup
|
||||
callback on weak receivers and senders.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
"""
|
||||
return Signal(doc="Emitted after a receiver disconnects.")
|
||||
|
||||
def __init__(self, doc: str | None = None) -> None:
|
||||
"""
|
||||
:param doc: optional. If provided, will be assigned to the signal's
|
||||
__doc__ attribute.
|
||||
|
||||
"""
|
||||
if doc:
|
||||
self.__doc__ = doc
|
||||
#: A mapping of connected receivers.
|
||||
#:
|
||||
#: The values of this mapping are not meaningful outside of the
|
||||
#: internal :class:`Signal` implementation, however the boolean value
|
||||
#: of the mapping is useful as an extremely efficient check to see if
|
||||
#: any receivers are connected to the signal.
|
||||
self.receivers: dict[IdentityType, t.Callable | annotatable_weakref] = {}
|
||||
self.is_muted = False
|
||||
self._by_receiver: dict[IdentityType, set[IdentityType]] = defaultdict(set)
|
||||
self._by_sender: dict[IdentityType, set[IdentityType]] = defaultdict(set)
|
||||
self._weak_senders: dict[IdentityType, annotatable_weakref] = {}
|
||||
|
||||
def connect(
|
||||
self, receiver: T_callable, sender: t.Any = ANY, weak: bool = True
|
||||
) -> T_callable:
|
||||
"""Connect *receiver* to signal events sent by *sender*.
|
||||
|
||||
:param receiver: A callable. Will be invoked by :meth:`send` with
|
||||
`sender=` as a single positional argument and any ``kwargs`` that
|
||||
were provided to a call to :meth:`send`.
|
||||
|
||||
:param sender: Any object or :obj:`ANY`, defaults to ``ANY``.
|
||||
Restricts notifications delivered to *receiver* to only those
|
||||
:meth:`send` emissions sent by *sender*. If ``ANY``, the receiver
|
||||
will always be notified. A *receiver* may be connected to
|
||||
multiple *sender* values on the same Signal through multiple calls
|
||||
to :meth:`connect`.
|
||||
|
||||
:param weak: If true, the Signal will hold a weakref to *receiver*
|
||||
and automatically disconnect when *receiver* goes out of scope or
|
||||
is garbage collected. Defaults to True.
|
||||
|
||||
"""
|
||||
receiver_id = hashable_identity(receiver)
|
||||
receiver_ref: T_callable | annotatable_weakref
|
||||
|
||||
if weak:
|
||||
receiver_ref = reference(receiver, self._cleanup_receiver)
|
||||
receiver_ref.receiver_id = receiver_id
|
||||
else:
|
||||
receiver_ref = receiver
|
||||
sender_id: IdentityType
|
||||
if sender is ANY:
|
||||
sender_id = ANY_ID
|
||||
else:
|
||||
sender_id = hashable_identity(sender)
|
||||
|
||||
self.receivers.setdefault(receiver_id, receiver_ref)
|
||||
self._by_sender[sender_id].add(receiver_id)
|
||||
self._by_receiver[receiver_id].add(sender_id)
|
||||
del receiver_ref
|
||||
|
||||
if sender is not ANY and sender_id not in self._weak_senders:
|
||||
# wire together a cleanup for weakref-able senders
|
||||
try:
|
||||
sender_ref = reference(sender, self._cleanup_sender)
|
||||
sender_ref.sender_id = sender_id
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
self._weak_senders.setdefault(sender_id, sender_ref)
|
||||
del sender_ref
|
||||
|
||||
# broadcast this connection. if receivers raise, disconnect.
|
||||
if "receiver_connected" in self.__dict__ and self.receiver_connected.receivers:
|
||||
try:
|
||||
self.receiver_connected.send(
|
||||
self, receiver=receiver, sender=sender, weak=weak
|
||||
)
|
||||
except TypeError as e:
|
||||
self.disconnect(receiver, sender)
|
||||
raise e
|
||||
if receiver_connected.receivers and self is not receiver_connected:
|
||||
try:
|
||||
receiver_connected.send(
|
||||
self, receiver_arg=receiver, sender_arg=sender, weak_arg=weak
|
||||
)
|
||||
except TypeError as e:
|
||||
self.disconnect(receiver, sender)
|
||||
raise e
|
||||
return receiver
|
||||
|
||||
def connect_via(
|
||||
self, sender: t.Any, weak: bool = False
|
||||
) -> t.Callable[[T_callable], T_callable]:
|
||||
"""Connect the decorated function as a receiver for *sender*.
|
||||
|
||||
:param sender: Any object or :obj:`ANY`. The decorated function
|
||||
will only receive :meth:`send` emissions sent by *sender*. If
|
||||
``ANY``, the receiver will always be notified. A function may be
|
||||
decorated multiple times with differing *sender* values.
|
||||
|
||||
:param weak: If true, the Signal will hold a weakref to the
|
||||
decorated function and automatically disconnect when *receiver*
|
||||
goes out of scope or is garbage collected. Unlike
|
||||
:meth:`connect`, this defaults to False.
|
||||
|
||||
The decorated function will be invoked by :meth:`send` with
|
||||
`sender=` as a single positional argument and any ``kwargs`` that
|
||||
were provided to the call to :meth:`send`.
|
||||
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
"""
|
||||
|
||||
def decorator(fn: T_callable) -> T_callable:
|
||||
self.connect(fn, sender, weak)
|
||||
return fn
|
||||
|
||||
return decorator
|
||||
|
||||
@contextmanager
|
||||
def connected_to(
|
||||
self, receiver: t.Callable, sender: t.Any = ANY
|
||||
) -> t.Generator[None, None, None]:
|
||||
"""Execute a block with the signal temporarily connected to *receiver*.
|
||||
|
||||
:param receiver: a receiver callable
|
||||
:param sender: optional, a sender to filter on
|
||||
|
||||
This is a context manager for use in the ``with`` statement. It can
|
||||
be useful in unit tests. *receiver* is connected to the signal for
|
||||
the duration of the ``with`` block, and will be disconnected
|
||||
automatically when exiting the block:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with on_ready.connected_to(receiver):
|
||||
# do stuff
|
||||
on_ready.send(123)
|
||||
|
||||
.. versionadded:: 1.1
|
||||
|
||||
"""
|
||||
self.connect(receiver, sender=sender, weak=False)
|
||||
try:
|
||||
yield None
|
||||
except Exception as e:
|
||||
self.disconnect(receiver)
|
||||
raise e
|
||||
else:
|
||||
self.disconnect(receiver)
|
||||
|
||||
@contextmanager
|
||||
def muted(self) -> t.Generator[None, None, None]:
|
||||
"""Context manager for temporarily disabling signal.
|
||||
Useful for test purposes.
|
||||
"""
|
||||
self.is_muted = True
|
||||
try:
|
||||
yield None
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
self.is_muted = False
|
||||
|
||||
def temporarily_connected_to(
|
||||
self, receiver: t.Callable, sender: t.Any = ANY
|
||||
) -> t.ContextManager[None]:
|
||||
"""An alias for :meth:`connected_to`.
|
||||
|
||||
:param receiver: a receiver callable
|
||||
:param sender: optional, a sender to filter on
|
||||
|
||||
.. versionadded:: 0.9
|
||||
|
||||
.. versionchanged:: 1.1
|
||||
Renamed to :meth:`connected_to`. ``temporarily_connected_to`` was
|
||||
deprecated in 1.2 and will be removed in a subsequent version.
|
||||
|
||||
"""
|
||||
warn(
|
||||
"temporarily_connected_to is deprecated; use connected_to instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
return self.connected_to(receiver, sender)
|
||||
|
||||
def send(
|
||||
self,
|
||||
*sender: t.Any,
|
||||
_async_wrapper: AsyncWrapperType | None = None,
|
||||
**kwargs: t.Any,
|
||||
) -> list[tuple[t.Callable, t.Any]]:
|
||||
"""Emit this signal on behalf of *sender*, passing on ``kwargs``.
|
||||
|
||||
Returns a list of 2-tuples, pairing receivers with their return
|
||||
value. The ordering of receiver notification is undefined.
|
||||
|
||||
:param sender: Any object or ``None``. If omitted, synonymous
|
||||
with ``None``. Only accepts one positional argument.
|
||||
:param _async_wrapper: A callable that should wrap a coroutine
|
||||
receiver and run it when called synchronously.
|
||||
|
||||
:param kwargs: Data to be sent to receivers.
|
||||
"""
|
||||
if self.is_muted:
|
||||
return []
|
||||
|
||||
sender = self._extract_sender(sender)
|
||||
results = []
|
||||
for receiver in self.receivers_for(sender):
|
||||
if is_coroutine_function(receiver):
|
||||
if _async_wrapper is None:
|
||||
raise RuntimeError("Cannot send to a coroutine function")
|
||||
receiver = _async_wrapper(receiver)
|
||||
result = receiver(sender, **kwargs) # type: ignore[call-arg]
|
||||
results.append((receiver, result))
|
||||
return results
|
||||
|
||||
async def send_async(
|
||||
self,
|
||||
*sender: t.Any,
|
||||
_sync_wrapper: SyncWrapperType | None = None,
|
||||
**kwargs: t.Any,
|
||||
) -> list[tuple[t.Callable, t.Any]]:
|
||||
"""Emit this signal on behalf of *sender*, passing on ``kwargs``.
|
||||
|
||||
Returns a list of 2-tuples, pairing receivers with their return
|
||||
value. The ordering of receiver notification is undefined.
|
||||
|
||||
:param sender: Any object or ``None``. If omitted, synonymous
|
||||
with ``None``. Only accepts one positional argument.
|
||||
:param _sync_wrapper: A callable that should wrap a synchronous
|
||||
receiver and run it when awaited.
|
||||
|
||||
:param kwargs: Data to be sent to receivers.
|
||||
"""
|
||||
if self.is_muted:
|
||||
return []
|
||||
|
||||
sender = self._extract_sender(sender)
|
||||
results = []
|
||||
for receiver in self.receivers_for(sender):
|
||||
if not is_coroutine_function(receiver):
|
||||
if _sync_wrapper is None:
|
||||
raise RuntimeError("Cannot send to a non-coroutine function")
|
||||
receiver = _sync_wrapper(receiver) # type: ignore[arg-type]
|
||||
result = await receiver(sender, **kwargs) # type: ignore[call-arg, misc]
|
||||
results.append((receiver, result))
|
||||
return results
|
||||
|
||||
def _extract_sender(self, sender: t.Any) -> t.Any:
|
||||
if not self.receivers:
|
||||
# Ensure correct signature even on no-op sends, disable with -O
|
||||
# for lowest possible cost.
|
||||
if __debug__ and sender and len(sender) > 1:
|
||||
raise TypeError(
|
||||
f"send() accepts only one positional argument, {len(sender)} given"
|
||||
)
|
||||
return []
|
||||
|
||||
# Using '*sender' rather than 'sender=None' allows 'sender' to be
|
||||
# used as a keyword argument- i.e. it's an invisible name in the
|
||||
# function signature.
|
||||
if len(sender) == 0:
|
||||
sender = None
|
||||
elif len(sender) > 1:
|
||||
raise TypeError(
|
||||
f"send() accepts only one positional argument, {len(sender)} given"
|
||||
)
|
||||
else:
|
||||
sender = sender[0]
|
||||
return sender
|
||||
|
||||
def has_receivers_for(self, sender: t.Any) -> bool:
|
||||
"""True if there is probably a receiver for *sender*.
|
||||
|
||||
Performs an optimistic check only. Does not guarantee that all
|
||||
weakly referenced receivers are still alive. See
|
||||
:meth:`receivers_for` for a stronger search.
|
||||
|
||||
"""
|
||||
if not self.receivers:
|
||||
return False
|
||||
if self._by_sender[ANY_ID]:
|
||||
return True
|
||||
if sender is ANY:
|
||||
return False
|
||||
return hashable_identity(sender) in self._by_sender
|
||||
|
||||
def receivers_for(
|
||||
self, sender: t.Any
|
||||
) -> t.Generator[t.Callable | annotatable_weakref, None, None]:
|
||||
"""Iterate all live receivers listening for *sender*."""
|
||||
# TODO: test receivers_for(ANY)
|
||||
if self.receivers:
|
||||
sender_id = hashable_identity(sender)
|
||||
if sender_id in self._by_sender:
|
||||
ids = self._by_sender[ANY_ID] | self._by_sender[sender_id]
|
||||
else:
|
||||
ids = self._by_sender[ANY_ID].copy()
|
||||
for receiver_id in ids:
|
||||
receiver = self.receivers.get(receiver_id)
|
||||
if receiver is None:
|
||||
continue
|
||||
if isinstance(receiver, WeakTypes):
|
||||
strong = receiver()
|
||||
if strong is None:
|
||||
self._disconnect(receiver_id, ANY_ID)
|
||||
continue
|
||||
receiver = strong
|
||||
yield receiver # type: ignore[misc]
|
||||
|
||||
def disconnect(self, receiver: t.Callable, sender: t.Any = ANY) -> None:
|
||||
"""Disconnect *receiver* from this signal's events.
|
||||
|
||||
:param receiver: a previously :meth:`connected<connect>` callable
|
||||
|
||||
:param sender: a specific sender to disconnect from, or :obj:`ANY`
|
||||
to disconnect from all senders. Defaults to ``ANY``.
|
||||
|
||||
"""
|
||||
sender_id: IdentityType
|
||||
if sender is ANY:
|
||||
sender_id = ANY_ID
|
||||
else:
|
||||
sender_id = hashable_identity(sender)
|
||||
receiver_id = hashable_identity(receiver)
|
||||
self._disconnect(receiver_id, sender_id)
|
||||
|
||||
if (
|
||||
"receiver_disconnected" in self.__dict__
|
||||
and self.receiver_disconnected.receivers
|
||||
):
|
||||
self.receiver_disconnected.send(self, receiver=receiver, sender=sender)
|
||||
|
||||
def _disconnect(self, receiver_id: IdentityType, sender_id: IdentityType) -> None:
|
||||
if sender_id == ANY_ID:
|
||||
if self._by_receiver.pop(receiver_id, False):
|
||||
for bucket in self._by_sender.values():
|
||||
bucket.discard(receiver_id)
|
||||
self.receivers.pop(receiver_id, None)
|
||||
else:
|
||||
self._by_sender[sender_id].discard(receiver_id)
|
||||
self._by_receiver[receiver_id].discard(sender_id)
|
||||
|
||||
def _cleanup_receiver(self, receiver_ref: annotatable_weakref) -> None:
|
||||
"""Disconnect a receiver from all senders."""
|
||||
self._disconnect(t.cast(IdentityType, receiver_ref.receiver_id), ANY_ID)
|
||||
|
||||
def _cleanup_sender(self, sender_ref: annotatable_weakref) -> None:
|
||||
"""Disconnect all receivers from a sender."""
|
||||
sender_id = t.cast(IdentityType, sender_ref.sender_id)
|
||||
assert sender_id != ANY_ID
|
||||
self._weak_senders.pop(sender_id, None)
|
||||
for receiver_id in self._by_sender.pop(sender_id, ()):
|
||||
self._by_receiver[receiver_id].discard(sender_id)
|
||||
|
||||
def _cleanup_bookkeeping(self) -> None:
|
||||
"""Prune unused sender/receiver bookkeeping. Not threadsafe.
|
||||
|
||||
Connecting & disconnecting leave behind a small amount of bookkeeping
|
||||
for the receiver and sender values. Typical workloads using Blinker,
|
||||
for example in most web apps, Flask, CLI scripts, etc., are not
|
||||
adversely affected by this bookkeeping.
|
||||
|
||||
With a long-running Python process performing dynamic signal routing
|
||||
with high volume- e.g. connecting to function closures, "senders" are
|
||||
all unique object instances, and doing all of this over and over- you
|
||||
may see memory usage will grow due to extraneous bookkeeping. (An empty
|
||||
set() for each stale sender/receiver pair.)
|
||||
|
||||
This method will prune that bookkeeping away, with the caveat that such
|
||||
pruning is not threadsafe. The risk is that cleanup of a fully
|
||||
disconnected receiver/sender pair occurs while another thread is
|
||||
connecting that same pair. If you are in the highly dynamic, unique
|
||||
receiver/sender situation that has lead you to this method, that
|
||||
failure mode is perhaps not a big deal for you.
|
||||
"""
|
||||
for mapping in (self._by_sender, self._by_receiver):
|
||||
for _id, bucket in list(mapping.items()):
|
||||
if not bucket:
|
||||
mapping.pop(_id, None)
|
||||
|
||||
def _clear_state(self) -> None:
|
||||
"""Throw away all signal state. Useful for unit tests."""
|
||||
self._weak_senders.clear()
|
||||
self.receivers.clear()
|
||||
self._by_sender.clear()
|
||||
self._by_receiver.clear()
|
||||
|
||||
|
||||
receiver_connected = Signal(
|
||||
"""\
|
||||
Sent by a :class:`Signal` after a receiver connects.
|
||||
|
||||
:argument: the Signal that was connected to
|
||||
:keyword receiver_arg: the connected receiver
|
||||
:keyword sender_arg: the sender to connect to
|
||||
:keyword weak_arg: true if the connection to receiver_arg is a weak reference
|
||||
|
||||
.. deprecated:: 1.2
|
||||
|
||||
As of 1.2, individual signals have their own private
|
||||
:attr:`~Signal.receiver_connected` and
|
||||
:attr:`~Signal.receiver_disconnected` signals with a slightly simplified
|
||||
call signature. This global signal is planned to be removed in 1.6.
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
class NamedSignal(Signal):
|
||||
"""A named generic notification emitter."""
|
||||
|
||||
def __init__(self, name: str, doc: str | None = None) -> None:
|
||||
Signal.__init__(self, doc)
|
||||
|
||||
#: The name of this signal.
|
||||
self.name = name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
base = Signal.__repr__(self)
|
||||
return f"{base[:-1]}; {self.name!r}>"
|
||||
|
||||
|
||||
class Namespace(dict):
|
||||
"""A mapping of signal names to signals."""
|
||||
|
||||
def signal(self, name: str, doc: str | None = None) -> NamedSignal:
|
||||
"""Return the :class:`NamedSignal` *name*, creating it if required.
|
||||
|
||||
Repeated calls to this function will return the same signal object.
|
||||
|
||||
"""
|
||||
try:
|
||||
return self[name] # type: ignore[no-any-return]
|
||||
except KeyError:
|
||||
result = self.setdefault(name, NamedSignal(name, doc))
|
||||
return result # type: ignore[no-any-return]
|
||||
|
||||
|
||||
class WeakNamespace(WeakValueDictionary):
|
||||
"""A weak mapping of signal names to signals.
|
||||
|
||||
Automatically cleans up unused Signals when the last reference goes out
|
||||
of scope. This namespace implementation exists for a measure of legacy
|
||||
compatibility with Blinker <= 1.2, and may be dropped in the future.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
"""
|
||||
|
||||
def signal(self, name: str, doc: str | None = None) -> NamedSignal:
|
||||
"""Return the :class:`NamedSignal` *name*, creating it if required.
|
||||
|
||||
Repeated calls to this function will return the same signal object.
|
||||
|
||||
"""
|
||||
try:
|
||||
return self[name] # type: ignore[no-any-return]
|
||||
except KeyError:
|
||||
result = self.setdefault(name, NamedSignal(name, doc))
|
||||
return result # type: ignore[no-any-return]
|
||||
|
||||
|
||||
signal = Namespace().signal
|
0
venv/lib/python3.11/site-packages/blinker/py.typed
Normal file
0
venv/lib/python3.11/site-packages/blinker/py.typed
Normal file
Loading…
Add table
Add a link
Reference in a new issue