diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-05-13 08:41:32 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2024-05-13 08:49:12 +0300 |
commit | 729cf217631f33c3297a77b89df46671992350ac (patch) | |
tree | db808d27d756c10fe26ab8aba46e1b52d7fb0476 | |
parent | b2d826f65dc1cddb9e2ebca3b0048bb0e8052340 (diff) | |
download | ydb-729cf217631f33c3297a77b89df46671992350ac.tar.gz |
Intermediate changes
20 files changed, 374 insertions, 228 deletions
diff --git a/contrib/python/blinker/py3/.dist-info/METADATA b/contrib/python/blinker/py3/.dist-info/METADATA index 2eb62efbef..d3906453a1 100644 --- a/contrib/python/blinker/py3/.dist-info/METADATA +++ b/contrib/python/blinker/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: blinker -Version: 1.8.0 +Version: 1.8.1 Summary: Fast, simple object-to-object and broadcast signaling Author: Jason Kirtland Maintainer-email: Pallets Ecosystem <contact@palletsprojects.com> diff --git a/contrib/python/blinker/py3/blinker/_utilities.py b/contrib/python/blinker/py3/blinker/_utilities.py index 784ba4e121..000c902a25 100644 --- a/contrib/python/blinker/py3/blinker/_utilities.py +++ b/contrib/python/blinker/py3/blinker/_utilities.py @@ -1,5 +1,6 @@ from __future__ import annotations +import collections.abc as c import inspect import typing as t from weakref import ref @@ -34,18 +35,29 @@ class Symbol: def __repr__(self) -> str: return self.name - def __getnewargs__(self) -> tuple[t.Any]: + def __getnewargs__(self) -> tuple[t.Any, ...]: return (self.name,) -def make_id(obj: object) -> t.Hashable: +def make_id(obj: object) -> c.Hashable: + """Get a stable identifier for a receiver or sender, to be used as a dict + key or in a set. + """ if inspect.ismethod(obj): + # The id of a bound method is not stable, but the id of the unbound + # function and instance are. return id(obj.__func__), id(obj.__self__) + if isinstance(obj, (str, int)): + # Instances with the same value always compare equal and have the same + # hash, even if the id may change. + return obj + + # Assume other types are not hashable but will always be the same instance. return id(obj) -def make_ref(obj: T, callback: t.Callable[[ref[T]], None] | None = None) -> ref[T]: +def make_ref(obj: T, callback: c.Callable[[ref[T]], None] | None = None) -> ref[T]: if inspect.ismethod(obj): return WeakMethod(obj, callback) # type: ignore[arg-type, return-value] diff --git a/contrib/python/blinker/py3/blinker/base.py b/contrib/python/blinker/py3/blinker/base.py index 8aa65255b9..62086aa57f 100644 --- a/contrib/python/blinker/py3/blinker/base.py +++ b/contrib/python/blinker/py3/blinker/base.py @@ -1,18 +1,11 @@ -"""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 collections.abc as c import typing as t import warnings import weakref from collections import defaultdict +from contextlib import AbstractContextManager from contextlib import contextmanager from functools import cached_property from inspect import iscoroutinefunction @@ -25,37 +18,46 @@ from ._utilities import Symbol if t.TYPE_CHECKING: import typing_extensions as te - F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + F = t.TypeVar("F", bound=c.Callable[..., t.Any]) T = t.TypeVar("T") P = te.ParamSpec("P") class PAsyncWrapper(t.Protocol): - def __call__(self, f: t.Callable[P, t.Awaitable[T]]) -> t.Callable[P, T]: ... + def __call__(self, f: c.Callable[P, c.Awaitable[T]]) -> c.Callable[P, T]: ... class PSyncWrapper(t.Protocol): - def __call__(self, f: t.Callable[P, T]) -> t.Callable[P, t.Awaitable[T]]: ... + def __call__(self, f: c.Callable[P, T]) -> c.Callable[P, c.Awaitable[T]]: ... ANY = Symbol("ANY") -"""Token for "any sender".""" +"""Symbol for "any sender".""" + ANY_ID = 0 class Signal: - """A notification emitter.""" + """A notification emitter. + + :param doc: The docstring for the signal. + """ - #: An :obj:`ANY` convenience synonym, allows ``Signal.ANY`` - #: without an additional import. ANY = ANY + """An alias for the :data:`~blinker.ANY` sender symbol.""" set_class: type[set[t.Any]] = set + """The set class to use for tracking connected receivers and senders. + Python's ``set`` is unordered. If receivers must be dispatched in the order + they were connected, an ordered set implementation can be used. + + .. versionadded:: 1.7 + """ @cached_property def receiver_connected(self) -> Signal: - """Emitted after each :meth:`connect`. + """Emitted at the end of each :meth:`connect` call. The signal sender is the signal instance, and the :meth:`connect` - arguments are passed through: *receiver*, *sender*, and *weak*. + arguments are passed through: ``receiver``, ``sender``, and ``weak``. .. versionadded:: 1.2 """ @@ -63,18 +65,16 @@ class Signal: @cached_property def receiver_disconnected(self) -> Signal: - """Emitted after :meth:`disconnect`. + """Emitted at the end of each :meth:`disconnect` call. The sender is the signal instance, and the :meth:`disconnect` arguments - are passed through: *receiver* and *sender*. + 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. + This signal is emitted **only** when :meth:`disconnect` is called + explicitly. This signal cannot be emitted by an automatic disconnect + when a weakly referenced receiver or sender goes out of scope, as the + instance is no longer be available to be used as the sender for this + signal. An alternative approach is available by subscribing to :attr:`receiver_connected` and setting up a custom weakref cleanup @@ -85,43 +85,38 @@ class Signal: return Signal(doc="Emitted after a receiver disconnects.") def __init__(self, doc: str | None = None) -> None: - """ - :param doc: Set the instance's ``__doc__`` attribute for documentation. - """ 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[ - t.Any, weakref.ref[t.Callable[..., t.Any]] | t.Callable[..., t.Any] + t.Any, weakref.ref[c.Callable[..., t.Any]] | c.Callable[..., t.Any] ] = {} + """The map of connected receivers. Useful to quickly check if any + receivers are connected to the signal: ``if s.receivers:``. The + structure and data is not part of the public API, but checking its + boolean value is. + """ + self.is_muted: bool = False self._by_receiver: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) self._by_sender: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) self._weak_senders: dict[t.Any, weakref.ref[t.Any]] = {} def connect(self, receiver: F, sender: t.Any = ANY, weak: bool = True) -> F: - """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. + """Connect ``receiver`` to be called when the signal is sent by + ``sender``. + + :param receiver: The callable to call when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument + along with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. A receiver may be connected + to multiple senders by calling :meth:`connect` multiple times. + :param weak: Track the receiver with a :mod:`weakref`. The receiver will + be automatically disconnected when it is garbage collected. When + connecting a receiver defined within a function, set to ``False``, + otherwise it will be disconnected when the function scope ends. """ receiver_id = make_id(receiver) sender_id = ANY_ID if sender is ANY else make_id(sender) @@ -166,23 +161,22 @@ class Signal: return receiver - def connect_via(self, sender: t.Any, weak: bool = False) -> t.Callable[[F], F]: - """Connect the decorated function as a receiver for *sender*. + def connect_via(self, sender: t.Any, weak: bool = False) -> c.Callable[[F], F]: + """Connect the decorated function to be called when the signal is sent + by ``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`. + The decorated function will be called when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument along + with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. A receiver may be connected + to multiple senders by calling :meth:`connect` multiple times. + :param weak: Track the receiver with a :mod:`weakref`. The receiver will + be automatically disconnected when it is garbage collected. When + connecting a receiver defined within a function, set to ``False``, + otherwise it will be disconnected when the function scope ends.= .. versionadded:: 1.1 """ @@ -195,26 +189,20 @@ class Signal: @contextmanager def connected_to( - self, receiver: t.Callable[..., t.Any], 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) + self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY + ) -> c.Generator[None, None, None]: + """A context manager that temporarily connects ``receiver`` to the + signal while a ``with`` block executes. When the block exits, the + receiver is disconnected. Useful for tests. + + :param receiver: The callable to call when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument + along with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. .. versionadded:: 1.1 - """ self.connect(receiver, sender=sender, weak=False) @@ -224,9 +212,10 @@ class Signal: self.disconnect(receiver) @contextmanager - def muted(self) -> t.Generator[None, None, None]: - """Context manager for temporarily disabling signal. - Useful for test purposes. + def muted(self) -> c.Generator[None, None, None]: + """A context manager that temporarily disables the signal. No receivers + will be called if the signal is sent, until the ``with`` block exits. + Useful for tests. """ self.is_muted = True @@ -236,17 +225,14 @@ class Signal: self.is_muted = False def temporarily_connected_to( - self, receiver: t.Callable[..., t.Any], 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 + self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY + ) -> AbstractContextManager[None]: + """Deprecated alias for :meth:`connected_to`. .. deprecated:: 1.1 Renamed to ``connected_to``. Will be removed in Blinker 1.9. + + .. versionadded:: 0.9 """ warnings.warn( "'temporarily_connected_to' is renamed to 'connected_to'. The old name is" @@ -263,18 +249,28 @@ class Signal: *, _async_wrapper: PAsyncWrapper | None = None, **kwargs: t.Any, - ) -> list[tuple[t.Callable[..., t.Any], 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. + ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: + """Call all receivers that are connected to the given ``sender`` + or :data:`ANY`. Each receiver is called with ``sender`` as a positional + argument along with any extra keyword arguments. Return a list of + ``(receiver, return value)`` tuples. + + The order receivers are called is undefined, but can be influenced by + setting :attr:`set_class`. + + If a receiver raises an exception, that exception will propagate up. + This makes debugging straightforward, with an assumption that correctly + implemented receivers will not raise. + + :param sender: Call receivers connected to this sender, in addition to + those connected to :data:`ANY`. + :param _async_wrapper: Will be called on any receivers that are async + coroutines to turn them into sync callables. For example, could run + the receiver with an event loop. + :param kwargs: Extra keyword arguments to pass to each receiver. + + .. versionchanged:: 1.7 + Added the ``_async_wrapper`` argument. """ if self.is_muted: return [] @@ -301,18 +297,27 @@ class Signal: *, _sync_wrapper: PSyncWrapper | None = None, **kwargs: t.Any, - ) -> list[tuple[t.Callable[..., t.Any], 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. + ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: + """Await all receivers that are connected to the given ``sender`` + or :data:`ANY`. Each receiver is called with ``sender`` as a positional + argument along with any extra keyword arguments. Return a list of + ``(receiver, return value)`` tuples. + + The order receivers are called is undefined, but can be influenced by + setting :attr:`set_class`. + + If a receiver raises an exception, that exception will propagate up. + This makes debugging straightforward, with an assumption that correctly + implemented receivers will not raise. + + :param sender: Call receivers connected to this sender, in addition to + those connected to :data:`ANY`. + :param _sync_wrapper: Will be called on any receivers that are sync + callables to turn them into async coroutines. For example, + could call the receiver in a thread. + :param kwargs: Extra keyword arguments to pass to each receiver. + + .. versionadded:: 1.7 """ if self.is_muted: return [] @@ -333,12 +338,14 @@ class Signal: return results 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. - + """Check if there is at least one receiver that will be called with the + given ``sender``. A receiver connected to :data:`ANY` will always be + called, regardless of sender. Does not check if weakly referenced + receivers are still live. See :meth:`receivers_for` for a stronger + search. + + :param sender: Check for receivers connected to this sender, in addition + to those connected to :data:`ANY`. """ if not self.receivers: return False @@ -353,8 +360,14 @@ class Signal: def receivers_for( self, sender: t.Any - ) -> t.Generator[t.Callable[..., t.Any], None, None]: - """Iterate all live receivers listening for *sender*.""" + ) -> c.Generator[c.Callable[..., t.Any], None, None]: + """Yield each receiver to be called for ``sender``, in addition to those + to be called for :data:`ANY`. Weakly referenced receivers that are not + live will be disconnected and skipped. + + :param sender: Yield receivers connected to this sender, in addition + to those connected to :data:`ANY`. + """ # TODO: test receivers_for(ANY) if not self.receivers: return @@ -383,16 +396,15 @@ class Signal: else: yield receiver - def disconnect(self, receiver: t.Callable[..., t.Any], 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``. + def disconnect(self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY) -> None: + """Disconnect ``receiver`` from being called when the signal is sent by + ``sender``. + :param receiver: A connected receiver callable. + :param sender: Disconnect from only this sender. By default, disconnect + from all senders. """ - sender_id: t.Hashable + sender_id: c.Hashable if sender is ANY: sender_id = ANY_ID @@ -408,7 +420,7 @@ class Signal: ): self.receiver_disconnected.send(self, receiver=receiver, sender=sender) - def _disconnect(self, receiver_id: t.Hashable, sender_id: t.Hashable) -> None: + def _disconnect(self, receiver_id: c.Hashable, sender_id: c.Hashable) -> None: if sender_id == ANY_ID: if self._by_receiver.pop(receiver_id, None) is not None: for bucket in self._by_sender.values(): @@ -420,19 +432,23 @@ class Signal: self._by_receiver[receiver_id].discard(sender_id) def _make_cleanup_receiver( - self, receiver_id: t.Hashable - ) -> t.Callable[[weakref.ref[t.Callable[..., t.Any]]], None]: - """Disconnect a receiver from all senders.""" + self, receiver_id: c.Hashable + ) -> c.Callable[[weakref.ref[c.Callable[..., t.Any]]], None]: + """Create a callback function to disconnect a weakly referenced + receiver when it is garbage collected. + """ - def cleanup(ref: weakref.ref[t.Callable[..., t.Any]]) -> None: + def cleanup(ref: weakref.ref[c.Callable[..., t.Any]]) -> None: self._disconnect(receiver_id, ANY_ID) return cleanup def _make_cleanup_sender( - self, sender_id: t.Hashable - ) -> t.Callable[[weakref.ref[t.Any]], None]: - """Disconnect all receivers from a sender.""" + self, sender_id: c.Hashable + ) -> c.Callable[[weakref.ref[t.Any]], None]: + """Create a callback function to disconnect all receivers for a weakly + referenced sender when it is garbage collected. + """ assert sender_id != ANY_ID def cleanup(ref: weakref.ref[t.Any]) -> None: @@ -446,23 +462,23 @@ class Signal: 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. + Connecting & disconnecting leaves behind a small amount of bookkeeping + data. 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.) + With a long-running process performing dynamic signal routing with high + volume, e.g. connecting to function closures, senders are all unique + object instances. Doing all of this over and over may cause memory usage + to 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. + 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 ident, bucket in list(mapping.items()): @@ -470,7 +486,7 @@ class Signal: mapping.pop(ident, None) def _clear_state(self) -> None: - """Throw away all signal state. Useful for unit tests.""" + """Disconnect all receivers and senders. Useful for tests.""" self._weak_senders.clear() self.receivers.clear() self._by_sender.clear() @@ -495,7 +511,12 @@ Sent by a :class:`Signal` after a receiver connects. class NamedSignal(Signal): - """A named generic notification emitter.""" + """A named generic notification emitter. The name is not used by the signal + itself, but matches the key in the :class:`Namespace` that it belongs to. + + :param name: The name of the signal within the namespace. + :param doc: The docstring for the signal. + """ def __init__(self, name: str, doc: str | None = None) -> None: super().__init__(doc) @@ -508,32 +529,44 @@ class NamedSignal(Signal): return f"{base[:-1]}; {self.name!r}>" # noqa: E702 -class Namespace(dict): # type: ignore[type-arg] - """A mapping of signal names to signals.""" +if t.TYPE_CHECKING: + + class PNamespaceSignal(t.Protocol): + def __call__(self, name: str, doc: str | None = None) -> NamedSignal: ... + + # Python < 3.9 + _NamespaceBase = dict[str, NamedSignal] # type: ignore[misc] +else: + _NamespaceBase = dict + + +class Namespace(_NamespaceBase): + """A dict mapping names to signals.""" def signal(self, name: str, doc: str | None = None) -> NamedSignal: - """Return the :class:`NamedSignal` *name*, creating it if required. + """Return the :class:`NamedSignal` for the given ``name``, creating it + if required. Repeated calls with the same name return the same signal. - Repeated calls to this function will return the same signal object. + :param name: The name of the signal. + :param doc: The docstring of the signal. """ - 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] + if name not in self: + self[name] = NamedSignal(name, doc) + return self[name] -class _WeakNamespace(WeakValueDictionary): # type: ignore[type-arg] - """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. +class _WeakNamespace(WeakValueDictionary): # type: ignore[type-arg] + """A weak mapping of names to signals. - .. versionadded:: 1.3 + Automatically cleans up unused signals when the last reference goes out + of scope. This namespace implementation provides similar behavior to Blinker + <= 1.2. .. deprecated:: 1.3 Will be removed in Blinker 1.9. + + .. versionadded:: 1.3 """ def __init__(self) -> None: @@ -546,35 +579,36 @@ class _WeakNamespace(WeakValueDictionary): # type: ignore[type-arg] super().__init__() 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. + """Return the :class:`NamedSignal` for the given ``name``, creating it + if required. Repeated calls with the same name return the same signal. + :param name: The name of the signal. + :param doc: The docstring of the signal. """ - 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] + if name not in self: + self[name] = NamedSignal(name, doc) + + return self[name] # type: ignore[no-any-return] -default_namespace = Namespace() -"""A default namespace for creating named signals. :func:`signal` creates a -:class:`NamedSignal` in this namespace. +default_namespace: Namespace = Namespace() +"""A default :class:`Namespace` for creating named signals. :func:`signal` +creates a :class:`NamedSignal` in this namespace. """ -signal = default_namespace.signal -"""Create a :class:`NamedSignal` in :data:`default_namespace`. Repeated calls -with the same name will return the same signal. +signal: PNamespaceSignal = default_namespace.signal +"""Return a :class:`NamedSignal` in :data:`default_namespace` with the given +``name``, creating it if required. Repeated calls with the same name return the +same signal. """ def __getattr__(name: str) -> t.Any: - if name == "reciever_connected": + if name == "receiver_connected": warnings.warn( - "The global 'reciever_connected' signal is deprecated and will be" + "The global 'receiver_connected' signal is deprecated and will be" " removed in Blinker 1.9. Use 'Signal.receiver_connected' and" - " 'Signal.reciever_disconnected' instead.", + " 'Signal.receiver_disconnected' instead.", DeprecationWarning, stacklevel=2, ) @@ -587,5 +621,6 @@ def __getattr__(name: str) -> t.Any: DeprecationWarning, stacklevel=2, ) + return _WeakNamespace raise AttributeError(name) diff --git a/contrib/python/blinker/py3/ya.make b/contrib/python/blinker/py3/ya.make index 0520c07319..303dabc4e4 100644 --- a/contrib/python/blinker/py3/ya.make +++ b/contrib/python/blinker/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(1.8.0) +VERSION(1.8.1) LICENSE(MIT) diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA index 5c622375cd..79347abbf0 100644 --- a/contrib/python/hypothesis/py3/.dist-info/METADATA +++ b/contrib/python/hypothesis/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: hypothesis -Version: 6.100.1 +Version: 6.100.2 Summary: A library for property-based testing Home-page: https://hypothesis.works Author: David R. MacIver and Zac Hatfield-Dodds diff --git a/contrib/python/hypothesis/py3/hypothesis/errors.py b/contrib/python/hypothesis/py3/hypothesis/errors.py index d71f1c337e..0d376a7493 100644 --- a/contrib/python/hypothesis/py3/hypothesis/errors.py +++ b/contrib/python/hypothesis/py3/hypothesis/errors.py @@ -8,6 +8,7 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. + class HypothesisException(Exception): """Generic parent class for exceptions thrown by Hypothesis.""" diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py b/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py index 8c82f63114..85e5f3f8f9 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py @@ -282,7 +282,7 @@ def _from_dtype( if allow_subnormal is not None: kw["allow_subnormal"] = allow_subnormal else: - subnormal = next_down(finfo.smallest_normal, width=finfo.bits) + subnormal = next_down(float(finfo.smallest_normal), width=finfo.bits) ftz = bool(xp.asarray(subnormal, dtype=dtype) == 0) if ftz: kw["allow_subnormal"] = False @@ -303,7 +303,7 @@ def _from_dtype( # complex array, in case complex arrays have different FTZ behaviour # than arrays of the respective composite float. if allow_subnormal is None: - subnormal = next_down(finfo.smallest_normal, width=finfo.bits) + subnormal = next_down(float(finfo.smallest_normal), width=finfo.bits) x = xp.asarray(complex(subnormal, subnormal), dtype=dtype) builtin_x = complex(x) allow_subnormal = builtin_x.real != 0 and builtin_x.imag != 0 diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py index 93f7758ba2..d3b7ba9a8a 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py @@ -1977,6 +1977,7 @@ class ConjectureData: self.extra_information = ExtraInformation() self.ir_tree_nodes = ir_tree_prefix + self._node_index = 0 self.start_example(TOP_LABEL) def __repr__(self): @@ -2274,10 +2275,11 @@ class ConjectureData: def _pop_ir_tree_node(self, ir_type: IRTypeName, kwargs: IRKWargsType) -> IRNode: assert self.ir_tree_nodes is not None - if self.ir_tree_nodes == []: + if self._node_index == len(self.ir_tree_nodes): self.mark_overrun() - node = self.ir_tree_nodes.pop(0) + node = self.ir_tree_nodes[self._node_index] + self._node_index += 1 # If we're trying to draw a different ir type at the same location, then # this ir tree has become badly misaligned. We don't have many good/simple # options here for realigning beyond giving up. diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py index c22cd0b294..ac79b42e6e 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py @@ -63,6 +63,15 @@ class Killed: next_node = attr.ib() + def _repr_pretty_(self, p, cycle): + assert cycle is False + p.text("Killed") + + +def _node_pretty(ir_type, value, kwargs, *, forced): + forced_marker = " [forced]" if forced else "" + return f"{ir_type} {value}{forced_marker} {kwargs}" + @attr.s(slots=True) class Branch: @@ -79,6 +88,16 @@ class Branch: assert max_children > 0 return max_children + def _repr_pretty_(self, p, cycle): + assert cycle is False + for i, (value, child) in enumerate(self.children.items()): + if i > 0: + p.break_() + p.text(_node_pretty(self.ir_type, value, self.kwargs, forced=False)) + with p.indent(2): + p.break_() + p.pretty(child) + @attr.s(slots=True, frozen=True) class Conclusion: @@ -87,6 +106,15 @@ class Conclusion: status: Status = attr.ib() interesting_origin: Optional[InterestingOrigin] = attr.ib() + def _repr_pretty_(self, p, cycle): + assert cycle is False + o = self.interesting_origin + # avoid str(o), which can include multiple lines of context + origin = ( + "" if o is None else f", {o.exc_type.__name__} at {o.filename}:{o.lineno}" + ) + p.text(f"Conclusion ({self.status!r}{origin})") + # The number of max children where, beyond this, it is practically impossible # for hypothesis to saturate / explore all children nodes in a reasonable time @@ -493,6 +521,29 @@ class TreeNode: ) return self.is_exhausted + def _repr_pretty_(self, p, cycle): + assert cycle is False + indent = 0 + for i, (ir_type, kwargs, value) in enumerate( + zip(self.ir_types, self.kwargs, self.values) + ): + with p.indent(indent): + if i > 0: + p.break_() + p.text(_node_pretty(ir_type, value, kwargs, forced=i in self.forced)) + indent += 2 + + if isinstance(self.transition, Branch): + if len(self.values) > 0: + p.break_() + p.pretty(self.transition) + + if isinstance(self.transition, (Killed, Conclusion)): + with p.indent(indent): + if len(self.values) > 0: + p.break_() + p.pretty(self.transition) + class DataTree: """ @@ -889,6 +940,10 @@ class DataTree: if child in children: children.remove(child) + def _repr_pretty_(self, p, cycle): + assert cycle is False + return p.pretty(self.root) + class TreeRecordingObserver(DataObserver): def __init__(self, tree): diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py index ae28fcd741..04bbe079a3 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py @@ -922,7 +922,7 @@ class Shrinker: new_blocks[i] = int_to_bytes(v + o, len(blocked[i])) return self.incorporate_new_buffer(b"".join(new_blocks)) - Integer.shrink(offset, reoffset, random=self.random) + Integer.shrink(offset, reoffset) self.clear_change_tracking() def clear_change_tracking(self): @@ -1193,7 +1193,6 @@ class Shrinker: Lexical.shrink( block, lambda b: self.try_shrinking_blocks(targets, b), - random=self.random, ) @defines_shrink_pass() @@ -1236,7 +1235,6 @@ class Shrinker: + [node.copy(with_value=sign * val)] + self.nodes[node.index + 1 :] ), - random=self.random, node=node, ) @@ -1362,7 +1360,6 @@ class Shrinker: Lexical.shrink( self.shrink_target.buffer[u:v], lambda b: self.try_shrinking_blocks((i,), b), - random=self.random, ) if self.shrink_target is not initial: @@ -1459,7 +1456,6 @@ class Shrinker: ], ) ), - random=self.random, key=lambda i: st.buffer[examples[i].start : examples[i].end], ) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py index 5acdf85a8d..1de89bd18b 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/common.py @@ -20,7 +20,6 @@ class Shrinker: self, initial, predicate, - random, *, full=False, debug=False, @@ -30,7 +29,6 @@ class Shrinker: self.setup(**kwargs) self.current = self.make_immutable(initial) self.initial = self.current - self.random = random self.full = full self.changes = 0 self.name = name @@ -75,7 +73,7 @@ class Shrinker: Note we explicitly do not pass through full. """ - return other_class.shrink(initial, predicate, random=self.random, **kwargs) + return other_class.shrink(initial, predicate, **kwargs) def debug(self, *args): if self.debugging_enabled: @@ -155,7 +153,6 @@ class Shrinker: Does nothing by default. """ - raise NotImplementedError def short_circuit(self): """Possibly attempt to do some shrinking. @@ -163,7 +160,7 @@ class Shrinker: If this returns True, the ``run`` method will terminate early without doing any more work. """ - raise NotImplementedError + return False def left_is_better(self, left, right): """Returns True if the left is strictly simpler than the right diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/lexical.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/lexical.py index 569561c4ed..2f69f1fee3 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/lexical.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinking/lexical.py @@ -43,16 +43,10 @@ class Lexical(Shrinker): Integer.shrink( self.current_int, lambda c: c == self.current_int or self.incorporate_int(c), - random=self.random, ) def partial_sort(self): - Ordering.shrink(self.current, self.consider, random=self.random) - - def short_circuit(self): - """This is just an assemblage of other shrinkers, so we rely on their - short circuiting.""" - return False + Ordering.shrink(self.current, self.consider) def run_step(self): self.minimize_as_integer() diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py index 448f7e51ac..53bee1fe22 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py @@ -908,9 +908,9 @@ def _collection_ish_functions(): np.diag, # bonus undocumented functions from tab-completion: np.asarray_chkfinite, - np.asfarray, np.asfortranarray, ] + return funcs diff --git a/contrib/python/hypothesis/py3/hypothesis/utils/conventions.py b/contrib/python/hypothesis/py3/hypothesis/utils/conventions.py index ead205adc6..ec01326b49 100644 --- a/contrib/python/hypothesis/py3/hypothesis/utils/conventions.py +++ b/contrib/python/hypothesis/py3/hypothesis/utils/conventions.py @@ -8,6 +8,7 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. + class UniqueIdentifier: """A factory for sentinel objects with nice reprs.""" diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py index be22825634..7d9f42de8e 100644 --- a/contrib/python/hypothesis/py3/hypothesis/version.py +++ b/contrib/python/hypothesis/py3/hypothesis/version.py @@ -8,5 +8,5 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. -__version_info__ = (6, 100, 1) +__version_info__ = (6, 100, 2) __version__ = ".".join(map(str, __version_info__)) diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make index c899d543fd..4e4a82e03a 100644 --- a/contrib/python/hypothesis/py3/ya.make +++ b/contrib/python/hypothesis/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.100.1) +VERSION(6.100.2) LICENSE(MPL-2.0) diff --git a/contrib/python/pg8000/.dist-info/METADATA b/contrib/python/pg8000/.dist-info/METADATA index ba1c9b0727..f89f5ac7aa 100644 --- a/contrib/python/pg8000/.dist-info/METADATA +++ b/contrib/python/pg8000/.dist-info/METADATA @@ -1,8 +1,9 @@ Metadata-Version: 2.3 Name: pg8000 -Version: 1.31.1 +Version: 1.31.2 Summary: PostgreSQL interface library Project-URL: Homepage, https://github.com/tlocke/pg8000 +Author: The Contributors License: BSD 3-Clause License License-File: LICENSE Keywords: dbapi,postgresql @@ -23,7 +24,7 @@ Classifier: Topic :: Database :: Front-Ends Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.8 Requires-Dist: python-dateutil>=2.8.2 -Requires-Dist: scramp>=1.4.4 +Requires-Dist: scramp>=1.4.5 Description-Content-Type: text/markdown # pg8000 @@ -400,7 +401,26 @@ pg8000.exceptions.DatabaseError: ... ``` -instead you can write it using the [unnest +the most straightforward way to get around this problem is to rewrie the query using the [`ANY`]( +https://www.postgresql.org/docs/current/functions-comparisons.html#FUNCTIONS-COMPARISONS-ANY-SOME) +function: + +```python +>>> import pg8000.native +>>> +>>> con = pg8000.native.Connection("postgres", password="cpsnow") +>>> +>>> con.run("SELECT 'silo 1' WHERE 'a' = ANY(:v)", v=['a', 'b']) +[['silo 1']] +>>> con.close() + +``` + +However, using the array variant of `ANY` [may cause a performance problem]( +https://stackoverflow.com/questions/34627026/in-vs-any-operator-in-postgresql/34627688#34627688) +and so you can use the [subquery variant of `IN`]( +https://www.postgresql.org/docs/current/functions-subquery.html#FUNCTIONS-SUBQUERY-IN) +with the [unnest ](https://www.postgresql.org/docs/current/functions-array.html) function: ```python @@ -2068,6 +2088,12 @@ twine upload dist/* ## Release Notes +### Version 1.31.2, 2024-04-28 + +- Fix bug where `parameter_statuses` fails for non-ascii encoding. +- Add support for Python 3.12 + + ### Version 1.31.1, 2024-04-01 - Move to src style layout, and also for packaging use Hatch rather than setuptools. This means that if the source distribution has a directory added to it (as is needed for packaging for OS distributions) the package can still be built. diff --git a/contrib/python/pg8000/README.md b/contrib/python/pg8000/README.md index 306734221a..05c7fb8de8 100644 --- a/contrib/python/pg8000/README.md +++ b/contrib/python/pg8000/README.md @@ -372,7 +372,26 @@ pg8000.exceptions.DatabaseError: ... ``` -instead you can write it using the [unnest +the most straightforward way to get around this problem is to rewrie the query using the [`ANY`]( +https://www.postgresql.org/docs/current/functions-comparisons.html#FUNCTIONS-COMPARISONS-ANY-SOME) +function: + +```python +>>> import pg8000.native +>>> +>>> con = pg8000.native.Connection("postgres", password="cpsnow") +>>> +>>> con.run("SELECT 'silo 1' WHERE 'a' = ANY(:v)", v=['a', 'b']) +[['silo 1']] +>>> con.close() + +``` + +However, using the array variant of `ANY` [may cause a performance problem]( +https://stackoverflow.com/questions/34627026/in-vs-any-operator-in-postgresql/34627688#34627688) +and so you can use the [subquery variant of `IN`]( +https://www.postgresql.org/docs/current/functions-subquery.html#FUNCTIONS-SUBQUERY-IN) +with the [unnest ](https://www.postgresql.org/docs/current/functions-array.html) function: ```python @@ -2040,6 +2059,12 @@ twine upload dist/* ## Release Notes +### Version 1.31.2, 2024-04-28 + +- Fix bug where `parameter_statuses` fails for non-ascii encoding. +- Add support for Python 3.12 + + ### Version 1.31.1, 2024-04-01 - Move to src style layout, and also for packaging use Hatch rather than setuptools. This means that if the source distribution has a directory added to it (as is needed for packaging for OS distributions) the package can still be built. diff --git a/contrib/python/pg8000/pg8000/core.py b/contrib/python/pg8000/pg8000/core.py index ce187baa54..230ad90901 100644 --- a/contrib/python/pg8000/pg8000/core.py +++ b/contrib/python/pg8000/pg8000/core.py @@ -16,7 +16,7 @@ from pg8000.converters import ( make_params, string_in, ) -from pg8000.exceptions import DatabaseError, Error, InterfaceError +from pg8000.exceptions import DatabaseError, InterfaceError ver = version("pg8000") @@ -385,7 +385,7 @@ class CoreConnection: if context.error is not None: raise context.error - except Error as e: + except BaseException as e: self.close() raise e @@ -850,7 +850,9 @@ class CoreConnection: def handle_PARAMETER_STATUS(self, data, context): pos = data.find(NULL_BYTE) - key, value = data[:pos].decode("ascii"), data[pos + 1 : -1].decode("ascii") + key, value = data[:pos].decode("ascii"), data[pos + 1 : -1].decode( + self._client_encoding + ) self.parameter_statuses[key] = value if key == "client_encoding": encoding = value.lower() diff --git a/contrib/python/pg8000/ya.make b/contrib/python/pg8000/ya.make index 33a1150199..9dffc62147 100644 --- a/contrib/python/pg8000/ya.make +++ b/contrib/python/pg8000/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(1.31.1) +VERSION(1.31.2) LICENSE(BSD-3-Clause) |