From 460537c7f69ea36aa9ad12ca1bfb48ea56239462 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Wed, 4 May 2022 11:29:13 +0200 Subject: [PATCH] Add missing redis modules and classes (#7676) This adds asyncio support and support for redis.typing. --- stubs/redis/@tests/stubtest_allowlist.txt | 25 ++ stubs/redis/redis/asyncio/__init__.pyi | 58 +++++ stubs/redis/redis/asyncio/client.pyi | 204 +++++++++++++++ stubs/redis/redis/asyncio/connection.pyi | 294 ++++++++++++++++++++++ stubs/redis/redis/asyncio/lock.pyi | 44 ++++ stubs/redis/redis/asyncio/retry.pyi | 11 + stubs/redis/redis/asyncio/sentinel.pyi | 61 +++++ stubs/redis/redis/asyncio/utils.pyi | 12 + stubs/redis/redis/backoff.pyi | 30 +++ stubs/redis/redis/client.pyi | 5 +- stubs/redis/redis/commands/__init__.pyi | 15 +- stubs/redis/redis/commands/core.pyi | 111 +++++++- stubs/redis/redis/commands/sentinel.pyi | 3 + stubs/redis/redis/typing.pyi | 34 +++ 14 files changed, 901 insertions(+), 6 deletions(-) create mode 100644 stubs/redis/redis/asyncio/__init__.pyi create mode 100644 stubs/redis/redis/asyncio/client.pyi create mode 100644 stubs/redis/redis/asyncio/connection.pyi create mode 100644 stubs/redis/redis/asyncio/lock.pyi create mode 100644 stubs/redis/redis/asyncio/retry.pyi create mode 100644 stubs/redis/redis/asyncio/sentinel.pyi create mode 100644 stubs/redis/redis/asyncio/utils.pyi create mode 100644 stubs/redis/redis/backoff.pyi create mode 100644 stubs/redis/redis/typing.pyi diff --git a/stubs/redis/@tests/stubtest_allowlist.txt b/stubs/redis/@tests/stubtest_allowlist.txt index f19fdc2a3572..c6f71960339a 100644 --- a/stubs/redis/@tests/stubtest_allowlist.txt +++ b/stubs/redis/@tests/stubtest_allowlist.txt @@ -4,3 +4,28 @@ redis.sentinel.Sentinel.master_for redis.sentinel.Sentinel.slave_for redis.client.Pipeline.transaction # instance attribute has same name as superclass method redis.ocsp # requires cryptography to be installed + +# TypeAlias-related problems +redis.asyncio.client.CommandStackT +redis.asyncio.client.CommandT +redis.asyncio.client.PubSubHandler +redis.asyncio.connection.ExceptionMappingT + +# Protocol-related problems +redis.asyncio.client.AsyncPubsubWorkerExceptionHandler.__init__ +redis.asyncio.client.AsyncResponseCallbackProtocol.__init__ +redis.asyncio.client.PubsubWorkerExceptionHandler.__init__ +redis.asyncio.client.ResponseCallbackProtocol.__init__ +redis.asyncio.connection.AsyncConnectCallbackProtocol.__init__ +redis.asyncio.connection.ConnectCallbackProtocol.__init__ +redis.typing.CommandsProtocol.__init__ + +# unclear problems +redis.Sentinel.master_for +redis.Sentinel.slave_for +redis.asyncio.Sentinel.master_for +redis.asyncio.Sentinel.slave_for +redis.asyncio.sentinel.Sentinel.master_for +redis.asyncio.sentinel.Sentinel.slave_for +redis.sentinel.Sentinel.master_for +redis.sentinel.Sentinel.slave_for diff --git a/stubs/redis/redis/asyncio/__init__.pyi b/stubs/redis/redis/asyncio/__init__.pyi new file mode 100644 index 000000000000..22c85ecc09ba --- /dev/null +++ b/stubs/redis/redis/asyncio/__init__.pyi @@ -0,0 +1,58 @@ +from redis.asyncio.client import Redis as Redis, StrictRedis as StrictRedis +from redis.asyncio.connection import ( + BlockingConnectionPool as BlockingConnectionPool, + Connection as Connection, + ConnectionPool as ConnectionPool, + SSLConnection as SSLConnection, + UnixDomainSocketConnection as UnixDomainSocketConnection, +) +from redis.asyncio.sentinel import ( + Sentinel as Sentinel, + SentinelConnectionPool as SentinelConnectionPool, + SentinelManagedConnection as SentinelManagedConnection, + SentinelManagedSSLConnection as SentinelManagedSSLConnection, +) +from redis.asyncio.utils import from_url as from_url +from redis.exceptions import ( + AuthenticationError as AuthenticationError, + AuthenticationWrongNumberOfArgsError as AuthenticationWrongNumberOfArgsError, + BusyLoadingError as BusyLoadingError, + ChildDeadlockedError as ChildDeadlockedError, + ConnectionError as ConnectionError, + DataError as DataError, + InvalidResponse as InvalidResponse, + PubSubError as PubSubError, + ReadOnlyError as ReadOnlyError, + RedisError as RedisError, + ResponseError as ResponseError, + TimeoutError as TimeoutError, + WatchError as WatchError, +) + +__all__ = [ + "AuthenticationError", + "AuthenticationWrongNumberOfArgsError", + "BlockingConnectionPool", + "BusyLoadingError", + "ChildDeadlockedError", + "Connection", + "ConnectionError", + "ConnectionPool", + "DataError", + "from_url", + "InvalidResponse", + "PubSubError", + "ReadOnlyError", + "Redis", + "RedisError", + "ResponseError", + "Sentinel", + "SentinelConnectionPool", + "SentinelManagedConnection", + "SentinelManagedSSLConnection", + "SSLConnection", + "StrictRedis", + "TimeoutError", + "UnixDomainSocketConnection", + "WatchError", +] diff --git a/stubs/redis/redis/asyncio/client.pyi b/stubs/redis/redis/asyncio/client.pyi new file mode 100644 index 000000000000..263903a8d2b5 --- /dev/null +++ b/stubs/redis/redis/asyncio/client.pyi @@ -0,0 +1,204 @@ +from _typeshed import Self +from collections.abc import AsyncIterator, Callable, Iterable, Mapping, MutableMapping +from typing import Any, Awaitable, Generic, NoReturn, Protocol +from typing_extensions import TypeAlias, TypedDict + +from redis.asyncio.connection import Connection, ConnectionPool +from redis.asyncio.lock import Lock +from redis.asyncio.retry import Retry +from redis.client import AbstractRedis, _StrType +from redis.commands import AsyncCoreCommands, AsyncSentinelCommands, RedisModuleCommands +from redis.typing import ChannelT, EncodableT, KeyT + +PubSubHandler: TypeAlias = Callable[[dict[str, str]], Awaitable[None]] + +class ResponseCallbackProtocol(Protocol): + def __call__(self, response: Any, **kwargs): ... + +class AsyncResponseCallbackProtocol(Protocol): + async def __call__(self, response: Any, **kwargs): ... + +ResponseCallbackT: TypeAlias = ResponseCallbackProtocol | AsyncResponseCallbackProtocol + +class Redis(AbstractRedis, RedisModuleCommands, AsyncCoreCommands[_StrType], AsyncSentinelCommands, Generic[_StrType]): + response_callbacks: MutableMapping[str | bytes, ResponseCallbackT] + @classmethod + def from_url(cls, url: str, **kwargs) -> Redis[Any]: ... + auto_close_connection_pool: Any + connection_pool: Any + single_connection_client: Any + connection: Any + def __init__( + self, + *, + host: str = ..., + port: int = ..., + db: str | int = ..., + password: str | None = ..., + socket_timeout: float | None = ..., + socket_connect_timeout: float | None = ..., + socket_keepalive: bool | None = ..., + socket_keepalive_options: Mapping[int, int | bytes] | None = ..., + connection_pool: ConnectionPool | None = ..., + unix_socket_path: str | None = ..., + encoding: str = ..., + encoding_errors: str = ..., + decode_responses: bool = ..., + retry_on_timeout: bool = ..., + ssl: bool = ..., + ssl_keyfile: str | None = ..., + ssl_certfile: str | None = ..., + ssl_cert_reqs: str = ..., + ssl_ca_certs: str | None = ..., + ssl_ca_data: str | None = ..., + ssl_check_hostname: bool = ..., + max_connections: int | None = ..., + single_connection_client: bool = ..., + health_check_interval: int = ..., + client_name: str | None = ..., + username: str | None = ..., + retry: Retry | None = ..., + auto_close_connection_pool: bool = ..., + ) -> None: ... + def __await__(self): ... + async def initialize(self: Self) -> Self: ... + def set_response_callback(self, command: str, callback: ResponseCallbackT): ... + def load_external_module(self, funcname, func) -> None: ... + def pipeline(self, transaction: bool = ..., shard_hint: str | None = ...) -> Pipeline[_StrType]: ... + async def transaction( + self, + func: Callable[[Pipeline[_StrType]], Any | Awaitable[Any]], + *watches: KeyT, + shard_hint: str | None = ..., + value_from_callable: bool = ..., + watch_delay: float | None = ..., + ): ... + def lock( + self, + name: KeyT, + timeout: float | None = ..., + sleep: float = ..., + blocking_timeout: float | None = ..., + lock_class: type[Lock] | None = ..., + thread_local: bool = ..., + ) -> Lock: ... + def pubsub(self, **kwargs) -> PubSub: ... + def monitor(self) -> Monitor: ... + def client(self) -> Redis[_StrType]: ... + async def __aenter__(self: Self) -> Self: ... + async def __aexit__(self, exc_type, exc_value, traceback) -> None: ... + def __del__(self, _warnings: Any = ...) -> None: ... + async def close(self, close_connection_pool: bool | None = ...) -> None: ... + async def execute_command(self, *args, **options): ... + async def parse_response(self, connection: Connection, command_name: str | bytes, **options): ... + +StrictRedis = Redis + +class MonitorCommandInfo(TypedDict): + time: float + db: int + client_address: str + client_port: str + client_type: str + command: str + +class Monitor: + monitor_re: Any + command_re: Any + connection_pool: Any + connection: Any + def __init__(self, connection_pool: ConnectionPool) -> None: ... + async def connect(self) -> None: ... + async def __aenter__(self): ... + async def __aexit__(self, *args) -> None: ... + async def next_command(self) -> MonitorCommandInfo: ... + async def listen(self) -> AsyncIterator[MonitorCommandInfo]: ... + +class PubSub: + PUBLISH_MESSAGE_TYPES: Any + UNSUBSCRIBE_MESSAGE_TYPES: Any + HEALTH_CHECK_MESSAGE: str + connection_pool: Any + shard_hint: Any + ignore_subscribe_messages: Any + connection: Any + encoder: Any + health_check_response: Any + channels: Any + pending_unsubscribe_channels: Any + patterns: Any + pending_unsubscribe_patterns: Any + def __init__( + self, + connection_pool: ConnectionPool, + shard_hint: str | None = ..., + ignore_subscribe_messages: bool = ..., + encoder: Any | None = ..., + ) -> None: ... + async def __aenter__(self): ... + async def __aexit__(self, exc_type, exc_value, traceback) -> None: ... + def __del__(self) -> None: ... + async def reset(self) -> None: ... + def close(self) -> Awaitable[NoReturn]: ... + async def on_connect(self, connection: Connection): ... + @property + def subscribed(self): ... + async def execute_command(self, *args: EncodableT): ... + async def parse_response(self, block: bool = ..., timeout: float = ...): ... + async def check_health(self) -> None: ... + async def psubscribe(self, *args: ChannelT, **kwargs: PubSubHandler): ... + def punsubscribe(self, *args: ChannelT) -> Awaitable[Any]: ... + async def subscribe(self, *args: ChannelT, **kwargs: Callable[..., Any]): ... + def unsubscribe(self, *args) -> Awaitable[Any]: ... + async def listen(self) -> AsyncIterator[Any]: ... + async def get_message(self, ignore_subscribe_messages: bool = ..., timeout: float = ...): ... + def ping(self, message: Any | None = ...) -> Awaitable[Any]: ... + async def handle_message(self, response, ignore_subscribe_messages: bool = ...): ... + async def run(self, *, exception_handler: PSWorkerThreadExcHandlerT | None = ..., poll_timeout: float = ...) -> None: ... + +class PubsubWorkerExceptionHandler(Protocol): + def __call__(self, e: BaseException, pubsub: PubSub): ... + +class AsyncPubsubWorkerExceptionHandler(Protocol): + async def __call__(self, e: BaseException, pubsub: PubSub): ... + +PSWorkerThreadExcHandlerT: TypeAlias = PubsubWorkerExceptionHandler | AsyncPubsubWorkerExceptionHandler +CommandT: TypeAlias = tuple[tuple[str | bytes, ...], Mapping[str, Any]] +CommandStackT: TypeAlias = list[CommandT] + +class Pipeline(Redis[_StrType], Generic[_StrType]): + UNWATCH_COMMANDS: Any + connection_pool: Any + connection: Any + response_callbacks: Any + is_transaction: Any + shard_hint: Any + watching: bool + command_stack: Any + scripts: Any + explicit_transaction: bool + def __init__( + self, + connection_pool: ConnectionPool, + response_callbacks: MutableMapping[str | bytes, ResponseCallbackT], + transaction: bool, + shard_hint: str | None, + ) -> None: ... + async def __aenter__(self: Self) -> Self: ... + async def __aexit__(self, exc_type, exc_value, traceback) -> None: ... + def __await__(self): ... + def __len__(self): ... + def __bool__(self): ... + async def reset(self) -> None: ... # type: ignore[override] + def multi(self) -> None: ... + def execute_command(self, *args, **kwargs) -> Pipeline[_StrType] | Awaitable[Pipeline[_StrType]]: ... + async def immediate_execute_command(self, *args, **options): ... + def pipeline_execute_command(self, *args, **options): ... + def raise_first_error(self, commands: CommandStackT, response: Iterable[Any]): ... + def annotate_exception(self, exception: Exception, number: int, command: Iterable[object]) -> None: ... + async def parse_response(self, connection: Connection, command_name: str | bytes, **options): ... + async def load_scripts(self) -> None: ... + async def execute(self, raise_on_error: bool = ...): ... + async def discard(self) -> None: ... + async def watch(self, *names: KeyT): ... + async def unwatch(self): ... diff --git a/stubs/redis/redis/asyncio/connection.pyi b/stubs/redis/redis/asyncio/connection.pyi new file mode 100644 index 000000000000..96c8b82d863d --- /dev/null +++ b/stubs/redis/redis/asyncio/connection.pyi @@ -0,0 +1,294 @@ +import asyncio +import enum +import ssl +from collections.abc import Callable, Iterable, Mapping +from typing import Any, Protocol +from typing_extensions import TypeAlias, TypedDict + +from redis.asyncio.retry import Retry +from redis.exceptions import ResponseError +from redis.typing import EncodableT, EncodedT + +hiredis: Any +NONBLOCKING_EXCEPTION_ERROR_NUMBERS: Any +NONBLOCKING_EXCEPTIONS: Any +SYM_STAR: bytes +SYM_DOLLAR: bytes +SYM_CRLF: bytes +SYM_LF: bytes +SYM_EMPTY: bytes +SERVER_CLOSED_CONNECTION_ERROR: str + +class _Sentinel(enum.Enum): + sentinel: Any + +SENTINEL: Any +MODULE_LOAD_ERROR: str +NO_SUCH_MODULE_ERROR: str +MODULE_UNLOAD_NOT_POSSIBLE_ERROR: str +MODULE_EXPORTS_DATA_TYPES_ERROR: str + +class _HiredisReaderArgs(TypedDict): + protocolError: Callable[[str], Exception] + replyError: Callable[[str], Exception] + encoding: str | None + errors: str | None + +class Encoder: + encoding: Any + encoding_errors: Any + decode_responses: Any + def __init__(self, encoding: str, encoding_errors: str, decode_responses: bool) -> None: ... + def encode(self, value: EncodableT) -> EncodedT: ... + def decode(self, value: EncodableT, force: bool = ...) -> EncodableT: ... + +ExceptionMappingT: TypeAlias = Mapping[str, type[Exception] | Mapping[str, type[Exception]]] + +class BaseParser: + EXCEPTION_CLASSES: ExceptionMappingT + def __init__(self, socket_read_size: int) -> None: ... + def __del__(self) -> None: ... + def parse_error(self, response: str) -> ResponseError: ... + def on_disconnect(self) -> None: ... + def on_connect(self, connection: Connection): ... + async def can_read(self, timeout: float) -> bool: ... + async def read_response(self, disable_decoding: bool = ...) -> EncodableT | ResponseError | list[EncodableT] | None: ... + +class SocketBuffer: + socket_read_size: Any + socket_timeout: Any + bytes_written: int + bytes_read: int + def __init__(self, stream_reader: asyncio.StreamReader, socket_read_size: int, socket_timeout: float | None) -> None: ... + @property + def length(self): ... + async def can_read(self, timeout: float) -> bool: ... + async def read(self, length: int) -> bytes: ... + async def readline(self) -> bytes: ... + def purge(self) -> None: ... + def close(self) -> None: ... + +class PythonParser(BaseParser): + encoder: Any + def __init__(self, socket_read_size: int) -> None: ... + def on_connect(self, connection: Connection): ... + def on_disconnect(self) -> None: ... + async def can_read(self, timeout: float): ... + async def read_response(self, disable_decoding: bool = ...) -> EncodableT | ResponseError | None: ... + +class HiredisParser(BaseParser): + def __init__(self, socket_read_size: int) -> None: ... + def on_connect(self, connection: Connection): ... + def on_disconnect(self) -> None: ... + async def can_read(self, timeout: float): ... + async def read_from_socket(self, timeout: float | None | _Sentinel = ..., raise_on_timeout: bool = ...): ... + async def read_response(self, disable_decoding: bool = ...) -> EncodableT | list[EncodableT]: ... + +DefaultParser: type[PythonParser | HiredisParser] + +class ConnectCallbackProtocol(Protocol): + def __call__(self, connection: Connection): ... + +class AsyncConnectCallbackProtocol(Protocol): + async def __call__(self, connection: Connection): ... + +ConnectCallbackT: TypeAlias = ConnectCallbackProtocol | AsyncConnectCallbackProtocol + +class Connection: + pid: Any + host: Any + port: Any + db: Any + username: Any + client_name: Any + password: Any + socket_timeout: Any + socket_connect_timeout: Any + socket_keepalive: Any + socket_keepalive_options: Any + socket_type: Any + retry_on_timeout: Any + retry: Any + health_check_interval: Any + next_health_check: int + ssl_context: Any + encoder: Any + redis_connect_func: Any + def __init__( + self, + *, + host: str = ..., + port: str | int = ..., + db: str | int = ..., + password: str | None = ..., + socket_timeout: float | None = ..., + socket_connect_timeout: float | None = ..., + socket_keepalive: bool = ..., + socket_keepalive_options: Mapping[int, int | bytes] | None = ..., + socket_type: int = ..., + retry_on_timeout: bool = ..., + encoding: str = ..., + encoding_errors: str = ..., + decode_responses: bool = ..., + parser_class: type[BaseParser] = ..., + socket_read_size: int = ..., + health_check_interval: float = ..., + client_name: str | None = ..., + username: str | None = ..., + retry: Retry | None = ..., + redis_connect_func: ConnectCallbackT | None = ..., + encoder_class: type[Encoder] = ..., + ) -> None: ... + def repr_pieces(self): ... + def __del__(self) -> None: ... + @property + def is_connected(self): ... + def register_connect_callback(self, callback) -> None: ... + def clear_connect_callbacks(self) -> None: ... + def set_parser(self, parser_class) -> None: ... + async def connect(self) -> None: ... + async def on_connect(self) -> None: ... + async def disconnect(self) -> None: ... + async def check_health(self) -> None: ... + async def send_packed_command(self, command: bytes | str | Iterable[bytes], check_health: bool = ...): ... + async def send_command(self, *args, **kwargs) -> None: ... + async def can_read(self, timeout: float = ...): ... + async def read_response(self, disable_decoding: bool = ...): ... + def pack_command(self, *args: EncodableT) -> list[bytes]: ... + def pack_commands(self, commands: Iterable[Iterable[EncodableT]]) -> list[bytes]: ... + +class SSLConnection(Connection): + ssl_context: Any + def __init__( + self, + ssl_keyfile: str | None = ..., + ssl_certfile: str | None = ..., + ssl_cert_reqs: str = ..., + ssl_ca_certs: str | None = ..., + ssl_ca_data: str | None = ..., + ssl_check_hostname: bool = ..., + **kwargs, + ) -> None: ... + @property + def keyfile(self): ... + @property + def certfile(self): ... + @property + def cert_reqs(self): ... + @property + def ca_certs(self): ... + @property + def ca_data(self): ... + @property + def check_hostname(self): ... + +class RedisSSLContext: + keyfile: Any + certfile: Any + cert_reqs: Any + ca_certs: Any + ca_data: Any + check_hostname: Any + context: Any + def __init__( + self, + keyfile: str | None = ..., + certfile: str | None = ..., + cert_reqs: str | None = ..., + ca_certs: str | None = ..., + ca_data: str | None = ..., + check_hostname: bool = ..., + ) -> None: ... + def get(self) -> ssl.SSLContext: ... + +class UnixDomainSocketConnection(Connection): + pid: Any + path: Any + db: Any + username: Any + client_name: Any + password: Any + socket_timeout: Any + socket_connect_timeout: Any + retry_on_timeout: Any + retry: Any + health_check_interval: Any + next_health_check: int + redis_connect_func: Any + encoder: Any + def __init__( + self, + *, + path: str = ..., + db: str | int = ..., + username: str | None = ..., + password: str | None = ..., + socket_timeout: float | None = ..., + socket_connect_timeout: float | None = ..., + encoding: str = ..., + encoding_errors: str = ..., + decode_responses: bool = ..., + retry_on_timeout: bool = ..., + parser_class: type[BaseParser] = ..., + socket_read_size: int = ..., + health_check_interval: float = ..., + client_name: str | None = ..., + retry: Retry | None = ..., + redis_connect_func: Any | None = ..., + ) -> None: ... + def repr_pieces(self) -> Iterable[tuple[str, str | int]]: ... + +FALSE_STRINGS: Any + +def to_bool(value) -> bool | None: ... + +URL_QUERY_ARGUMENT_PARSERS: Mapping[str, Callable[..., object]] + +class ConnectKwargs(TypedDict): + username: str + password: str + connection_class: type[Connection] + host: str + port: int + db: int + path: str + +def parse_url(url: str) -> ConnectKwargs: ... + +class ConnectionPool: + @classmethod + def from_url(cls, url: str, **kwargs) -> ConnectionPool: ... + connection_class: Any + connection_kwargs: Any + max_connections: Any + encoder_class: Any + def __init__( + self, connection_class: type[Connection] = ..., max_connections: int | None = ..., **connection_kwargs + ) -> None: ... + pid: Any + def reset(self) -> None: ... + async def get_connection(self, command_name, *keys, **options): ... + def get_encoder(self): ... + def make_connection(self): ... + async def release(self, connection: Connection): ... + def owns_connection(self, connection: Connection): ... + async def disconnect(self, inuse_connections: bool = ...): ... + +class BlockingConnectionPool(ConnectionPool): + queue_class: Any + timeout: Any + def __init__( + self, + max_connections: int = ..., + timeout: int | None = ..., + connection_class: type[Connection] = ..., + queue_class: type[asyncio.Queue[Any]] = ..., + **connection_kwargs, + ) -> None: ... + pool: Any + pid: Any + def reset(self) -> None: ... + def make_connection(self): ... + async def get_connection(self, command_name, *keys, **options): ... + async def release(self, connection: Connection): ... + async def disconnect(self, inuse_connections: bool = ...): ... diff --git a/stubs/redis/redis/asyncio/lock.pyi b/stubs/redis/redis/asyncio/lock.pyi new file mode 100644 index 000000000000..579cd08df407 --- /dev/null +++ b/stubs/redis/redis/asyncio/lock.pyi @@ -0,0 +1,44 @@ +from typing import Any, Awaitable + +from redis.asyncio import Redis + +class Lock: + lua_release: Any + lua_extend: Any + lua_reacquire: Any + LUA_RELEASE_SCRIPT: str + LUA_EXTEND_SCRIPT: str + LUA_REACQUIRE_SCRIPT: str + redis: Any + name: Any + timeout: Any + sleep: Any + blocking: Any + blocking_timeout: Any + thread_local: Any + local: Any + def __init__( + self, + redis: Redis[Any], + name: str | bytes | memoryview, + timeout: float | None = ..., + sleep: float = ..., + blocking: bool = ..., + blocking_timeout: float | None = ..., + thread_local: bool = ..., + ) -> None: ... + def register_scripts(self) -> None: ... + async def __aenter__(self): ... + async def __aexit__(self, exc_type, exc_value, traceback) -> None: ... + async def acquire( + self, blocking: bool | None = ..., blocking_timeout: float | None = ..., token: str | bytes | None = ... + ): ... + async def do_acquire(self, token: str | bytes) -> bool: ... + async def locked(self) -> bool: ... + async def owned(self) -> bool: ... + def release(self) -> Awaitable[None]: ... + async def do_release(self, expected_token: bytes): ... + def extend(self, additional_time: float, replace_ttl: bool = ...) -> Awaitable[bool]: ... + async def do_extend(self, additional_time, replace_ttl) -> bool: ... + def reacquire(self) -> Awaitable[bool]: ... + async def do_reacquire(self) -> bool: ... diff --git a/stubs/redis/redis/asyncio/retry.pyi b/stubs/redis/redis/asyncio/retry.pyi new file mode 100644 index 000000000000..48135f5049c1 --- /dev/null +++ b/stubs/redis/redis/asyncio/retry.pyi @@ -0,0 +1,11 @@ +from collections.abc import Callable +from typing import Any, Awaitable, TypeVar + +from redis.backoff import AbstractBackoff +from redis.exceptions import RedisError + +_T = TypeVar("_T") + +class Retry: + def __init__(self, backoff: AbstractBackoff, retries: int, supported_errors: tuple[type[RedisError], ...] = ...) -> None: ... + async def call_with_retry(self, do: Callable[[], Awaitable[_T]], fail: Callable[[RedisError], Any]) -> _T: ... diff --git a/stubs/redis/redis/asyncio/sentinel.pyi b/stubs/redis/redis/asyncio/sentinel.pyi new file mode 100644 index 000000000000..59b31e0c5615 --- /dev/null +++ b/stubs/redis/redis/asyncio/sentinel.pyi @@ -0,0 +1,61 @@ +from collections.abc import AsyncIterator, Iterable, Mapping, Sequence +from typing import Any + +from redis.asyncio.client import Redis +from redis.asyncio.connection import Connection, ConnectionPool, SSLConnection +from redis.commands import AsyncSentinelCommands +from redis.exceptions import ConnectionError +from redis.typing import EncodableT + +class MasterNotFoundError(ConnectionError): ... +class SlaveNotFoundError(ConnectionError): ... + +class SentinelManagedConnection(Connection): + connection_pool: Any + def __init__(self, **kwargs) -> None: ... + async def connect_to(self, address) -> None: ... + async def connect(self): ... + async def read_response(self, disable_decoding: bool = ...): ... + +class SentinelManagedSSLConnection(SentinelManagedConnection, SSLConnection): ... + +class SentinelConnectionPool(ConnectionPool): + is_master: Any + check_connection: Any + service_name: Any + sentinel_manager: Any + master_address: Any + slave_rr_counter: Any + def __init__(self, service_name, sentinel_manager, **kwargs) -> None: ... + def reset(self) -> None: ... + def owns_connection(self, connection: Connection): ... + async def get_master_address(self): ... + async def rotate_slaves(self) -> AsyncIterator[Any]: ... + +class Sentinel(AsyncSentinelCommands): + sentinel_kwargs: Any + sentinels: Any + min_other_sentinels: Any + connection_kwargs: Any + def __init__( + self, sentinels, min_other_sentinels: int = ..., sentinel_kwargs: Any | None = ..., **connection_kwargs + ) -> None: ... + async def execute_command(self, *args, **kwargs): ... + def check_master_state(self, state: dict[Any, Any], service_name: str) -> bool: ... + async def discover_master(self, service_name: str): ... + def filter_slaves(self, slaves: Iterable[Mapping[Any, Any]]) -> Sequence[tuple[EncodableT, EncodableT]]: ... + async def discover_slaves(self, service_name: str) -> Sequence[tuple[EncodableT, EncodableT]]: ... + def master_for( + self, + service_name: str, + redis_class: type[Redis[Any]] = ..., + connection_pool_class: type[SentinelConnectionPool] = ..., + **kwargs, + ): ... + def slave_for( + self, + service_name: str, + redis_class: type[Redis[Any]] = ..., + connection_pool_class: type[SentinelConnectionPool] = ..., + **kwargs, + ): ... diff --git a/stubs/redis/redis/asyncio/utils.pyi b/stubs/redis/redis/asyncio/utils.pyi new file mode 100644 index 000000000000..f63aa2dae43e --- /dev/null +++ b/stubs/redis/redis/asyncio/utils.pyi @@ -0,0 +1,12 @@ +from typing import Any, Generic + +from redis.asyncio.client import Pipeline, Redis +from redis.client import _StrType + +def from_url(url: str, **kwargs) -> Redis[Any]: ... + +class pipeline(Generic[_StrType]): + p: Pipeline[_StrType] + def __init__(self, redis_obj: Redis[_StrType]) -> None: ... + async def __aenter__(self) -> Pipeline[_StrType]: ... + async def __aexit__(self, exc_type: object, exc_value: object, traceback: object) -> None: ... diff --git a/stubs/redis/redis/backoff.pyi b/stubs/redis/redis/backoff.pyi new file mode 100644 index 000000000000..d10aaabb3987 --- /dev/null +++ b/stubs/redis/redis/backoff.pyi @@ -0,0 +1,30 @@ +from abc import ABC, abstractmethod + +class AbstractBackoff(ABC): + def reset(self): ... + @abstractmethod + def compute(self, failures): ... + +class ConstantBackoff(AbstractBackoff): + def __init__(self, backoff) -> None: ... + def compute(self, failures): ... + +class NoBackoff(ConstantBackoff): + def __init__(self) -> None: ... + +class ExponentialBackoff(AbstractBackoff): + def __init__(self, cap, base) -> None: ... + def compute(self, failures): ... + +class FullJitterBackoff(AbstractBackoff): + def __init__(self, cap, base) -> None: ... + def compute(self, failures): ... + +class EqualJitterBackoff(AbstractBackoff): + def __init__(self, cap, base) -> None: ... + def compute(self, failures): ... + +class DecorrelatedJitterBackoff(AbstractBackoff): + def __init__(self, cap, base) -> None: ... + def reset(self) -> None: ... + def compute(self, failures): ... diff --git a/stubs/redis/redis/client.pyi b/stubs/redis/redis/client.pyi index a609b782dfcc..72a8922afa15 100644 --- a/stubs/redis/redis/client.pyi +++ b/stubs/redis/redis/client.pyi @@ -69,7 +69,10 @@ def parse_slowlog_get(response, **options): ... _LockType = TypeVar("_LockType") -class Redis(RedisModuleCommands, CoreCommands[_StrType], SentinelCommands, Generic[_StrType]): +class AbstractRedis: + RESPONSE_CALLBACKS: dict[Any, Any] + +class Redis(AbstractRedis, RedisModuleCommands, CoreCommands[_StrType], SentinelCommands, Generic[_StrType]): RESPONSE_CALLBACKS: Any @overload @classmethod diff --git a/stubs/redis/redis/commands/__init__.pyi b/stubs/redis/redis/commands/__init__.pyi index 5ab5e2219907..4959ea0fdf15 100644 --- a/stubs/redis/redis/commands/__init__.pyi +++ b/stubs/redis/redis/commands/__init__.pyi @@ -1,8 +1,17 @@ from .cluster import RedisClusterCommands as RedisClusterCommands -from .core import CoreCommands as CoreCommands +from .core import AsyncCoreCommands as AsyncCoreCommands, CoreCommands as CoreCommands from .helpers import list_or_args as list_or_args from .parser import CommandsParser as CommandsParser from .redismodules import RedisModuleCommands as RedisModuleCommands -from .sentinel import SentinelCommands as SentinelCommands +from .sentinel import AsyncSentinelCommands as AsyncSentinelCommands, SentinelCommands as SentinelCommands -__all__ = ["RedisClusterCommands", "CommandsParser", "CoreCommands", "list_or_args", "RedisModuleCommands", "SentinelCommands"] +__all__ = [ + "RedisClusterCommands", + "CommandsParser", + "AsyncCoreCommands", + "CoreCommands", + "list_or_args", + "RedisModuleCommands", + "AsyncSentinelCommands", + "SentinelCommands", +] diff --git a/stubs/redis/redis/commands/core.pyi b/stubs/redis/redis/commands/core.pyi index 6fac586c8ce9..a3af493284da 100644 --- a/stubs/redis/redis/commands/core.pyi +++ b/stubs/redis/redis/commands/core.pyi @@ -1,10 +1,12 @@ import builtins -from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence +from collections.abc import AsyncIterator, Callable, Iterable, Iterator, Mapping, Sequence from datetime import datetime, timedelta -from typing import Any, Generic, TypeVar, overload +from typing import Any, Awaitable, Generic, TypeVar, overload from typing_extensions import Literal +from ..asyncio.client import Redis as AsyncRedis from ..client import _CommandOptions, _Key, _Value +from ..typing import EncodableT, KeyT, PatternT, ScriptTextT _ScoreCastFuncReturn = TypeVar("_ScoreCastFuncReturn") _StrType = TypeVar("_StrType", bound=str | bytes) @@ -38,6 +40,8 @@ class ACLCommands(Generic[_StrType]): def acl_users(self, **kwargs: _CommandOptions) -> list[str]: ... def acl_whoami(self, **kwargs: _CommandOptions) -> str: ... +AsyncACLCommands = ACLCommands + class ManagementCommands: def bgrewriteaof(self, **kwargs: _CommandOptions): ... def bgsave(self, schedule: bool = ..., **kwargs: _CommandOptions): ... @@ -135,6 +139,13 @@ class ManagementCommands: def time(self, **kwargs: _CommandOptions): ... def wait(self, num_replicas, timeout, **kwargs: _CommandOptions): ... +class AsyncManagementCommands(ManagementCommands): + async def command_info(self, **kwargs) -> None: ... + async def debug_segfault(self, **kwargs) -> None: ... + async def memory_doctor(self, **kwargs) -> None: ... + async def memory_help(self, **kwargs) -> None: ... + async def shutdown(self, save: bool = ..., nosave: bool = ..., **kwargs) -> None: ... # type: ignore[override] + class BasicKeyCommands(Generic[_StrType]): def append(self, key, value): ... def bitcount(self, key: _Key, start: int | None = ..., end: int | None = ..., mode: str | None = ...) -> int: ... @@ -242,6 +253,14 @@ class BasicKeyCommands(Generic[_StrType]): def unwatch(self): ... def unlink(self, *names: _Key) -> int: ... +class AsyncBasicKeyCommands(BasicKeyCommands[_StrType], Generic[_StrType]): + def __delitem__(self, name: KeyT): ... + def __contains__(self, name: KeyT): ... # type: ignore[override] + def __getitem__(self, name: KeyT): ... + def __setitem__(self, name: KeyT, value: EncodableT): ... + async def watch(self, *names: KeyT) -> None: ... + async def unwatch(self) -> None: ... + class ListCommands(Generic[_StrType]): @overload def blpop(self, keys: _Value | Iterable[_Value], timeout: Literal[0] | None = ...) -> tuple[_StrType, _StrType]: ... @@ -310,6 +329,8 @@ class ListCommands(Generic[_StrType]): groups: bool = ..., ) -> int: ... +AsyncListCommands = ListCommands + class ScanCommands(Generic[_StrType]): def scan( self, @@ -371,6 +392,26 @@ class SetCommands(Generic[_StrType]): def sunion(self, keys: _Key | Iterable[_Key], *args: _Key) -> builtins.set[_Value]: ... def sunionstore(self, dest: _Key, keys: _Key | Iterable[_Key], *args: _Key) -> int: ... +class AsyncScanCommands(ScanCommands[_StrType], Generic[_StrType]): + async def scan_iter( # type: ignore[override] + self, match: PatternT | None = ..., count: int | None = ..., _type: str | None = ..., **kwargs + ) -> AsyncIterator[Any]: ... + async def sscan_iter( # type: ignore[override] + self, name: KeyT, match: PatternT | None = ..., count: int | None = ... + ) -> AsyncIterator[Any]: ... + async def hscan_iter( # type: ignore[override] + self, name: str, match: PatternT | None = ..., count: int | None = ... + ) -> AsyncIterator[Any]: ... + async def zscan_iter( # type: ignore[override] + self, + name: KeyT, + match: PatternT | None = ..., + count: int | None = ..., + score_cast_func: type[Any] | Callable[..., Any] = ..., + ) -> AsyncIterator[Any]: ... + +AsyncSetCommands = SetCommands + class StreamCommands: def xack(self, name, groupname, *ids): ... def xadd( @@ -412,6 +453,8 @@ class StreamCommands: def xrevrange(self, name, max: str = ..., min: str = ..., count: Any | None = ...): ... def xtrim(self, name, maxlen: int, approximate: bool = ..., minid: Any | None = ..., limit: Any | None = ...): ... +AsyncStreamCommands = StreamCommands + class SortedSetCommands(Generic[_StrType]): def zadd( self, @@ -574,11 +617,15 @@ class SortedSetCommands(Generic[_StrType]): def zunionstore(self, dest: _Key, keys: Iterable[_Key], aggregate: Literal["SUM", "MIN", "MAX"] | None = ...) -> int: ... def zmscore(self, key, members): ... +AsyncSortedSetCommands = SortedSetCommands + class HyperlogCommands: def pfadd(self, name: _Key, *values: _Value) -> int: ... def pfcount(self, name: _Key) -> int: ... def pfmerge(self, dest: _Key, *sources: _Key) -> bool: ... +AsyncHyperlogCommands = HyperlogCommands + class HashCommands(Generic[_StrType]): def hdel(self, name: _Key, *keys: _Key) -> int: ... def hexists(self, name: _Key, key: _Key) -> bool: ... @@ -602,12 +649,22 @@ class HashCommands(Generic[_StrType]): def hvals(self, name: _Key) -> list[_StrType]: ... def hstrlen(self, name, key): ... +AsyncHashCommands = HashCommands + +class AsyncScript: + def __init__(self, registered_client: AsyncRedis[Any], script: ScriptTextT) -> None: ... + async def __call__( + self, keys: Sequence[KeyT] | None = ..., args: Iterable[EncodableT] | None = ..., client: AsyncRedis[Any] | None = ... + ): ... + class PubSubCommands: def publish(self, channel: _Key, message: _Key, **kwargs: _CommandOptions) -> int: ... def pubsub_channels(self, pattern: _Key = ..., **kwargs: _CommandOptions) -> list[str]: ... def pubsub_numpat(self, **kwargs: _CommandOptions) -> int: ... def pubsub_numsub(self, *args: _Key, **kwargs: _CommandOptions) -> list[tuple[str, int]]: ... +AsyncPubSubCommands = PubSubCommands + class ScriptCommands(Generic[_StrType]): def eval(self, script, numkeys, *keys_and_args): ... def evalsha(self, sha, numkeys, *keys_and_args): ... @@ -618,6 +675,10 @@ class ScriptCommands(Generic[_StrType]): def script_load(self, script): ... def register_script(self, script: str | _StrType) -> Script: ... +class AsyncScriptCommands(ScriptCommands[_StrType], Generic[_StrType]): + async def script_debug(self, *args) -> None: ... + def register_script(self, script: ScriptTextT) -> AsyncScript: ... # type: ignore[override] + class GeoCommands: def geoadd(self, name, values, nx: bool = ..., xx: bool = ..., ch: bool = ...): ... def geodist(self, name, place1, place2, unit: Any | None = ...): ... @@ -688,6 +749,8 @@ class GeoCommands: storedist: bool = ..., ): ... +AsyncGeoCommands = GeoCommands + class ModuleCommands: def module_load(self, path, *args): ... def module_unload(self, name): ... @@ -712,11 +775,32 @@ class BitFieldOperation: def command(self): ... def execute(self): ... +class AsyncModuleCommands(ModuleCommands): + async def command_info(self) -> None: ... + class ClusterCommands: def cluster(self, cluster_arg: str, *args, **kwargs: _CommandOptions): ... def readwrite(self, **kwargs: _CommandOptions) -> bool: ... def readonly(self, **kwargs: _CommandOptions) -> bool: ... +AsyncClusterCommands = ClusterCommands + +class FunctionCommands: + def function_load( + self, engine: str, library: str, code: str, replace: bool | None = ..., description: str | None = ... + ) -> Awaitable[str] | str: ... + def function_delete(self, library: str) -> Awaitable[str] | str: ... + def function_flush(self, mode: str = ...) -> Awaitable[str] | str: ... + def function_list(self, library: str | None = ..., withcode: bool | None = ...) -> Awaitable[list[Any]] | list[Any]: ... + def fcall(self, function, numkeys: int, *keys_and_args: list[Any] | None) -> Awaitable[str] | str: ... + def fcall_ro(self, function, numkeys: int, *keys_and_args: list[Any] | None) -> Awaitable[str] | str: ... + def function_dump(self) -> Awaitable[str] | str: ... + def function_restore(self, payload: str, policy: str | None = ...) -> Awaitable[str] | str: ... + def function_kill(self) -> Awaitable[str] | str: ... + def function_stats(self) -> Awaitable[list[Any]] | list[Any]: ... + +AsyncFunctionCommands = FunctionCommands + class DataAccessCommands( BasicKeyCommands[_StrType], HyperlogCommands, @@ -729,6 +813,18 @@ class DataAccessCommands( SortedSetCommands[_StrType], Generic[_StrType], ): ... +class AsyncDataAccessCommands( + AsyncBasicKeyCommands[_StrType], + AsyncHyperlogCommands, + AsyncHashCommands[_StrType], + AsyncGeoCommands, + AsyncListCommands[_StrType], + AsyncScanCommands[_StrType], + AsyncSetCommands[_StrType], + AsyncStreamCommands, + AsyncSortedSetCommands[_StrType], + Generic[_StrType], +): ... class CoreCommands( ACLCommands[_StrType], ClusterCommands, @@ -739,3 +835,14 @@ class CoreCommands( ScriptCommands[_StrType], Generic[_StrType], ): ... +class AsyncCoreCommands( + AsyncACLCommands[_StrType], + AsyncClusterCommands, + AsyncDataAccessCommands[_StrType], + AsyncManagementCommands, + AsyncModuleCommands, + AsyncPubSubCommands, + AsyncScriptCommands[_StrType], + AsyncFunctionCommands, + Generic[_StrType], +): ... diff --git a/stubs/redis/redis/commands/sentinel.pyi b/stubs/redis/redis/commands/sentinel.pyi index 545642fac979..b526a45f14e3 100644 --- a/stubs/redis/redis/commands/sentinel.pyi +++ b/stubs/redis/redis/commands/sentinel.pyi @@ -12,3 +12,6 @@ class SentinelCommands: def sentinel_failover(self, new_master_name): ... def sentinel_ckquorum(self, new_master_name): ... def sentinel_flushconfig(self): ... + +class AsyncSentinelCommands(SentinelCommands): + async def sentinel(self, *args) -> None: ... diff --git a/stubs/redis/redis/typing.pyi b/stubs/redis/redis/typing.pyi new file mode 100644 index 000000000000..bbce081fc37a --- /dev/null +++ b/stubs/redis/redis/typing.pyi @@ -0,0 +1,34 @@ +from collections.abc import Iterable +from datetime import datetime, timedelta +from typing import Protocol, TypeVar +from typing_extensions import TypeAlias + +from redis.asyncio.connection import ConnectionPool as AsyncConnectionPool +from redis.connection import ConnectionPool + +# The following type aliases exist at runtime. +EncodedT: TypeAlias = bytes | memoryview +DecodedT: TypeAlias = str | int | float +EncodableT: TypeAlias = EncodedT | DecodedT +AbsExpiryT: TypeAlias = int | datetime +ExpiryT: TypeAlias = float | timedelta +ZScoreBoundT: TypeAlias = float | str +BitfieldOffsetT: TypeAlias = int | str +_StringLikeT: TypeAlias = bytes | str | memoryview +KeyT: TypeAlias = _StringLikeT +PatternT: TypeAlias = _StringLikeT +FieldT: TypeAlias = EncodableT +KeysT: TypeAlias = KeyT | Iterable[KeyT] +ChannelT: TypeAlias = _StringLikeT +GroupT: TypeAlias = _StringLikeT +ConsumerT: TypeAlias = _StringLikeT +StreamIdT: TypeAlias = int | _StringLikeT +ScriptTextT: TypeAlias = _StringLikeT +TimeoutSecT: TypeAlias = int | float | _StringLikeT +AnyKeyT = TypeVar("AnyKeyT", bytes, str, memoryview) # noqa: Y001 +AnyFieldT = TypeVar("AnyFieldT", bytes, str, memoryview) # noqa: Y001 +AnyChannelT = TypeVar("AnyChannelT", bytes, str, memoryview) # noqa: Y001 + +class CommandsProtocol(Protocol): + connection_pool: AsyncConnectionPool | ConnectionPool + def execute_command(self, *args, **options): ...