Full Code of redis/redis-py for AI

master f00b9db39407 cached
372 files
5.7 MB
1.5M tokens
8361 symbols
1 requests
Download .txt
Showing preview only (6,082K chars total). Download the full file or copy to clipboard to get everything.
Repository: redis/redis-py
Branch: master
Commit: f00b9db39407
Files: 372
Total size: 5.7 MB

Directory structure:
gitextract_8s9rmjqg/

├── .agent/
│   └── sync_async_type_hints_overload_guide.md
├── .dockerignore
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── actions/
│   │   └── run-tests/
│   │       └── action.yml
│   ├── dependabot.yml
│   ├── release-drafter-config.yml
│   ├── spellcheck-settings.yml
│   ├── wordlist.txt
│   └── workflows/
│       ├── codeql-analysis.yml
│       ├── docs.yaml
│       ├── hiredis-py-integration.yaml
│       ├── install_and_test.sh
│       ├── integration.yaml
│       ├── pypi-publish.yaml
│       ├── release-drafter.yml
│       ├── spellcheck.yml
│       └── stale-issues.yml
├── .gitignore
├── .mypy.ini
├── .readthedocs.yml
├── CHANGES
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── benchmarks/
│   ├── __init__.py
│   ├── base.py
│   ├── basic_operations.py
│   ├── cluster_async.py
│   ├── cluster_async_pipeline.py
│   ├── command_packer_benchmark.py
│   ├── otel_benchmark.py
│   └── socket_read_size.py
├── codecov.yml
├── dev_requirements.txt
├── docker-compose.yml
├── dockers/
│   └── sentinel.conf
├── docs/
│   ├── Makefile
│   ├── _static/
│   │   └── .keep
│   ├── _templates/
│   │   └── .keep
│   ├── advanced_features.rst
│   ├── backoff.rst
│   ├── clustering.rst
│   ├── commands.rst
│   ├── conf.py
│   ├── connections.rst
│   ├── examples/
│   │   ├── README.md
│   │   ├── asyncio_examples.ipynb
│   │   ├── connection_examples.ipynb
│   │   ├── opentelemetry/
│   │   │   ├── README.md
│   │   │   ├── config/
│   │   │   │   ├── alertmanager.yml
│   │   │   │   ├── otel-collector.yaml
│   │   │   │   └── vector.toml
│   │   │   ├── docker-compose.yml
│   │   │   ├── main.py
│   │   │   ├── requirements.txt
│   │   │   └── uptrace.yml
│   │   ├── opentelemetry_api_examples.ipynb
│   │   ├── pipeline_examples.ipynb
│   │   ├── redis-stream-example.ipynb
│   │   ├── search_json_examples.ipynb
│   │   ├── search_vector_similarity_examples.ipynb
│   │   ├── set_and_get_examples.ipynb
│   │   ├── ssl_connection_examples.ipynb
│   │   └── timeseries_examples.ipynb
│   ├── examples.rst
│   ├── exceptions.rst
│   ├── genindex.rst
│   ├── geographic_failover.rst
│   ├── index.rst
│   ├── lock.rst
│   ├── lua_scripting.rst
│   ├── opentelemetry.rst
│   ├── redismodules.rst
│   ├── requirements.txt
│   ├── resp3_features.rst
│   └── retry.rst
├── doctests/
│   ├── README.md
│   ├── cmds_cnxmgmt.py
│   ├── cmds_generic.py
│   ├── cmds_hash.py
│   ├── cmds_list.py
│   ├── cmds_servermgmt.py
│   ├── cmds_set.py
│   ├── cmds_sorted_set.py
│   ├── cmds_string.py
│   ├── data/
│   │   ├── query_em.json
│   │   └── query_vector.json
│   ├── dt_bitfield.py
│   ├── dt_bitmap.py
│   ├── dt_bloom.py
│   ├── dt_cms.py
│   ├── dt_cuckoo.py
│   ├── dt_geo.py
│   ├── dt_hash.py
│   ├── dt_hll.py
│   ├── dt_json.py
│   ├── dt_list.py
│   ├── dt_set.py
│   ├── dt_ss.py
│   ├── dt_stream.py
│   ├── dt_string.py
│   ├── dt_tdigest.py
│   ├── dt_time_series.py
│   ├── dt_topk.py
│   ├── dt_vec_set.py
│   ├── geo_index.py
│   ├── home_json.py
│   ├── home_prob_dts.py
│   ├── query_agg.py
│   ├── query_combined.py
│   ├── query_em.py
│   ├── query_ft.py
│   ├── query_geo.py
│   ├── query_range.py
│   ├── requirements.txt
│   ├── run_examples.sh
│   ├── search_quickstart.py
│   ├── search_vss.py
│   ├── string_set_get.py
│   └── trans_pipe.py
├── pyproject.toml
├── redis/
│   ├── __init__.py
│   ├── _parsers/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── commands.py
│   │   ├── encoders.py
│   │   ├── helpers.py
│   │   ├── hiredis.py
│   │   ├── resp2.py
│   │   ├── resp3.py
│   │   └── socket.py
│   ├── asyncio/
│   │   ├── __init__.py
│   │   ├── client.py
│   │   ├── cluster.py
│   │   ├── connection.py
│   │   ├── http/
│   │   │   ├── __init__.py
│   │   │   └── http_client.py
│   │   ├── lock.py
│   │   ├── multidb/
│   │   │   ├── __init__.py
│   │   │   ├── client.py
│   │   │   ├── command_executor.py
│   │   │   ├── config.py
│   │   │   ├── database.py
│   │   │   ├── event.py
│   │   │   ├── failover.py
│   │   │   ├── failure_detector.py
│   │   │   └── healthcheck.py
│   │   ├── observability/
│   │   │   ├── __init__.py
│   │   │   └── recorder.py
│   │   ├── retry.py
│   │   ├── sentinel.py
│   │   └── utils.py
│   ├── auth/
│   │   ├── __init__.py
│   │   ├── err.py
│   │   ├── idp.py
│   │   ├── token.py
│   │   └── token_manager.py
│   ├── background.py
│   ├── backoff.py
│   ├── cache.py
│   ├── client.py
│   ├── cluster.py
│   ├── commands/
│   │   ├── __init__.py
│   │   ├── bf/
│   │   │   ├── __init__.py
│   │   │   ├── commands.py
│   │   │   └── info.py
│   │   ├── cluster.py
│   │   ├── core.py
│   │   ├── helpers.py
│   │   ├── json/
│   │   │   ├── __init__.py
│   │   │   ├── _util.py
│   │   │   ├── commands.py
│   │   │   ├── decoders.py
│   │   │   └── path.py
│   │   ├── policies.py
│   │   ├── redismodules.py
│   │   ├── search/
│   │   │   ├── __init__.py
│   │   │   ├── _util.py
│   │   │   ├── aggregation.py
│   │   │   ├── commands.py
│   │   │   ├── dialect.py
│   │   │   ├── document.py
│   │   │   ├── field.py
│   │   │   ├── hybrid_query.py
│   │   │   ├── hybrid_result.py
│   │   │   ├── index_definition.py
│   │   │   ├── profile_information.py
│   │   │   ├── query.py
│   │   │   ├── querystring.py
│   │   │   ├── reducers.py
│   │   │   ├── result.py
│   │   │   └── suggestion.py
│   │   ├── sentinel.py
│   │   ├── timeseries/
│   │   │   ├── __init__.py
│   │   │   ├── commands.py
│   │   │   ├── info.py
│   │   │   └── utils.py
│   │   └── vectorset/
│   │       ├── __init__.py
│   │       ├── commands.py
│   │       └── utils.py
│   ├── connection.py
│   ├── crc.py
│   ├── credentials.py
│   ├── data_structure.py
│   ├── driver_info.py
│   ├── event.py
│   ├── exceptions.py
│   ├── http/
│   │   ├── __init__.py
│   │   └── http_client.py
│   ├── lock.py
│   ├── maint_notifications.py
│   ├── multidb/
│   │   ├── __init__.py
│   │   ├── circuit.py
│   │   ├── client.py
│   │   ├── command_executor.py
│   │   ├── config.py
│   │   ├── database.py
│   │   ├── event.py
│   │   ├── exception.py
│   │   ├── failover.py
│   │   └── failure_detector.py
│   ├── observability/
│   │   ├── __init__.py
│   │   ├── attributes.py
│   │   ├── config.py
│   │   ├── metrics.py
│   │   ├── providers.py
│   │   ├── recorder.py
│   │   └── registry.py
│   ├── ocsp.py
│   ├── py.typed
│   ├── retry.py
│   ├── sentinel.py
│   ├── typing.py
│   └── utils.py
├── specs/
│   ├── commands_overload_inventory.md
│   └── sync_async_deduplication_analysis.md
├── tasks.py
├── tests/
│   ├── __init__.py
│   ├── conftest.py
│   ├── entraid_utils.py
│   ├── helpers.py
│   ├── maint_notifications/
│   │   ├── proxy_server_helpers.py
│   │   ├── test_cluster_maint_notifications_handling.py
│   │   ├── test_maint_notifications.py
│   │   └── test_maint_notifications_handling.py
│   ├── mocks.py
│   ├── ssl_utils.py
│   ├── test_asyncio/
│   │   ├── __init__.py
│   │   ├── compat.py
│   │   ├── conftest.py
│   │   ├── helpers.py
│   │   ├── mocks.py
│   │   ├── test_bloom.py
│   │   ├── test_client.py
│   │   ├── test_cluster.py
│   │   ├── test_cluster_transaction.py
│   │   ├── test_command_parser.py
│   │   ├── test_command_policies.py
│   │   ├── test_commands.py
│   │   ├── test_connect.py
│   │   ├── test_connection.py
│   │   ├── test_connection_pool.py
│   │   ├── test_credentials.py
│   │   ├── test_cwe_404.py
│   │   ├── test_encoding.py
│   │   ├── test_hash.py
│   │   ├── test_json.py
│   │   ├── test_lock.py
│   │   ├── test_monitor.py
│   │   ├── test_multidb/
│   │   │   ├── __init__.py
│   │   │   ├── conftest.py
│   │   │   ├── test_client.py
│   │   │   ├── test_command_executor.py
│   │   │   ├── test_config.py
│   │   │   ├── test_failover.py
│   │   │   ├── test_failure_detector.py
│   │   │   ├── test_healthcheck.py
│   │   │   └── test_pipeline.py
│   │   ├── test_observability/
│   │   │   ├── __init__.py
│   │   │   ├── test_cluster_metrics_error_handling.py
│   │   │   └── test_recorder.py
│   │   ├── test_pipeline.py
│   │   ├── test_pubsub.py
│   │   ├── test_retry.py
│   │   ├── test_scenario/
│   │   │   ├── __init__.py
│   │   │   ├── conftest.py
│   │   │   └── test_active_active.py
│   │   ├── test_scripting.py
│   │   ├── test_search.py
│   │   ├── test_sentinel.py
│   │   ├── test_sentinel_managed_connection.py
│   │   ├── test_ssl.py
│   │   ├── test_timeseries.py
│   │   ├── test_usage_counter.py
│   │   ├── test_utils.py
│   │   ├── test_vsets.py
│   │   └── testdata/
│   │       ├── jsontestdata.py
│   │       ├── titles.csv
│   │       └── will_play_text.csv.bz2
│   ├── test_auth/
│   │   ├── __init__.py
│   │   ├── test_token.py
│   │   └── test_token_manager.py
│   ├── test_background.py
│   ├── test_backoff.py
│   ├── test_bloom.py
│   ├── test_cache.py
│   ├── test_client.py
│   ├── test_cluster.py
│   ├── test_cluster_transaction.py
│   ├── test_command_parser.py
│   ├── test_command_policies.py
│   ├── test_commands.py
│   ├── test_connect.py
│   ├── test_connection.py
│   ├── test_connection_pool.py
│   ├── test_credentials.py
│   ├── test_data_structure.py
│   ├── test_driver_info.py
│   ├── test_encoding.py
│   ├── test_event.py
│   ├── test_function.py
│   ├── test_hash.py
│   ├── test_helpers.py
│   ├── test_http/
│   │   ├── __init__.py
│   │   └── test_http_client.py
│   ├── test_json.py
│   ├── test_lock.py
│   ├── test_max_connections_error.py
│   ├── test_monitor.py
│   ├── test_multidb/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── test_circuit.py
│   │   ├── test_client.py
│   │   ├── test_command_executor.py
│   │   ├── test_config.py
│   │   ├── test_failover.py
│   │   ├── test_failure_detector.py
│   │   └── test_pipeline.py
│   ├── test_multiprocessing.py
│   ├── test_observability/
│   │   ├── __init__.py
│   │   ├── test_cluster_metrics_error_handling.py
│   │   ├── test_config.py
│   │   ├── test_metrics_connection_attributes.py
│   │   ├── test_provider.py
│   │   ├── test_public_api.py
│   │   └── test_recorder.py
│   ├── test_parsers/
│   │   ├── test_errors.py
│   │   └── test_helpers.py
│   ├── test_pipeline.py
│   ├── test_pubsub.py
│   ├── test_retry.py
│   ├── test_scenario/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── fault_injector_client.py
│   │   ├── maint_notifications_helpers.py
│   │   ├── test_active_active.py
│   │   └── test_maint_notifications.py
│   ├── test_scripting.py
│   ├── test_search.py
│   ├── test_sentinel.py
│   ├── test_sentinel_managed_connection.py
│   ├── test_ssl.py
│   ├── test_timeseries.py
│   ├── test_utils.py
│   ├── test_vsets.py
│   └── testdata/
│       ├── jsontestdata.py
│       ├── titles.csv
│       └── will_play_text.csv.bz2
├── util/
│   └── wait-for-it.sh
└── whitelist.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .agent/sync_async_type_hints_overload_guide.md
================================================
# Agent Overload Implementation Guide

## Purpose
This document provides instructions for implementing `@overload` signatures for redis-py command methods.

## How to Use This Guide

### Step 1: Select a Batch
Work on one batch at a time. Each batch contains methods grouped by command class.

### Step 2: For Each Method in the Batch
1. **Verify the return type** by checking Redis docs at https://redis.io/commands
2. **Add two `@overload` signatures** before the method when the sync and async return types differ:
   - One for sync (`self: SyncClientProtocol`) returning the sync type
   - One for async (`self: AsyncClientProtocol`) returning `Awaitable[sync_type]`
   - If both clients return the same concrete type, keep a single typed method instead of adding redundant overloads
3. **Mirror the full input signature exactly** in both overloads:
   - Same parameter names, order, defaults, positional/keyword shape, `*args`, and `**kwargs` as the implementation
   - Use the same modern annotation style as return types: prefer `X | Y` and `T | None` over `Union[...]` / `Optional[...]`
4. **Keep the original implementation** unchanged except for signature annotation normalization when needed
   - For implementation return unions, prefer the readable order `SyncType | Awaitable[SyncType]` (for example `(dict | None) | Awaitable[dict | None]`)
   - Runtime decorators such as deprecation or experimental markers must stay on the real implementation, not on `@overload` stubs. Put those decorators immediately above the implementation `def` after the overload block.
5. **Run type checker** to verify no errors

### Step 3: Mark Batch Complete
After implementing all methods in a batch, update status in inventory.

---

## Overload Pattern Template

```python
from typing import Awaitable, overload

from redis.typing import AsyncClientProtocol, SyncClientProtocol

class SomeCommands:
    @overload
    def method(self: SyncClientProtocol, arg: str | None = None) -> SyncReturnType: ...
    @overload
    def method(
        self: AsyncClientProtocol, arg: str | None = None
    ) -> Awaitable[SyncReturnType]: ...
    def method(
        self, arg: str | None = None
    ) -> SyncReturnType | Awaitable[SyncReturnType]:
        # original implementation unchanged
        ...
```

### Protocol Definitions (in `redis/typing.py`)

```python
class SyncClientProtocol(Protocol):
    """Marker for sync clients."""
    _is_async_client: Literal[False]

class AsyncClientProtocol(Protocol):
    """Marker for async clients."""
    _is_async_client: Literal[True]
```

**Note:** We use Protocol-based discrimination (not `Redis[bytes]` / `AsyncRedis[str]`) to avoid multiplying overloads by `decode_responses` setting.

---

## Status Legend

| Symbol | Meaning |
|--------|---------|
| ✅ | Can use standard overload pattern |
| ⚠️ | Has separate async implementation - SKIP |
| 🔄 | Returns iterator - SKIP |
| ❌ | Dunder method - SKIP |
| 📋 | Already has explicit types - may still need overloads |

---

## CRITICAL: Response Callback System

Understanding the callback system is essential for determining accurate return types.

### Three-Tier Callback Architecture

The `redis-py` library uses three callback dictionaries in `redis/_parsers/helpers.py`:

| Dictionary | Lines | Purpose |
|------------|-------|---------|
| `_RedisCallbacks` | 754-844 | **Base callbacks** - shared by both RESP2 and RESP3 |
| `_RedisCallbacksRESP2` | 847-896 | **RESP2-specific** overrides/additions |
| `_RedisCallbacksRESP3` | 899-947 | **RESP3-specific** overrides/additions |

### How Callbacks Are Applied

1. When a command is executed, the library checks for a callback in this order:
   - If using RESP2: Check `_RedisCallbacksRESP2` first, then fall back to `_RedisCallbacks`
   - If using RESP3: Check `_RedisCallbacksRESP3` first, then fall back to `_RedisCallbacks`

2. If no callback exists, the raw response is returned (depends on `decode_responses` setting)

### Key Callback Functions

| Callback | Return Type | Notes |
|----------|-------------|-------|
| `bool_ok` | `bool` | Converts "OK" to `True` |
| `bool` | `bool` | Converts int to bool |
| `str_if_bytes` | `str` | Converts bytes to str regardless of decode_responses |
| `float_or_none` | `float \| None` | Parses float, returns None if null |
| `parse_scan` | `tuple[int, list]` | Cursor + keys |
| `parse_info` | `dict[str, Any]` | Parses INFO output |
| `zset_score_pairs` | `list[tuple[..., float]]` | For sorted set with scores (RESP2) |
| `zset_score_pairs_resp3` | `list[tuple[..., float]]` | For sorted set with scores (RESP3) |

### Protocol-Specific Return Types

**IMPORTANT**: Some commands have different return types depending on protocol version:

| Command | RESP2 Return | RESP3 Return | Reason |
|---------|--------------|--------------|--------|
| `acl_cat` | `list[str]` | `list[bytes \| str]` | RESP2 has `str_if_bytes`, RESP3 has no callback |
| `acl_genpass` | `str` | `bytes \| str` | RESP2 has `str_if_bytes`, RESP3 has no callback |
| `client_getname` | `str \| None` | `bytes \| str \| None` | RESP2 has `str_if_bytes`, RESP3 has no callback |
| `geohash` | `list[str]` | `list[bytes \| str]` | RESP2 has `str_if_bytes`, RESP3 has no callback |
| `hgetall` | `dict` (pairs_to_dict) | `dict` (identity) | Different parsing logic |
| `zincrby`, `zscore` | `float` (float_or_none) | `float` (raw) | RESP2 parses, RESP3 returns directly |

### How to Determine Return Type

1. **Check `_RedisCallbacks`** (base) - Is there a callback for the command?
2. **Check `_RedisCallbacksRESP2`** - Is there a RESP2-specific override?
3. **Check `_RedisCallbacksRESP3`** - Is there a RESP3-specific override?
4. **If no callback** - Return type depends on `decode_responses`:
   - Bulk string: `bytes | str`
   - Integer: `int`
   - Array: `list[...]`
   - Null: `None`

### Recommended Typing Strategy

For commands with protocol-specific differences, use the **most permissive union type**:
- If RESP2 returns `str` and RESP3 returns `bytes | str`, use `bytes | str`
- This ensures type safety regardless of protocol version

---

## Batches

### BATCH 1: ACLCommands (core.py)
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 1 | `acl_cat` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 2 | `acl_dryrun` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback |
| 3 | `acl_deluser` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 4 | `acl_genpass` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 5 | `acl_getuser` | `dict \| None` | `Awaitable[dict \| None]` | ✅ | Base: parse_acl_getuser |
| 6 | `acl_help` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 7 | `acl_list` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 8 | `acl_log` | `list[dict]` | `Awaitable[list[dict]]` | ✅ | Base: parse_acl_log / RESP3: lambda |
| 9 | `acl_log_reset` | `bool` | `Awaitable[bool]` | ✅ | Via acl_log with RESET |
| 10 | `acl_load` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 11 | `acl_save` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 12 | `acl_setuser` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 13 | `acl_users` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 14 | `acl_whoami` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | RESP2: str_if_bytes / RESP3: raw |

### BATCH 2: ManagementCommands Part 1 (core.py) - Methods 15-50
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 15 | `auth` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 16 | `bgrewriteaof` | `bool \| bytes \| str` | `Awaitable[bool \| bytes \| str]` | ✅ | RESP2: True / RESP3: raw |
| 17 | `bgsave` | `bool \| bytes \| str` | `Awaitable[bool \| bytes \| str]` | ✅ | RESP2: True / RESP3: raw |
| 18 | `role` | `list` | `Awaitable[list]` | ✅ | No callback - mixed types |
| 19 | `client_kill` | `bool \| int` | `Awaitable[bool \| int]` | ✅ | Base: parse_client_kill |
| 20 | `client_kill_filter` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 21 | `client_info` | `dict[str, str]` | `Awaitable[dict[str, str]]` | ✅ | Base: parse_client_info |
| 22 | `client_list` | `list[dict[str, str]]` | `Awaitable[list[dict[str, str]]]` | ✅ | Base: parse_client_list |
| 23 | `client_getname` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 24 | `client_getredir` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 25 | `client_reply` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback |
| 26 | `client_id` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 27 | `client_tracking_on` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - returns raw OK |
| 28 | `client_tracking_off` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - returns raw OK |
| 29 | `client_tracking` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - returns raw OK |
| 30 | `client_trackinginfo` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 31 | `client_setname` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 32 | `client_setinfo` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 33 | `client_unblock` | `bool` | `Awaitable[bool]` | ✅ | Base: bool (converts int) |
| 34 | `client_pause` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 35 | `client_unpause` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - returns raw OK |
| 36 | `client_no_evict` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - returns raw OK |
| 37 | `client_no_touch` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - returns raw OK |
| 38 | `command` | `list` | `Awaitable[list]` | ✅ | Base: parse_command / RESP3: parse_command_resp3 |
| 39 | `command_info` | `None` | `None` | ⚠️ SKIP | N/A |
| 40 | `command_count` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 41 | `command_list` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw |
| 42 | `command_getkeysandflags` | `list[list[bytes \| str \| list[bytes \| str]]]` | `Awaitable[list[list[bytes \| str \| list[bytes \| str]]]]` | ✅ | No callback - mixed [key, flags] shape |
| 43 | `command_docs` | `dict` | `Awaitable[dict]` | ✅ | No callback - raw dict |
| 44 | `config_get` | `dict[str, str]` | `Awaitable[dict[str, str]]` | ✅ | RESP2: parse_config_get / RESP3: lambda |
| 45 | `config_set` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 46 | `config_resetstat` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 47 | `config_rewrite` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - returns raw OK |
| 48 | `dbsize` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 49 | `debug_object` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | RESP2: parse_debug_object / RESP3: raw |
| 50 | `debug_segfault` | `None` | `None` | ⚠️ SKIP | N/A |

### BATCH 3: ManagementCommands Part 2 (core.py) - Methods 51-93
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 51 | `echo` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 52 | `flushall` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 53 | `flushdb` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 54 | `sync` | `bytes` | `Awaitable[bytes]` | ✅ | No callback - raw bytes |
| 55 | `psync` | `bytes` | `Awaitable[bytes]` | ✅ | No callback - raw bytes |
| 56 | `swapdb` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 57 | `select` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 58 | `info` | `dict[str, Any]` | `Awaitable[dict[str, Any]]` | ✅ | Base: parse_info |
| 59 | `lastsave` | `datetime` | `Awaitable[datetime]` | ✅ | Base: timestamp_to_datetime |
| 60 | `latency_doctor` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 61 | `latency_graph` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 62 | `lolwut` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 63 | `reset` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 64 | `migrate` | `bool \| bytes \| str` | `Awaitable[bool \| bytes \| str]` | ✅ | No callback - NOKEY or OK |
| 65 | `object` | `Any` | `Awaitable[Any]` | ✅ | Varies by subcommand |
| 66 | `memory_doctor` | `None` | `None` | ⚠️ SKIP | N/A |
| 67 | `memory_help` | `None` | `None` | ⚠️ SKIP | N/A |
| 68 | `memory_stats` | `dict[str, Any]` | `Awaitable[dict[str, Any]]` | ✅ | RESP2: parse_memory_stats / RESP3: lambda |
| 69 | `memory_malloc_stats` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 70 | `memory_usage` | `int \| None` | `Awaitable[int \| None]` | ✅ | Integer or nil reply |
| 71 | `memory_purge` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 72 | `latency_histogram` | `dict` | `Awaitable[dict]` | ✅ | No callback - raw dict |
| 73 | `latency_history` | `list[tuple[int, int]]` | `Awaitable[list[tuple[int, int]]]` | ✅ | No callback - array of arrays |
| 74 | `latency_latest` | `list[tuple[bytes \| str, int, int, int]]` | `Awaitable[list[tuple[bytes \| str, int, int, int]]]` | ✅ | First element is key name |
| 75 | `latency_reset` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 76 | `ping` | `bool` | `Awaitable[bool]` | 📋 | Base: lambda - returns True if PONG |
| 77 | `quit` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 78 | `replicaof` | `bool` | `Awaitable[bool]` | ✅ | No callback - OK |
| 79 | `save` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 80 | `shutdown` | `None` | `None` | ⚠️ SKIP | N/A |
| 81 | `slaveof` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 82 | `slowlog_get` | `list[dict]` | `Awaitable[list[dict]]` | ✅ | Base: parse_slowlog_get |
| 83 | `slowlog_len` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 84 | `slowlog_reset` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 85 | `time` | `tuple[int, int]` | `Awaitable[tuple[int, int]]` | ✅ | Base: lambda - tuple(secs, usecs) |
| 86 | `wait` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 87 | `waitaof` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |
| 88 | `hello` | `dict` | `Awaitable[dict]` | ✅ | No callback - raw dict |
| 89 | `failover` | `bool` | `Awaitable[bool]` | ✅ | No callback - OK |
| 90 | `hotkeys_start` | `bytes \| str` | `Awaitable[bytes \| str]` | 📋 | No callback - raw |
| 91 | `hotkeys_stop` | `bytes \| str` | `Awaitable[bytes \| str]` | 📋 | No callback - raw |
| 92 | `hotkeys_reset` | `bytes \| str` | `Awaitable[bytes \| str]` | 📋 | No callback - raw |
| 93 | `hotkeys_get` | `list[dict]` | `Awaitable[list[dict]]` | 📋 | RESP2: lambda pairs_to_dict |

### BATCH 4: BasicKeyCommands Part 1 (core.py) - Methods 94-130
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 94 | `append` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 95 | `bitcount` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 96 | `bitfield` | `BitFieldOperation` | `BitFieldOperation` | 📋 | Returns operation builder |
| 97 | `bitfield_ro` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |
| 98 | `bitop` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 99 | `bitpos` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 100 | `copy` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 101 | `decrby` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 102 | `delete` | `int` | `Awaitable[int]` | ✅ | Integer reply - count deleted |
| 103 | `__delitem__` | `None` | N/A | ❌ SKIP | Dunder |
| 104 | `delex` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 105 | `dump` | `bytes \| None` | `Awaitable[bytes \| None]` | ✅ | Always bytes (serialized) |
| 106 | `exists` | `int` | `Awaitable[int]` | ✅ | Integer reply - count |
| 107 | `expire` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 108 | `expireat` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 109 | `expiretime` | `int` | `Awaitable[int]` | 📋 | Integer - timestamp |
| 110 | `digest_local` | `bytes \| str` | `Awaitable[bytes \| str]` | 📋 | No callback - raw |
| 111 | `digest` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 112 | `get` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ DONE | No callback - raw |
| 113 | `getdel` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 114 | `getex` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 115 | `__getitem__` | `bytes \| str` | N/A | ❌ SKIP | Dunder |
| 116 | `getbit` | `int` | `Awaitable[int]` | ✅ | Integer reply - 0 or 1 |
| 117 | `getrange` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 118 | `getset` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 119 | `incrby` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 120 | `incrbyfloat` | `float` | `Awaitable[float]` | ✅ | Base: float |
| 121 | `keys` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw array |
| 122 | `lmove` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 123 | `blmove` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 124 | `mget` | `list[bytes \| str \| None]` | `Awaitable[list[bytes \| str \| None]]` | ✅ | No callback - raw array |
| 125 | `mset` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 126 | `msetex` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 127 | `msetnx` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 128 | `move` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 129 | `persist` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 130 | `pexpire` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |

### BATCH 5: BasicKeyCommands Part 2 (core.py) - Methods 131-156
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 131 | `pexpireat` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 132 | `pexpiretime` | `int` | `Awaitable[int]` | ✅ | Integer - timestamp in ms |
| 133 | `psetex` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 134 | `pttl` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 135 | `hrandfield` | `bytes \| str \| list \| None` | `Awaitable[bytes \| str \| list \| None]` | ✅ | Varies by COUNT param |
| 136 | `randomkey` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 137 | `rename` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 138 | `renamenx` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 139 | `restore` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw OK |
| 140 | `set` | `bool \| None` | `Awaitable[bool \| None]` | ✅ DONE | Base: parse_set_result |
| 141 | `__setitem__` | `None` | N/A | ❌ SKIP | Dunder |
| 142 | `setbit` | `int` | `Awaitable[int]` | ✅ | Integer reply - prev bit |
| 143 | `setex` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 144 | `setnx` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 145 | `setrange` | `int` | `Awaitable[int]` | ✅ | Integer reply - new length |
| 146 | `stralgo` | `dict \| str \| int` | `Awaitable[dict \| str \| int]` | ✅ | RESP2: parse_stralgo / RESP3: lambda |
| 147 | `strlen` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 148 | `substr` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 149 | `touch` | `int` | `Awaitable[int]` | ✅ | Integer reply - count |
| 150 | `ttl` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 151 | `type` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 152 | `watch` | `bool` | `Awaitable[bool]` | ⚠️ SKIP | Transaction |
| 153 | `unwatch` | `bool` | `Awaitable[bool]` | ⚠️ SKIP | Transaction |
| 154 | `unlink` | `int` | `Awaitable[int]` | ✅ | Integer reply - count |
| 155 | `lcs` | `bytes \| str \| int \| list \| dict` | `Awaitable[bytes \| str \| int \| list \| dict]` | ✅ | Raw reply varies by options and protocol |
| 156 | `__contains__` | `bool` | N/A | ❌ SKIP | Dunder |

### BATCH 6: ListCommands (core.py) - Methods 157-178
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 157 | `blpop` | `tuple[bytes \| str, bytes \| str] \| list[bytes \| str] \| None` | `Awaitable[tuple[bytes \| str, bytes \| str] \| list[bytes \| str] \| None]` | ✅ | RESP2: tuple / RESP3: raw list |
| 158 | `brpop` | `tuple[bytes \| str, bytes \| str] \| list[bytes \| str] \| None` | `Awaitable[tuple[bytes \| str, bytes \| str] \| list[bytes \| str] \| None]` | ✅ | RESP2: tuple / RESP3: raw list |
| 159 | `brpoplpush` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 160 | `blmpop` | `list[bytes \| str \| list[bytes \| str]] \| None` | `Awaitable[list[bytes \| str \| list[bytes \| str]] \| None]` | ✅ | No callback - nested [key, values] shape |
| 161 | `lmpop` | `list[bytes \| str \| list[bytes \| str]] \| None` | `Awaitable[list[bytes \| str \| list[bytes \| str]] \| None]` | ✅ | No callback - nested [key, values] shape |
| 162 | `lindex` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 163 | `linsert` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 164 | `llen` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 165 | `lpop` | `bytes \| str \| list[bytes \| str] \| None` | `Awaitable[bytes \| str \| list[bytes \| str] \| None]` | ✅ | Varies by COUNT |
| 166 | `lpush` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 167 | `lpushx` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 168 | `lrange` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw array |
| 169 | `lrem` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 170 | `lset` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 171 | `ltrim` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 172 | `rpop` | `bytes \| str \| list[bytes \| str] \| None` | `Awaitable[bytes \| str \| list[bytes \| str] \| None]` | ✅ | Varies by COUNT |
| 173 | `rpoplpush` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 174 | `rpush` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 175 | `rpushx` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 176 | `lpos` | `int \| list[int] \| None` | `Awaitable[int \| list[int] \| None]` | ✅ | Varies by COUNT/RANK |
| 177 | `sort` | `list[bytes \| str] \| list[tuple[bytes \| str, ...]] \| int` | `Awaitable[list[bytes \| str] \| list[tuple[bytes \| str, ...]] \| int]` | ✅ | Base: sort_return_tuples incl. grouped tuples |
| 178 | `sort_ro` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw array |

### BATCH 7: ScanCommands + SetCommands (core.py) - Methods 179-202
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 179 | `scan` | `tuple[int, list[bytes \| str]]` | `Awaitable[tuple[int, list[bytes \| str]]]` | ✅ | Base: parse_scan |
| 180 | `scan_iter` | `Iterator` | `AsyncIterator` | 🔄 SKIP | Iterator |
| 181 | `sscan` | `tuple[int, list[bytes \| str]]` | `Awaitable[tuple[int, list[bytes \| str]]]` | ✅ | Base: parse_scan |
| 182 | `sscan_iter` | `Iterator` | `AsyncIterator` | 🔄 SKIP | Iterator |
| 183 | `hscan` | `tuple[int, dict[bytes \| str, bytes \| str] \| list[bytes \| str]]` | `Awaitable[tuple[int, dict[bytes \| str, bytes \| str] \| list[bytes \| str]]]` | ✅ | Base: parse_hscan, `NOVALUES` returns key list |
| 184 | `hscan_iter` | `Iterator` | `AsyncIterator` | 🔄 SKIP | Iterator |
| 185 | `zscan` | `tuple[int, list[tuple[bytes \| str, float]]]` | `Awaitable[tuple[int, list[tuple[bytes \| str, float]]]]` | ✅ | Base: parse_zscan |
| 186 | `zscan_iter` | `Iterator` | `AsyncIterator` | 🔄 SKIP | Iterator |
| 187 | `sadd` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 188 | `scard` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 189 | `sdiff` | `set[bytes \| str]` | `Awaitable[set[bytes \| str]]` | ✅ | RESP2+RESP3: lambda set |
| 190 | `sdiffstore` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 191 | `sinter` | `set[bytes \| str]` | `Awaitable[set[bytes \| str]]` | ✅ | RESP2+RESP3: lambda set |
| 192 | `sintercard` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 193 | `sinterstore` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 194 | `Literal[0] \| Literal[1]` | `Awaitable[Literal[0] \| Literal[1]]` | ✅ | Integer 0/1 - no callback |
| 195 | `smembers` | `set[bytes \| str]` | `Awaitable[set[bytes \| str]]` | ✅ | RESP2+RESP3: lambda set |
| 196 | `smismember` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of 0/1 |
| 197 | `smove` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 198 | `spop` | `bytes \| str \| set[bytes \| str] \| None` | `Awaitable[bytes \| str \| set[bytes \| str] \| None]` | ✅ | Varies by COUNT, count form returns a set |
| 199 | `srandmember` | `bytes \| str \| list[bytes \| str] \| None` | `Awaitable[bytes \| str \| list[bytes \| str] \| None]` | ✅ | Varies by NUMBER |
| 200 | `srem` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 201 | `sunion` | `set[bytes \| str]` | `Awaitable[set[bytes \| str]]` | ✅ | RESP2+RESP3: lambda set |
| 202 | `sunionstore` | `int` | `Awaitable[int]` | ✅ | Integer reply |

### BATCH 8: StreamCommands (core.py) - Methods 203-226
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 203 | `xack` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 204 | `xackdel` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 205 | `xadd` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - stream ID |
| 206 | `xcfgset` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw OK |
| 207 | `xautoclaim` | `list[Any]` | `Awaitable[list[Any]]` | ✅ | Base: parse_xautoclaim / JUSTID special case |
| 208 | `xclaim` | `list[tuple[bytes \| str \| None, dict \| None]] \| list[bytes \| str]` | `Awaitable[list[tuple[bytes \| str \| None, dict \| None]] \| list[bytes \| str]]` | ✅ | Base: parse_xclaim / JUSTID returns ID list |
| 209 | `xdel` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 210 | `xdelex` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 211 | `xgroup_create` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 212 | `xgroup_delconsumer` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 213 | `xgroup_destroy` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 214 | `xgroup_createconsumer` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 215 | `xgroup_setid` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 216 | `xinfo_consumers` | `list[dict]` | `Awaitable[list[dict]]` | ✅ | RESP2: parse_list_of_dicts / RESP3: lambda |
| 217 | `xinfo_groups` | `list[dict]` | `Awaitable[list[dict]]` | ✅ | RESP2: parse_list_of_dicts / RESP3: lambda |
| 218 | `xinfo_stream` | `dict[str, Any]` | `Awaitable[dict[str, Any]]` | ✅ | Base: parse_xinfo_stream |
| 219 | `xlen` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 220 | `xpending` | `dict[str, Any]` | `Awaitable[dict[str, Any]]` | ✅ | Base: parse_xpending |
| 221 | `xpending_range` | `list[dict[str, bytes \| str \| int]]` | `Awaitable[list[dict[str, bytes \| str \| int]]]` | ✅ | parse_xpending_range detail rows |
| 222 | `xrange` | `list[tuple[bytes \| str \| None, dict \| None]] \| None` | `Awaitable[list[tuple[bytes \| str \| None, dict \| None]] \| None]` | ✅ | Base: parse_stream_list |
| 223 | `xread` | `list \| dict` | `Awaitable[list \| dict]` | ✅ | RESP2: parse_xread / RESP3: parse_xread_resp3 |
| 224 | `xreadgroup` | `list \| dict` | `Awaitable[list \| dict]` | ✅ | RESP2: parse_xread / RESP3: parse_xread_resp3 |
| 225 | `xrevrange` | `list[tuple[bytes \| str \| None, dict \| None]] \| None` | `Awaitable[list[tuple[bytes \| str \| None, dict \| None]] \| None]` | ✅ | Base: parse_stream_list |
| 226 | `xtrim` | `int` | `Awaitable[int]` | ✅ | Integer reply |

### BATCH 9: SortedSetCommands Part 1 (core.py) - Methods 227-260
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 227 | `zadd` | `int \| float` | `Awaitable[int \| float]` | ✅ | RESP2: parse_zadd |
| 228 | `zcard` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 229 | `zcount` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 230 | `zdiff` | `ZSetRangeResponse` | `Awaitable[ZSetRangeResponse]` | ✅ | `WITHSCORES`: RESP2 tuple pairs / RESP3 raw nested lists |
| 231 | `zdiffstore` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 232 | `zincrby` | `float \| None` | `Awaitable[float \| None]` | ✅ | RESP2: `float_or_none` / RESP3: raw float |
| 233 | `zinter` | `ZSetRangeResponse` | `Awaitable[ZSetRangeResponse]` | ✅ | `score_cast_func` means scored branch uses `Any` |
| 234 | `zinterstore` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 235 | `zintercard` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 236 | `zlexcount` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 237 | `zpopmax` | `ZSetRangeResponse` | `Awaitable[ZSetRangeResponse]` | ✅ | RESP2 tuple pairs / RESP3 nested lists |
| 238 | `zpopmin` | `ZSetRangeResponse` | `Awaitable[ZSetRangeResponse]` | ✅ | RESP2 tuple pairs / RESP3 nested lists |
| 239 | `zrandmember` | `ZRandMemberResponse` | `Awaitable[ZRandMemberResponse]` | ✅ | COUNT / WITHSCORES shape varies by protocol |
| 240 | `bzpopmax` | `BlockingZSetPopResponse` | `Awaitable[BlockingZSetPopResponse]` | ✅ | RESP2 tuple / RESP3 raw list / `None` |
| 241 | `bzpopmin` | `BlockingZSetPopResponse` | `Awaitable[BlockingZSetPopResponse]` | ✅ | RESP2 tuple / RESP3 raw list / `None` |
| 242 | `zmpop` | `ZMPopResponse` | `Awaitable[ZMPopResponse]` | ✅ | Raw `[key, [[member, score], ...]]` or `None` |
| 243 | `bzmpop` | `ZMPopResponse` | `Awaitable[ZMPopResponse]` | ✅ | Raw `[key, [[member, score], ...]]` or `None` |
| 244 | `zrange` | `ZSetRangeResponse` | `Awaitable[ZSetRangeResponse]` | ✅ | `score_cast_func` means scored branch uses `Any` |
| 245 | `zrevrange` | `ZSetRangeResponse` | `Awaitable[ZSetRangeResponse]` | ✅ | `score_cast_func` means scored branch uses `Any` |
| 246 | `zrangestore` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 247 | `zrangebylex` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw array |
| 248 | `zrevrangebylex` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw array |
| 249 | `zrangebyscore` | `ZSetRangeResponse` | `Awaitable[ZSetRangeResponse]` | ✅ | `score_cast_func` means scored branch uses `Any` |
| 250 | `zrevrangebyscore` | `ZSetRangeResponse` | `Awaitable[ZSetRangeResponse]` | ✅ | `score_cast_func` means scored branch uses `Any` |
| 251 | `zrank` | `ZRankResponse` | `Awaitable[ZRankResponse]` | ✅ | `WITHSCORE` returns `[rank, score]`, not tuple |
| 252 | `zrem` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 253 | `zremrangebylex` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 254 | `zremrangebyrank` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 255 | `zremrangebyscore` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 256 | `zrevrank` | `ZRankResponse` | `Awaitable[ZRankResponse]` | ✅ | `WITHSCORE` returns `[rank, score]`, not tuple |
| 257 | `zscore` | `float \| None` | `Awaitable[float \| None]` | ✅ | RESP2: `float_or_none` / RESP3: raw float |
| 258 | `zunion` | `ZSetRangeResponse` | `Awaitable[ZSetRangeResponse]` | ✅ | `score_cast_func` means scored branch uses `Any` |
| 259 | `zunionstore` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 260 | `zmscore` | `list[float \| None]` | `Awaitable[list[float \| None]]` | ✅ | RESP2: `parse_zmscore` / RESP3: raw float list |

### BATCH 10: HyperlogCommands + HashCommands (core.py) - Methods 261-289
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 261 | `pfadd` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 262 | `pfcount` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 263 | `pfmerge` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 264 | `hdel` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 265 | `hexists` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 266 | `hget` | `bytes \| str \| None` | `Awaitable[bytes \| str \| None]` | ✅ | No callback - raw |
| 267 | `hgetall` | `dict[bytes \| str, bytes \| str]` | `Awaitable[dict[bytes \| str, bytes \| str]]` | ✅ | RESP2: pairs_to_dict / RESP3: identity |
| 268 | `hgetdel` | `list[bytes \| str \| None]` | `Awaitable[list[bytes \| str \| None]]` | ✅ | No callback - one result per requested field |
| 269 | `hgetex` | `list[bytes \| str \| None]` | `Awaitable[list[bytes \| str \| None]]` | ✅ | No callback - one result per requested field |
| 270 | `hincrby` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 271 | `hincrbyfloat` | `float` | `Awaitable[float]` | ✅ | Base: float |
| 272 | `hkeys` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw array |
| 273 | `hlen` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 274 | `hset` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 275 | `hsetex` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 276 | `hsetnx` | `int` | `Awaitable[int]` | ✅ | No callback - integer 0/1 reply |
| 277 | `hmset` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 278 | `hmget` | `list[bytes \| str \| None]` | `Awaitable[list[bytes \| str \| None]]` | ✅ | No callback - raw array |
| 279 | `hvals` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw array |
| 280 | `hstrlen` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 281 | `hexpire` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |
| 282 | `hpexpire` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |
| 283 | `hexpireat` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |
| 284 | `hpexpireat` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |
| 285 | `hpersist` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |
| 286 | `hexpiretime` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |
| 287 | `hpexpiretime` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |
| 288 | `httl` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |
| 289 | `hpttl` | `list[int]` | `Awaitable[list[int]]` | ✅ | Array of integers |

### BATCH 11: PubSubCommands + ScriptCommands (core.py) - Methods 290-306
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 290 | `publish` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 291 | `spublish` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 292 | `pubsub_channels` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw |
| 293 | `pubsub_shardchannels` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw |
| 294 | `pubsub_numpat` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 295 | `pubsub_numsub` | `list[tuple[bytes \| str, int]]` | `Awaitable[list[tuple[bytes \| str, int]]]` | ✅ | Base: parse_pubsub_numsub |
| 296 | `pubsub_shardnumsub` | `list[tuple[bytes \| str, int]]` | `Awaitable[list[tuple[bytes \| str, int]]]` | ✅ | Base: parse_pubsub_numsub |
| 297 | `eval` | `Any` | `Awaitable[Any]` | ✅ | Script-dependent |
| 298 | `eval_ro` | `Any` | `Awaitable[Any]` | ✅ | Script-dependent |
| 299 | `evalsha` | `Any` | `Awaitable[Any]` | ✅ | Script-dependent |
| 300 | `evalsha_ro` | `Any` | `Awaitable[Any]` | ✅ | Script-dependent |
| 301 | `script_exists` | `list[bool]` | `Awaitable[list[bool]]` | ✅ | Base: lambda map bool |
| 302 | `script_debug` | `None` | `None` | ⚠️ SKIP | N/A |
| 303 | `script_flush` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 304 | `script_kill` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 305 | `script_load` | `str` | `Awaitable[str]` | ✅ | Base: str_if_bytes |
| 306 | `register_script` | `Script` | `AsyncScript` | ⚠️ SKIP | Different classes |

### BATCH 12: GeoCommands + ModuleCommands + ClusterCommands + FunctionCommands (core.py) - Methods 307-335
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 307 | `geoadd` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 308 | `geodist` | `float \| None` | `Awaitable[float \| None]` | ✅ | Base: float_or_none |
| 309 | `geohash` | `list[bytes \| str \| None]` | `Awaitable[list[bytes \| str \| None]]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 310 | `geopos` | `list[tuple[float, float] \| None]` | `Awaitable[list[tuple[float, float] \| None]]` | ✅ | RESP2: lambda float tuple / RESP3: raw |
| 311 | `georadius` | `list[Any] \| int` | `Awaitable[list[Any] \| int]` | ✅ | `store` / `store_dist` return int, otherwise parsed list |
| 312 | `georadiusbymember` | `list[Any] \| int` | `Awaitable[list[Any] \| int]` | ✅ | `store` / `store_dist` return int, otherwise parsed list |
| 313 | `geosearch` | `list[Any]` | `Awaitable[list[Any]]` | ✅ | Base: parse_geosearch_generic |
| 314 | `geosearchstore` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 315 | `module_load` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 316 | `module_loadex` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw OK reply |
| 317 | `module_unload` | `bool` | `Awaitable[bool]` | ✅ | Base: bool |
| 318 | `module_list` | `list[dict[Any, Any]]` | `Awaitable[list[dict[Any, Any]]]` | ✅ | RESP2: lambda pairs_to_dict / RESP3: raw dict list |
| 319 | `command_info` | `None` | `None` | ⚠️ SKIP | N/A |
| 320 | `command_count` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 321 | `command_getkeys` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 322 | `command` | `dict[str, dict[str, Any]]` | `Awaitable[dict[str, dict[str, Any]]]` | ✅ | Base: parse_command / RESP3: parse_command_resp3 |
| 323 | `cluster` | `Any` | `Awaitable[Any]` | ✅ | Subcommand-dependent |
| 324 | `readwrite` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 325 | `readonly` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 326 | `function_load` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 327 | `function_delete` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 328 | `function_flush` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 329 | `function_list` | `list[Any]` | `Awaitable[list[Any]]` | ✅ | No callback - raw protocol-dependent list |
| 330 | `fcall` | `Any` | `Awaitable[Any]` | ✅ | Function-dependent |
| 331 | `fcall_ro` | `Any` | `Awaitable[Any]` | ✅ | Function-dependent |
| 332 | `function_dump` | `bytes` | `Awaitable[bytes]` | ✅ | No callback - raw bytes |
| 333 | `function_restore` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 334 | `function_kill` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw OK reply |
| 335 | `function_stats` | `Any` | `Awaitable[Any]` | ✅ | No callback - raw protocol-dependent structure |

### BATCH 13: ClusterCommands (cluster.py) - Methods 336-378
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 336 | `mget_nonatomic` | `list[bytes \| str \| None]` | `Awaitable[list[bytes \| str \| None]]` | ⚠️ SKIP | Complex multi-node |
| 337 | `mset_nonatomic` | `list[bool]` | `Awaitable[list[bool]]` | ⚠️ SKIP | Complex multi-node |
| 338 | `exists` | `int` | `Awaitable[int]` | 📋 | Integer reply |
| 339 | `delete` | `int` | `Awaitable[int]` | 📋 | Integer reply |
| 340 | `touch` | `int` | `Awaitable[int]` | 📋 | Integer reply |
| 341 | `unlink` | `int` | `Awaitable[int]` | 📋 | Integer reply |
| 342 | `slaveof` | `NoReturn` | `NoReturn` | 📋 SKIP | Raises error |
| 343 | `replicaof` | `NoReturn` | `NoReturn` | 📋 SKIP | Raises error |
| 344 | `swapdb` | `NoReturn` | `NoReturn` | 📋 SKIP | Raises error |
| 345 | `cluster_myid` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 346 | `cluster_addslots` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 347 | `cluster_addslotsrange` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 348 | `cluster_countkeysinslot` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 349 | `cluster_count_failure_report` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 350 | `cluster_delslots` | `list[bool]` | `Awaitable[list[bool]]` | ⚠️ SKIP | Complex multi-node |
| 351 | `cluster_delslotsrange` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 352 | `cluster_failover` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 353 | `cluster_info` | `dict[str, str]` | `Awaitable[dict[str, str]]` | ✅ | Base: parse_cluster_info |
| 354 | `cluster_keyslot` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 355 | `cluster_meet` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 356 | `cluster_nodes` | `str` | `Awaitable[str]` | ✅ | Base: parse_cluster_nodes |
| 357 | `cluster_replicate` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 358 | `cluster_reset` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 359 | `cluster_save_config` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 360 | `cluster_get_keys_in_slot` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | RESP2: str_if_bytes / RESP3: raw |
| 361 | `cluster_set_config_epoch` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 362 | `cluster_setslot` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 363 | `cluster_setslot_stable` | `bool` | `Awaitable[bool]` | ✅ | No callback - OK |
| 364 | `cluster_replicas` | `str` | `Awaitable[str]` | ✅ | Base: parse_cluster_nodes |
| 365 | `cluster_slots` | `list` | `Awaitable[list]` | ✅ | No callback - raw |
| 366 | `cluster_shards` | `list[dict]` | `Awaitable[list[dict]]` | ✅ | No callback - raw |
| 367 | `cluster_myshardid` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 368 | `cluster_links` | `list[dict]` | `Awaitable[list[dict]]` | ✅ | No callback - raw |
| 369 | `cluster_flushslots` | `bool` | `Awaitable[bool]` | ✅ | No callback - OK |
| 370 | `cluster_bumpepoch` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 371 | `client_tracking_on` | `bytes \| str` | `Awaitable[bytes \| str]` | ⚠️ SKIP | Cluster-specific, no callback |
| 372 | `client_tracking_off` | `bytes \| str` | `Awaitable[bytes \| str]` | ⚠️ SKIP | Cluster-specific, no callback |
| 373 | `hotkeys_start` | `bytes \| str` | `Awaitable[bytes \| str]` | ⚠️ SKIP | Cluster-specific |
| 374 | `hotkeys_stop` | `bytes \| str` | `Awaitable[bytes \| str]` | ⚠️ SKIP | Cluster-specific |
| 375 | `hotkeys_reset` | `bytes \| str` | `Awaitable[bytes \| str]` | ⚠️ SKIP | Cluster-specific |
| 376 | `hotkeys_get` | `list[dict]` | `Awaitable[list[dict]]` | ⚠️ SKIP | Cluster-specific |
| 377 | `stralgo` | `dict \| bytes \| str \| int` | `Awaitable[dict \| bytes \| str \| int]` | ✅ | RESP2: parse_stralgo / RESP3: lambda |
| 378 | `scan_iter` | `Iterator` | `AsyncIterator` | 🔄 SKIP | Iterator |

### BATCH 14: SentinelCommands (sentinel.py) - Methods 379-391
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 379 | `sentinel` | `Any` | `Awaitable[Any]` | ⚠️ SKIP | Async differs |
| 380 | `sentinel_get_master_addr_by_name` | `tuple[str, int] \| None` | `Awaitable[tuple[str, int] \| None]` | ✅ | Base: parse_sentinel_get_master |
| 381 | `sentinel_master` | `dict` | `Awaitable[dict]` | ✅ | RESP2: parse_sentinel_master / RESP3: parse_sentinel_state_resp3 |
| 382 | `sentinel_masters` | `list[dict]` | `Awaitable[list[dict]]` | ✅ | RESP2: parse_sentinel_masters / RESP3: parse_sentinel_masters_resp3 |
| 383 | `sentinel_monitor` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 384 | `sentinel_remove` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 385 | `sentinel_sentinels` | `list[dict]` | `Awaitable[list[dict]]` | ✅ | RESP2: parse_sentinel_slaves_and_sentinels / RESP3: _resp3 |
| 386 | `sentinel_set` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 387 | `sentinel_slaves` | `list[dict]` | `Awaitable[list[dict]]` | ✅ | RESP2: parse_sentinel_slaves_and_sentinels / RESP3: _resp3 |
| 388 | `sentinel_reset` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 389 | `sentinel_failover` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 390 | `sentinel_ckquorum` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |
| 391 | `sentinel_flushconfig` | `bool` | `Awaitable[bool]` | ✅ | Base: bool_ok |

### BATCH 15: SearchCommands (search/commands.py) - Methods 392-424
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 392 | `batch_indexer` | `BatchIndexer` | `BatchIndexer` | 📋 SKIP | Builder pattern |
| 393 | `create_index` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 394 | `alter_schema_add` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 395 | `dropindex` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 396 | `add_document` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 397 | `add_document_hash` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 398 | `delete_document` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 399 | `load_document` | `Document` | `Awaitable[Document]` | ⚠️ SKIP | Async result parsing |
| 400 | `get` | `Document` | `Awaitable[Document]` | ✅ | Module-specific |
| 401 | `info` | `dict` | `Awaitable[dict]` | ⚠️ SKIP | Async result parsing |
| 402 | `get_params_args` | `list` | `list` | 📋 SKIP | Helper method |
| 403 | `search` | `Result` | `Awaitable[Result]` | ⚠️ SKIP | Async result parsing |
| 404 | `hybrid_search` | `HybridResult` | `Awaitable[HybridResult]` | ⚠️ SKIP | Async result parsing |
| 405 | `explain` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | No callback - raw |
| 406 | `explain_cli` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw |
| 407 | `aggregate` | `AggregateResult` | `Awaitable[AggregateResult]` | ⚠️ SKIP | Async result parsing |
| 408 | `profile` | `tuple` | `Awaitable[tuple]` | ✅ | Module-specific |
| 409 | `spellcheck` | `dict` | `Awaitable[dict]` | ⚠️ SKIP | Async result parsing |
| 410 | `dict_add` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 411 | `dict_del` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 412 | `dict_dump` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw |
| 413 | `config_set` | `bool` | `Awaitable[bool]` | ⚠️ SKIP | Async result parsing |
| 414 | `config_get` | `dict` | `Awaitable[dict]` | ⚠️ SKIP | Async result parsing |
| 415 | `tagvals` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | No callback - raw |
| 416 | `aliasadd` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 417 | `aliasupdate` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 418 | `aliasdel` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 419 | `sugadd` | `int` | `Awaitable[int]` | ⚠️ SKIP | Async result parsing |
| 420 | `suglen` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 421 | `sugdel` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 422 | `sugget` | `list` | `Awaitable[list]` | ⚠️ SKIP | Async result parsing |
| 423 | `synupdate` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 424 | `syndump` | `dict[bytes \| str, list[bytes \| str]]` | `Awaitable[dict[bytes \| str, list[bytes \| str]]]` | ✅ | No callback - raw |

### BATCH 16: JSONCommands (json/commands.py) - Methods 425-452
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 425 | `arrappend` | `list[int \| None]` | `Awaitable[list[int \| None]]` | ✅ | Module int array |
| 426 | `arrindex` | `list[int \| None]` | `Awaitable[list[int \| None]]` | ✅ | Module int array |
| 427 | `arrinsert` | `list[int \| None]` | `Awaitable[list[int \| None]]` | ✅ | Module int array |
| 428 | `arrlen` | `list[int \| None]` | `Awaitable[list[int \| None]]` | ✅ | Module int array |
| 429 | `arrpop` | `list[bytes \| str \| None]` | `Awaitable[list[bytes \| str \| None]]` | ✅ | Depends on decode_responses |
| 430 | `arrtrim` | `list[int \| None]` | `Awaitable[list[int \| None]]` | ✅ | Module int array |
| 431 | `type` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | Depends on decode_responses |
| 432 | `resp` | `list` | `Awaitable[list]` | ✅ | JSON structure |
| 433 | `objkeys` | `list[list[bytes \| str] \| None]` | `Awaitable[list[list[bytes \| str] \| None]]` | ✅ | Depends on decode_responses |
| 434 | `objlen` | `list[int \| None]` | `Awaitable[list[int \| None]]` | ✅ | Module int array |
| 435 | `numincrby` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | Depends on decode_responses |
| 436 | `nummultby` | `bytes \| str` | `Awaitable[bytes \| str]` | ✅ | Depends on decode_responses |
| 437 | `clear` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 438 | `delete` | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 439 | `get` | `Any` | `Awaitable[Any]` | ✅ | JSON parsed |
| 440 | `mget` | `list[Any]` | `Awaitable[list[Any]]` | ✅ | JSON parsed |
| 441 | `set` | `bool \| None` | `Awaitable[bool \| None]` | ✅ | OK or None |
| 442 | `mset` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 443 | `merge` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 444 | `set_file` | `bool \| None` | `Awaitable[bool \| None]` | 📋 | Explicit |
| 445 | `set_path` | `dict[str, bool]` | `Awaitable[dict[str, bool]]` | 📋 | Explicit |
| 446 | `strlen` | `list[int \| None]` | `Awaitable[list[int \| None]]` | ✅ | Module int array |
| 447 | `toggle` | `bool \| list[bool]` | `Awaitable[bool \| list[bool]]` | ✅ | Module bool |
| 448 | `strappend` | `int \| list[int \| None]` | `Awaitable[int \| list[int \| None]]` | ✅ | Module int |
| 449 | `debug` | `int \| list[bytes \| str]` | `Awaitable[int \| list[bytes \| str]]` | ✅ | Module mixed |
| 450 | `jsonget` | `Any` | `Awaitable[Any]` | ✅ | Deprecated alias |
| 451 | `jsonmget` | `Any` | `Awaitable[Any]` | ✅ | Deprecated alias |
| 452 | `jsonset` | `Any` | `Awaitable[Any]` | ✅ | Deprecated alias |

### BATCH 17: TimeSeriesCommands (timeseries/commands.py) - Methods 453-469
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 453 | `create` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 454 | `alter` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 455 | `add` | `int` | `Awaitable[int]` | ✅ | Timestamp |
| 456 | `madd` | `list[int]` | `Awaitable[list[int]]` | ✅ | Timestamps |
| 457 | `incrby` | `int` | `Awaitable[int]` | ✅ | Timestamp |
| 458 | `decrby` | `int` | `Awaitable[int]` | ✅ | Timestamp |
| 459 | `delete` | `int` | `Awaitable[int]` | ✅ | Count deleted |
| 460 | `createrule` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 461 | `deleterule` | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 462 | `range` | `list[tuple[int, float]]` | `Awaitable[list[tuple[int, float]]]` | ✅ | Parsed samples |
| 463 | `revrange` | `list[tuple[int, float]]` | `Awaitable[list[tuple[int, float]]]` | ✅ | Parsed samples |
| 464 | `mrange` | `list` | `Awaitable[list]` | ✅ | Complex structure |
| 465 | `mrevrange` | `list` | `Awaitable[list]` | ✅ | Complex structure |
| 466 | `get` | `tuple[int, float] \| list` | `Awaitable[tuple[int, float] \| list]` | ✅ | Parsed sample |
| 467 | `mget` | `list` | `Awaitable[list]` | ✅ | Complex structure |
| 468 | `info` | `TSInfo` | `Awaitable[TSInfo]` | 📋 | Explicit type |
| 469 | `queryindex` | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | Depends on decode_responses |

### BATCH 18: BloomFilter Commands (bf/commands.py) - Methods 470-491
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 470 | `create` (BF) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 471 | `add` (BF) | `int` | `Awaitable[int]` | ✅ | 0 or 1 |
| 472 | `madd` (BF) | `list[int]` | `Awaitable[list[int]]` | ✅ | 0s and 1s |
| 473 | `insert` (BF) | `list[int]` | `Awaitable[list[int]]` | ✅ | 0s and 1s |
| 474 | `exists` (BF) | `int` | `Awaitable[int]` | ✅ | 0 or 1 |
| 475 | `mexists` (BF) | `list[int]` | `Awaitable[list[int]]` | ✅ | 0s and 1s |
| 476 | `scandump` (BF) | `tuple[int, bytes \| None]` | `Awaitable[tuple[int, bytes \| None]]` | ✅ | Cursor + data |
| 477 | `loadchunk` (BF) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 478 | `info` (BF) | `dict` | `Awaitable[dict]` | ✅ | Parsed info |
| 479 | `card` (BF) | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 480 | `create` (CF) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 481 | `add` (CF) | `int` | `Awaitable[int]` | ✅ | 0 or 1 |
| 482 | `addnx` (CF) | `int` | `Awaitable[int]` | ✅ | 0 or 1 |
| 483 | `insert` (CF) | `list[int]` | `Awaitable[list[int]]` | ✅ | 0s and 1s |
| 484 | `insertnx` (CF) | `list[int]` | `Awaitable[list[int]]` | ✅ | 0s and 1s |
| 485 | `exists` (CF) | `int` | `Awaitable[int]` | ✅ | 0 or 1 |
| 486 | `mexists` (CF) | `list[int]` | `Awaitable[list[int]]` | ✅ | 0s and 1s |
| 487 | `delete` (CF) | `int` | `Awaitable[int]` | ✅ | 0 or 1 |
| 488 | `count` (CF) | `int` | `Awaitable[int]` | ✅ | Integer reply |
| 489 | `scandump` (CF) | `tuple[int, bytes \| None]` | `Awaitable[tuple[int, bytes \| None]]` | ✅ | Cursor + data |
| 490 | `loadchunk` (CF) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 491 | `info` (CF) | `dict` | `Awaitable[dict]` | ✅ | Parsed info |

### BATCH 19: TOPKCommands + TDigestCommands + CMSCommands (bf/commands.py) - Methods 492-518
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 492 | `reserve` (TOPK) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 493 | `add` (TOPK) | `list[bytes \| str \| None]` | `Awaitable[list[bytes \| str \| None]]` | ✅ | Depends on decode_responses |
| 494 | `incrby` (TOPK) | `list[bytes \| str \| None]` | `Awaitable[list[bytes \| str \| None]]` | ✅ | Depends on decode_responses |
| 495 | `query` (TOPK) | `list[int]` | `Awaitable[list[int]]` | ✅ | 0s and 1s |
| 496 | `count` (TOPK) | `list[int]` | `Awaitable[list[int]]` | ✅ | Counts |
| 497 | `list` (TOPK) | `list[bytes \| str]` | `Awaitable[list[bytes \| str]]` | ✅ | Depends on decode_responses |
| 498 | `info` (TOPK) | `dict` | `Awaitable[dict]` | ✅ | Parsed info |
| 499 | `create` (TD) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 500 | `reset` (TD) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 501 | `add` (TD) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 502 | `merge` (TD) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 503 | `min` (TD) | `float` | `Awaitable[float]` | ✅ | Float reply |
| 504 | `max` (TD) | `float` | `Awaitable[float]` | ✅ | Float reply |
| 505 | `quantile` (TD) | `list[float]` | `Awaitable[list[float]]` | ✅ | Floats |
| 506 | `cdf` (TD) | `list[float]` | `Awaitable[list[float]]` | ✅ | Floats |
| 507 | `info` (TD) | `dict` | `Awaitable[dict]` | ✅ | Parsed info |
| 508 | `trimmed_mean` (TD) | `float` | `Awaitable[float]` | ✅ | Float reply |
| 509 | `rank` (TD) | `list[int]` | `Awaitable[list[int]]` | ✅ | Integers |
| 510 | `revrank` (TD) | `list[int]` | `Awaitable[list[int]]` | ✅ | Integers |
| 511 | `byrank` (TD) | `list[float]` | `Awaitable[list[float]]` | ✅ | Floats |
| 512 | `byrevrank` (TD) | `list[float]` | `Awaitable[list[float]]` | ✅ | Floats |
| 513 | `initbydim` (CMS) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 514 | `initbyprob` (CMS) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 515 | `incrby` (CMS) | `list[int]` | `Awaitable[list[int]]` | ✅ | Integers |
| 516 | `query` (CMS) | `list[int]` | `Awaitable[list[int]]` | ✅ | Integers |
| 517 | `merge` (CMS) | `bool` | `Awaitable[bool]` | ✅ | Module OK |
| 518 | `info` (CMS) | `dict` | `Awaitable[dict]` | ✅ | Parsed info |

### BATCH 20: VectorSetCommands (vectorset/commands.py) - Methods 519-530 ✅ DONE
| # | Method | Sync Type | Async Type | Status | Notes |
|---|--------|-----------|------------|--------|-------|
| 519 | `vadd` | `int` | `Awaitable[int]` | ✅ DONE | Integer reply |
| 520 | `vsim` | `VSimResult` | `Awaitable[VSimResult]` | 📋 DONE | Explicit type |
| 521 | `vdim` | `int` | `Awaitable[int]` | ✅ DONE | Integer reply |
| 522 | `vcard` | `int` | `Awaitable[int]` | ✅ DONE | Integer reply |
| 523 | `vrem` | `int` | `Awaitable[int]` | ✅ DONE | Integer reply |
| 524 | `vemb` | `VEmbResult` | `Awaitable[VEmbResult]` | 📋 DONE | Explicit type |
| 525 | `vlinks` | `VLinksResult` | `Awaitable[VLinksResult]` | 📋 DONE | Explicit type |
| 526 | `vinfo` | `dict` | `Awaitable[dict]` | ✅ DONE | Standard dict |
| 527 | `vsetattr` | `int` | `Awaitable[int]` | ✅ DONE | Integer reply |
| 528 | `vgetattr` | `VGetAttrResult` | `Awaitable[VGetAttrResult]` | 📋 DONE | Explicit type |
| 529 | `vrandmember` | `VRandMemberResult` | `Awaitable[VRandMemberResult]` | 📋 DONE | Explicit type |
| 530 | `vrange` | `list[str]` | `Awaitable[list[str]]` | ✅ DONE | Standard list |

---

## Batch Summary

| Batch | File | Command Class | Methods | Count |
|-------|------|---------------|---------|-------|
| 1 | core.py | ACLCommands | 1-14 | 14 |
| 2 | core.py | ManagementCommands (Part 1) | 15-50 | 36 |
| 3 | core.py | ManagementCommands (Part 2) | 51-93 | 43 |
| 4 | core.py | BasicKeyCommands (Part 1) | 94-130 | 37 |
| 5 | core.py | BasicKeyCommands (Part 2) | 131-156 | 26 |
| 6 | core.py | ListCommands | 157-178 | 22 |
| 7 | core.py | ScanCommands + SetCommands | 179-202 | 24 |
| 8 | core.py | StreamCommands | 203-226 | 24 |
| 9 | core.py | SortedSetCommands | 227-260 | 34 |
| 10 | core.py | HyperlogCommands + HashCommands | 261-289 | 29 |
| 11 | core.py | PubSubCommands + ScriptCommands | 290-306 | 17 |
| 12 | core.py | GeoCommands + ModuleCommands + ClusterCommands + FunctionCommands | 307-335 | 29 |
| 13 | cluster.py | ClusterCommands | 336-378 | 43 |
| 14 | sentinel.py | SentinelCommands | 379-391 | 13 |
| 15 | search/commands.py | SearchCommands | 392-424 | 33 |
| 16 | json/commands.py | JSONCommands | 425-452 | 28 |
| 17 | timeseries/commands.py | TimeSeriesCommands | 453-469 | 17 |
| 18 | bf/commands.py | BFCommands + CFCommands | 470-491 | 22 |
| 19 | bf/commands.py | TOPKCommands + TDigestCommands + CMSCommands | 492-518 | 27 |
| 20 | vectorset/commands.py | VectorSetCommands | 519-530 | 12 ✅ DONE |

---

## Implementation Checklist

For each batch:
- [ ] Review all methods in the batch
- [ ] Skip methods marked with ⚠️ SKIP, 🔄 SKIP, ❌ SKIP
- [ ] For each ✅ method:
  - [ ] Check the Notes column for callback info
  - [ ] Verify return type against `redis/_parsers/helpers.py` callback dictionaries
  - [ ] Add two `@overload` signatures
  - [ ] Run type checker
- [ ] Test changes
- [ ] Mark batch complete

---

## Notes

1. **Callback System is Critical** - Always check the three-tier callback hierarchy in `redis/_parsers/helpers.py`:
   - `_RedisCallbacks` (base) - shared by RESP2 and RESP3
   - `_RedisCallbacksRESP2` - RESP2-specific overrides
   - `_RedisCallbacksRESP3` - RESP3-specific overrides
2. **Protocol-Specific Types** - When RESP2 and RESP3 have different callbacks, use the most permissive union type
3. **Import considerations** - Make sure `TYPE_CHECKING`, `overload`, and `Awaitable` are imported
4. **Self-type discrimination** - Use `self: SyncClientProtocol` and `self: AsyncClientProtocol`
5. **Keep original method** - Only add overloads, don't modify the actual implementation
6. **No callback = raw response** - If a command has no callback, return type depends on `decode_responses`


================================================
FILE: .dockerignore
================================================
**/__pycache__
**/*.pyc
.coverage
.coverage.*


================================================
FILE: .github/CODEOWNERS
================================================
doctests/* @dmaier-redislabs


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
Thanks for wanting to report an issue you've found in redis-py. Please delete this text and fill in the template below.  
It is of course not always possible to reduce your code to a small test case, but it's highly appreciated to have as much data as possible. Thank you!

**Version**: What redis-py and what redis version is the issue happening on?

**Platform**: What platform / version? (For example Python 3.5.1 on Windows 7 / Ubuntu 15.10 / Azure)

**Description**: Description of your issue, stack traces from errors and code that reproduces the issue


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
### Description of change

_Please provide a description of the change here._

### Pull Request check-list

_Please make sure to review and check all of these items:_

- [ ] Do tests and lints pass with this change?
- [ ] Do the CI tests pass with this change (enable it first in your forked repo and wait for the github action build to finish)?
- [ ] Is the new or changed code fully tested?
- [ ] Is a documentation update included (if this change modifies existing APIs, or introduces new ones)?
- [ ] Is there an example added to the examples folder (if applicable)?

_NOTE: these things are not required to open a PR and can be done
afterwards / while the PR is open._


================================================
FILE: .github/actions/run-tests/action.yml
================================================
name: 'Run redis-py tests'
description: 'Runs redis-py tests against different Redis versions and configurations'
inputs:
  python-version:
    description: 'Python version to use for running tests'
    default: '3.12'
  parser-backend:
    description: 'Parser backend to use: plain or hiredis'
    required: true
  redis-version:
    description: 'Redis version to test against'
    required: true
  hiredis-version:
    description: 'hiredis version to test against'
    required: false
    default: '>3.0.0'
  hiredis-branch:
    description: 'hiredis branch to test against'
    required: false
    default: 'master'
  event-loop:
    description: 'Event loop to use'
    required: false
    default: 'asyncio'
  protocol:
    description: 'RESP protocol version to use'
    required: false
    default: 'all'
  repository:
    description: 'Repository to checkout'
    required: false
  ref:
    description: 'Branch to checkout'
    required: false
  client-libs-test-image:
    description: 'Client libs test image tag'
    required: false
runs:
  using: "composite"
  steps:
    - uses: actions/checkout@v4
      with:
        repository: ${{ inputs.repository }}
        ref: ${{ inputs.ref }}

    - uses: actions/setup-python@v5
      with:
        python-version: ${{ inputs.python-version }}
        cache: 'pip'

    - uses: actions/checkout@v4
      if: ${{ inputs.parser-backend == 'hiredis' && inputs.hiredis-version == 'unstable' }}
      with:
        repository: redis/hiredis-py
        submodules: true
        path: hiredis-py
        ref: ${{ inputs.hiredis-branch }}

    - name: Setup Test environment
      env:
        REDIS_VERSION: ${{ inputs.redis-version }}
        CLIENT_LIBS_TEST_IMAGE_TAG: ${{ inputs.client-libs-test-image || inputs.redis-version }}
        CLIENT_LIBS_TEST_STACK_IMAGE_TAG: ${{ inputs.client-libs-test-image }}
      run: |
        set -e

        echo "::group::Installing dependencies"
        pip install -r dev_requirements.txt
        pip uninstall -y redis  # uninstall Redis package installed via redis-entraid
        pip install -e .[jwt]  # install the working copy
        if [ "${{inputs.parser-backend}}" == "hiredis" ]; then
          if [[ "${{inputs.hiredis-version}}" == "unstable" ]]; then
            echo "Installing unstable version of hiredis from local directory"
            pip install -e ./hiredis-py
          else
            pip install "hiredis${{inputs.hiredis-version}}"
          fi
          echo "PARSER_BACKEND=$(echo "${{inputs.parser-backend}}_${{inputs.hiredis-version}}" | sed 's/[^a-zA-Z0-9]/_/g')" >> $GITHUB_ENV
        else
          echo "PARSER_BACKEND=${{inputs.parser-backend}}" >> $GITHUB_ENV
        fi
        echo "::endgroup::"

        echo "::group::Starting Redis servers"
        # Check if REDIS_VERSION is in the custom map
        mapped_version=""
        if [[ -n "${REDIS_VERSION_CUSTOM_MAP:-}" ]]; then
          for mapping in $REDIS_VERSION_CUSTOM_MAP; do
            tag="${mapping%%:*}"
            version="${mapping##*:}"
            if [[ "$REDIS_VERSION" == "$tag" ]]; then
              mapped_version="$version"
              echo "Found custom mapping: $REDIS_VERSION -> $mapped_version"
              break
            fi
          done
        fi
        # Use mapped version if found, otherwise use REDIS_VERSION
        version_to_parse="${mapped_version:-$REDIS_VERSION}"
        redis_major_version=$(echo "$version_to_parse" | grep -oP '^\d+')
        echo "REDIS_MAJOR_VERSION=${redis_major_version}" >> $GITHUB_ENV

        if (( redis_major_version < 8 )); then
          echo "Using redis-stack for module tests"

          # Mapping of redis version to stack version
          declare -A redis_stack_version_mapping=(
            ["7.4.4"]="rs-7.4.0-v5"
            ["7.2.9"]="rs-7.2.0-v17"
          )

          if [[ -v redis_stack_version_mapping[$REDIS_VERSION] ]]; then
            if [[ -z "${CLIENT_LIBS_TEST_STACK_IMAGE_TAG}" ]]; then
              export CLIENT_LIBS_TEST_STACK_IMAGE_TAG=${redis_stack_version_mapping[$REDIS_VERSION]}
            fi
            echo "REDIS_MOD_URL=redis://127.0.0.1:6479/0" >> $GITHUB_ENV
          else
            echo "Version not found in the mapping."
            exit 1
          fi

          if (( redis_major_version < 7 )); then
            export REDIS_STACK_EXTRA_ARGS="--tls-auth-clients optional --save ''"
            export REDIS_EXTRA_ARGS="--tls-auth-clients optional --save ''"
          fi

          invoke devenv --endpoints=all-stack

        else
          echo "Using redis CE for module tests"
          if [[ -z "${CLIENT_LIBS_TEST_STACK_IMAGE_TAG}" ]]; then
            export CLIENT_LIBS_TEST_STACK_IMAGE_TAG=$REDIS_VERSION
          fi
          echo "REDIS_MOD_URL=redis://127.0.0.1:6379" >> $GITHUB_ENV
          invoke devenv --endpoints all
        fi

        sleep 10 # time to settle
        echo "::endgroup::"
      shell: bash

    - name: Run tests
      run: |
        set -e

        run_tests() {
          local protocol=$1
          local eventloop=""

          if [ "${{inputs.event-loop}}" == "uvloop" ]; then
            eventloop="--uvloop"
          fi

          echo "::group::RESP${protocol} standalone tests"
          echo "REDIS_MOD_URL=${REDIS_MOD_URL}"

          if (( $REDIS_MAJOR_VERSION < 7 )) && [ "$protocol" == "3" ]; then
            echo "Skipping module tests: Modules doesn't support RESP3 for Redis versions < 7"
            invoke standalone-tests --redis-mod-url=${REDIS_MOD_URL} $eventloop --protocol="${protocol}" --extra-markers="not redismod and not cp_integration"
          else
            invoke standalone-tests --redis-mod-url=${REDIS_MOD_URL} $eventloop --protocol="${protocol}"
          fi

          echo "::endgroup::"

          echo "::group::RESP${protocol} cluster tests"
          invoke cluster-tests $eventloop --protocol=${protocol}
          echo "::endgroup::"
        }

        if [ "${{inputs.protocol}}" == "all" ]; then
          run_tests 2 "${{inputs.event-loop}}"
          run_tests 3 "${{inputs.event-loop}}"
        else
          run_tests "${{inputs.protocol}}" "${{inputs.event-loop}}"
        fi
      shell: bash

    - name: Debug
      if: failure()
      run: |
        sudo apt-get install -y redis-tools
        echo "Docker Containers:"
        docker ps
        echo "Cluster nodes:"
        redis-cli -p 16379 CLUSTER NODES
      shell: bash

    - name: Upload test results and profiling data
      uses: actions/upload-artifact@v4
      with:
        name: pytest-results-redis_${{inputs.redis-version}}-python_${{inputs.python-version}}-parser_${{env.PARSER_BACKEND}}-el_${{inputs.event-loop}}-protocol_${{inputs.protocol}}
        path: |
          *-results.xml
          prof/**
          profile_output*
        if-no-files-found: error
        retention-days: 10

    - name: Upload codecov coverage
      uses: codecov/codecov-action@v4
      with:
        fail_ci_if_error: false


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    labels:
      - "maintenance"
    schedule:
      interval: "monthly"


================================================
FILE: .github/release-drafter-config.yml
================================================
name-template: '$NEXT_MINOR_VERSION'
tag-template: 'v$NEXT_MINOR_VERSION'
filter-by-commitish: true
commitish: master
autolabeler:
  - label: 'maintenance'
    files:
      - '*.md'
      - '.github/*'
  - label: 'bug'
    branch:
      - '/bug-.+'
  - label: 'maintenance'
    branch:
      - '/maintenance-.+'
  - label: 'feature'
    branch:
      - '/feature-.+'
categories:
  - title: '🔥 Breaking Changes'
    labels:
      - 'breakingchange'
  - title: '🧪 Experimental Features'
    labels:
      - 'experimental'
  - title: '🚀 New Features'
    labels:
      - 'feature'
      - 'enhancement'
  - title: '🐛 Bug Fixes'
    labels:
      - 'fix'
      - 'bugfix'
      - 'bug'
      - 'BUG'
  - title: '🧰 Maintenance'
    labels:
      - 'maintenance'
      - 'dependencies'
      - 'documentation'
      - 'docs'
      - 'testing'
change-template: '- $TITLE (#$NUMBER)'
exclude-labels:
  - 'skip-changelog'
template: |
  # Changes

  $CHANGES

  ## Contributors
  We'd like to thank all the contributors who worked on this release!

  $CONTRIBUTORS



================================================
FILE: .github/spellcheck-settings.yml
================================================
matrix:
- name: Markdown
  expect_match: false
  apsell:
    lang: en
    d: en_US
    ignore-case: true
  dictionary:
    wordlists:
    - .github/wordlist.txt
    output: wordlist.dic
  pipeline:
  - pyspelling.filters.markdown:
      markdown_extensions:
      - markdown.extensions.extra:
  - pyspelling.filters.html:
      comments: false
      attributes:
      - alt
      ignores:
      - ':matches(code, pre)'
      - code
      - pre
      - blockquote
      - img
  sources:
  - '*.md'
  - 'docs/*.rst'
  - 'docs/*.ipynb'


================================================
FILE: .github/wordlist.txt
================================================
APM
ARGV
BFCommands
balancer
CacheImpl
cancelling
CAS
CFCommands
CMSCommands
ClusterNode
ClusterNodes
ClusterPipeline
ClusterPubSub
ConnectionPool
config
CoreCommands
CSC
DatabaseConfig
DNS
EchoHealthCheck
EVAL
EVALSHA
failover
FQDN
Grokzen's
handoff
Healthcheck
HealthCheckPolicies
healthcheck
healthchecks
INCR
init
IOError
Instrumentations
JSONCommands
Jaeger
Ludovico
Magnocavallo
MeterProvider
MetricGroup
millis
MultiDbConfig
MultiDBClient
McCurdy
NOSCRIPT
NoValidDatabaseException
NUMPAT
NUMPT
NUMSUB
OSS
OpenCensus
OpenTelemetry
OpenTracing
Otel
otel
OTelConfig
otlp
OTLPMetricExporter
PeriodicExportingMetricReader
proto
PubSub
READONLY
RediSearch
RedisBloom
RedisCluster
RedisClusterCommands
RedisClusterException
RedisClusters
RedisInstrumentor
RedisJSON
RedisTimeSeries
SHA
SLA
SearchCommands
SentinelCommands
SentinelConnectionPool
Sharded
Solovyov
SpanKind
Specfiying
StatusCode
TCP
TemporaryUnavailableException
TLS
TOPKCommands
TimeSeriesCommands
Uptrace
ValueError
WATCHed
WatchError
api
args
async
asyncio
autoclass
automodule
backoff
bdb
behaviour
bool
boolean
booleans
bysource
charset
del
dev
docstring
docstrings
eg
exc
firsttimersonly
fo
genindex
gmail
hiredis
http
idx
iff
ini
json
keyslot
keyspace
kwarg
kwargs
linters
localhost
lua
makeapullrequest
maxdepth
mget
microservice
microservices
mset
multikey
mykey
nonatomic
observability
opentelemetry
oss
performant
pmessage
png
pre
psubscribe
pubsub
punsubscribe
py
pypi
quickstart
readonly
readwrite
redis
redismodules
reinitialization
replicaof
repo
runtime
sedrik
sharded
sdk
ssl
str
stunnel
subcommands
thevalueofmykey
timeseries
toctree
topk
triaging
txt
un
unicode
url
virtualenv
www
XREAD
XREADGROUP
yaml


================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: [ master ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ master ]

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'python' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
        # Learn more about CodeQL language support at https://git.io/codeql-language-support

    steps:
    - name: Checkout repository
      uses: actions/checkout@v6

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v4
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.
        # queries: ./path/to/local/query, your-org/your-repo/queries@main

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v4

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 https://git.io/JvXDl

    # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
    #    and modify them (or add more) to build your code if your project
    #    uses a compiled language

    #- run: |
    #   make bootstrap
    #   make release

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v4


================================================
FILE: .github/workflows/docs.yaml
================================================
name: Docs CI

on:
  push:
    branches:
      - master
      - '[0-9].[0-9]'
  pull_request:
    branches:
      - master
      - '[0-9].[0-9]'
  schedule:
    - cron: '0 1 * * *' # nightly build

concurrency:
  group: ${{ github.event.pull_request.number || github.ref }}-docs
  cancel-in-progress: true

permissions:
  contents: read  #  to fetch code (actions/checkout)

jobs:

   build-docs:
     name: Build docs
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v6
       - uses: actions/setup-python@v6
         with:
           python-version: "3.10"
           cache: 'pip'
       - name: install deps
         run: |
           sudo apt-get update -yqq
           sudo apt-get install -yqq pandoc make
       - name: run code linters
         run: |
           pip install -r dev_requirements.txt -r docs/requirements.txt
           invoke build-docs

       - name: upload docs
         uses: actions/upload-artifact@v7
         with:
           name: redis-py-docs
           path: |
             docs/_build/html


================================================
FILE: .github/workflows/hiredis-py-integration.yaml
================================================
name: Hiredis-py integration tests

on:
  workflow_dispatch:
    inputs:
      redis-py-branch:
        description: 'redis-py branch to run tests on'
        required: true
        default: 'master'
      hiredis-branch:
        description: 'hiredis-py branch to run tests on'
        required: true
        default: 'master'

concurrency:
  group: ${{ github.event.pull_request.number || github.ref }}-hiredis-integration
  cancel-in-progress: true

permissions:
  contents: read  #  to fetch code (actions/checkout)

env:
  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
  # this speeds up coverage with Python 3.12: https://github.com/nedbat/coveragepy/issues/1665
  COVERAGE_CORE: sysmon
  CURRENT_CLIENT_LIBS_TEST_STACK_IMAGE_TAG: '8.4.0'
  CURRENT_REDIS_VERSION: '8.4.0'

jobs:
  redis_version:
    runs-on: ubuntu-latest
    outputs:
      CURRENT: ${{ env.CURRENT_REDIS_VERSION }}
    steps:
      - name: Compute outputs
        run: |
          echo "CURRENT=${{ env.CURRENT_REDIS_VERSION }}" >> $GITHUB_OUTPUT

  hiredis-tests:
    runs-on: ubuntu-latest
    needs: [redis_version]
    timeout-minutes: 60
    strategy:
      max-parallel: 15
      fail-fast: false
      matrix:
        redis-version: [ '${{ needs.redis_version.outputs.CURRENT }}' ]
        python-version: [ '3.10', '3.14']
        parser-backend: [ 'hiredis' ]
        hiredis-version: [ 'unstable' ]
        event-loop: [ 'asyncio' ]
    env:
      ACTIONS_ALLOW_UNSECURE_COMMANDS: true
    name: Redis ${{ matrix.redis-version }}; Python ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}} (${{ matrix.hiredis-version }}); EL:${{matrix.event-loop}}
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ inputs.redis-py-branch }}
      - name: Run tests
        uses: ./.github/actions/run-tests
        with:
          python-version: ${{ matrix.python-version }}
          parser-backend: ${{ matrix.parser-backend }}
          redis-version: ${{ matrix.redis-version }}
          hiredis-version: ${{ matrix.hiredis-version }}
          hiredis-branch: ${{ inputs.hiredis-branch }}


================================================
FILE: .github/workflows/install_and_test.sh
================================================
#!/bin/bash

set -e

SUFFIX=$1
if [ -z ${SUFFIX} ]; then
    echo "Supply valid python package extension such as whl or tar.gz. Exiting."
    exit 3
fi

script=`pwd`/${BASH_SOURCE[0]}
HERE=`dirname ${script}`
ROOT=`realpath ${HERE}/../..`

cd ${ROOT}
DESTENV=${ROOT}/.venvforinstall
if [ -d ${DESTENV} ]; then
    rm -rf ${DESTENV}
fi
python -m venv ${DESTENV}
source ${DESTENV}/bin/activate
pip install --upgrade --quiet pip
pip install --quiet -r dev_requirements.txt
pip uninstall -y redis  # uninstall Redis package installed via redis-entraid
invoke devenv --endpoints=all-stack
invoke package

# find packages
PKG=`ls ${ROOT}/dist/*.${SUFFIX}`
ls -l ${PKG}

TESTDIR=${ROOT}/STAGETESTS
if [ -d ${TESTDIR} ]; then
    rm -rf ${TESTDIR}
fi
mkdir ${TESTDIR}
cp -R ${ROOT}/tests ${TESTDIR}/tests
cd ${TESTDIR}

# install, run tests
pip install ${PKG}
# Redis tests
pytest -m 'not onlycluster' --ignore=tests/test_scenario --ignore=tests/test_asyncio/test_scenario
# RedisCluster tests
CLUSTER_URL="redis://localhost:16379/0"
CLUSTER_SSL_URL="rediss://localhost:27379/0"
pytest -m 'not onlynoncluster and not redismod and not ssl' \
  --ignore=tests/test_scenario \
  --ignore=tests/test_asyncio/test_scenario \
  --redis-url="${CLUSTER_URL}" \
  --redis-ssl-url="${CLUSTER_SSL_URL}"


================================================
FILE: .github/workflows/integration.yaml
================================================
name: CI

on:
  push:
    paths-ignore:
      - 'docs/**'
      - '**/*.rst'
      - '**/*.md'
    branches:
      - master
      - '[0-9].[0-9]'
  pull_request:
    branches:
      - master
      - '[0-9].[0-9]'
  schedule:
    - cron: '0 1 * * *' # nightly build

concurrency:
  group: ${{ github.event.pull_request.number || github.ref }}-integration
  cancel-in-progress: true

permissions:
  contents: read  #  to fetch code (actions/checkout)

env:
  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
  # this speeds up coverage with Python 3.12: https://github.com/nedbat/coveragepy/issues/1665
  COVERAGE_CORE: sysmon
  # patch releases get included in the base version image when they are published
  # for example after 8.2.1 is published, 8.2 image contains 8.2.1 content
  CURRENT_CLIENT_LIBS_TEST_STACK_IMAGE_TAG: '8.4.0'
  CURRENT_REDIS_VERSION: '8.4.0'
  REDIS_VERSION_CUSTOM_MAP: 'custom-21860421418-debian-amd64:8.6'

jobs:
  dependency-audit:
    name: Dependency audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: pypa/gh-action-pip-audit@v1.0.8
        with:
          inputs: dev_requirements.txt
          ignore-vulns: |
            GHSA-w596-4wvx-j9j6  # subversion related git pull, dependency for pytest. There is no impact here.
            CVE-2026-26007 # dependency for entraid tests
            CVE-2026-32597 # PyJWT does not validate the crit (Critical) Header Parameter defined in RFC 7515, this will be fixed in the next release

  lint:
    name: Code linters
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-python@v6
        with:
          python-version: "3.10"
          cache: 'pip'
      - name: run code linters
        run: |
          pip install -r dev_requirements.txt
          pip uninstall -y redis  # uninstall Redis package installed via redis-entraid
          invoke linters

  redis_version:
    runs-on: ubuntu-latest
    outputs:
      CURRENT: ${{ env.CURRENT_REDIS_VERSION }}
    steps:
      - name: Compute outputs
        run: |
          echo "CURRENT=${{ env.CURRENT_REDIS_VERSION }}" >> $GITHUB_OUTPUT

  tests:
    runs-on: ubuntu-latest
    timeout-minutes: 60
    needs: redis_version
    strategy:
      max-parallel: 30
      fail-fast: false
      matrix:
        redis-version: ['custom-21860421418-debian-amd64', '${{ needs.redis_version.outputs.CURRENT }}', '8.2', '8.0.2' ,'7.4.4', '7.2.9']
        python-version: ['3.10', '3.14']
        parser-backend: ['plain']
        event-loop: ['asyncio']
        protocol: ['2', '3']
    env:
      ACTIONS_ALLOW_UNSECURE_COMMANDS: true
    name: Redis ${{ matrix.redis-version }}; Python ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}}; EL:${{matrix.event-loop}}; Protocol:${{matrix.protocol}}
    steps:
      - uses: actions/checkout@v6
      - name: Run tests
        uses: ./.github/actions/run-tests
        with:
            python-version: ${{ matrix.python-version }}
            parser-backend: ${{ matrix.parser-backend }}
            redis-version: ${{ matrix.redis-version }}
            protocol: ${{ matrix.protocol }}

  python-compatibility-tests:
    runs-on: ubuntu-latest
    needs: [ redis_version ]
    timeout-minutes: 60
    strategy:
      max-parallel: 30
      fail-fast: false
      matrix:
        redis-version: [ '${{ needs.redis_version.outputs.CURRENT }}' ]
        python-version: ['3.11', '3.12', '3.13']
        parser-backend: [ 'plain' ]
        event-loop: [ 'asyncio' ]
        protocol: ['2', '3']
    env:
      ACTIONS_ALLOW_UNSECURE_COMMANDS: true
    name: Redis ${{ matrix.redis-version }}; Python ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}}; EL:${{matrix.event-loop}}; Protocol:${{matrix.protocol}}
    steps:
      - uses: actions/checkout@v6
      - name: Run tests
        uses: ./.github/actions/run-tests
        with:
          python-version: ${{ matrix.python-version }}
          parser-backend: ${{ matrix.parser-backend }}
          redis-version: ${{ matrix.redis-version }}
          protocol: ${{ matrix.protocol }}

  pypy-compatibility-tests:
    runs-on: ubuntu-latest
    needs: [ redis_version ]
    # in pipeline ofter these pypy jobs hang, so their timeout is set
    # to just 50 minutes - close to the max time the jobs are running at the moment
    # adding more tests will make them even slower, and we will need to adjust the timeout
    timeout-minutes: 50
    strategy:
      max-parallel: 30
      fail-fast: false
      matrix:
        redis-version: [ '${{ needs.redis_version.outputs.CURRENT }}' ]
        python-version: ['pypy-3.10', 'pypy-3.11']
        parser-backend: [ 'plain' ]
        event-loop: [ 'asyncio' ]
        protocol: ['2', '3']
    env:
      ACTIONS_ALLOW_UNSECURE_COMMANDS: true
    name: PyPy Redis ${{ matrix.redis-version }}; ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}}; EL:${{matrix.event-loop}}; Protocol:${{matrix.protocol}}
    steps:
      - uses: actions/checkout@v6
      - name: Run tests
        uses: ./.github/actions/run-tests
        with:
          python-version: ${{ matrix.python-version }}
          parser-backend: ${{ matrix.parser-backend }}
          redis-version: ${{ matrix.redis-version }}
          protocol: ${{ matrix.protocol }}

  hiredis-tests:
    runs-on: ubuntu-latest
    needs: [redis_version]
    timeout-minutes: 60
    strategy:
      max-parallel: 30
      fail-fast: false
      matrix:
        redis-version: [ '${{ needs.redis_version.outputs.CURRENT }}' ]
        python-version: [ '3.10', '3.14']
        parser-backend: [ 'hiredis' ]
        hiredis-version: [ '>=3.2.0', '<3.0.0' ]
        event-loop: [ 'asyncio' ]
        protocol: ['2', '3']
    env:
      ACTIONS_ALLOW_UNSECURE_COMMANDS: true
    name: Redis ${{ matrix.redis-version }}; Python ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}} (${{ matrix.hiredis-version }}); EL:${{matrix.event-loop}}; Protocol:${{matrix.protocol}}
    steps:
      - uses: actions/checkout@v6
      - name: Run tests
        uses: ./.github/actions/run-tests
        with:
          python-version: ${{ matrix.python-version }}
          parser-backend: ${{ matrix.parser-backend }}
          redis-version: ${{ matrix.redis-version }}
          hiredis-version: ${{ matrix.hiredis-version }}
          protocol: ${{ matrix.protocol }}

  uvloop-tests:
    runs-on: ubuntu-latest
    needs: [redis_version]
    timeout-minutes: 60
    strategy:
      max-parallel: 30
      fail-fast: false
      matrix:
        redis-version: [ '${{ needs.redis_version.outputs.CURRENT }}' ]
        python-version: [ '3.10', '3.14' ]
        parser-backend: [ 'plain' ]
        event-loop: [ 'uvloop' ]
        protocol: ['2', '3']
    env:
      ACTIONS_ALLOW_UNSECURE_COMMANDS: true
    name: Redis ${{ matrix.redis-version }}; Python ${{ matrix.python-version }}; RESP Parser:${{matrix.parser-backend}}; EL:${{matrix.event-loop}}; Protocol:${{matrix.protocol}}
    steps:
      - uses: actions/checkout@v6
      - name: Run tests
        uses: ./.github/actions/run-tests
        with:
          python-version: ${{ matrix.python-version }}
          parser-backend: ${{ matrix.parser-backend }}
          redis-version: ${{ matrix.redis-version }}
          event-loop: ${{ matrix.event-loop }}
          protocol: ${{ matrix.protocol }}

  build-and-test-package:
    name: Validate building and installing the package
    runs-on: ubuntu-latest
    needs: [redis_version]
    strategy:
      fail-fast: false
      matrix:
        extension: ['tar.gz', 'whl']
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-python@v6
        with:
          python-version: "3.10"
      - name: Run installed unit tests
        env:
          CLIENT_LIBS_TEST_IMAGE_TAG: ${{ env.CURRENT_REDIS_VERSION }}
          CLIENT_LIBS_TEST_STACK_IMAGE_TAG: ${{ env.CURRENT_CLIENT_LIBS_TEST_STACK_IMAGE_TAG }}
        run: |
          bash .github/workflows/install_and_test.sh ${{ matrix.extension }}

  install-package-from-commit:
    name: Install package from commit hash
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ['3.10', '3.11', '3.12', '3.13', '3.14', 'pypy-3.10', 'pypy-3.11']
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-python@v6
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'pip'
      - name: install from pip
        run: |
          pip install --quiet git+${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git@${GITHUB_SHA}


================================================
FILE: .github/workflows/pypi-publish.yaml
================================================
name: Publish tag to Pypi

on:
  release:
    types: [published]
  workflow_dispatch:

permissions:
  contents: read  #  to fetch code (actions/checkout)

jobs:

  build_and_package:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - name: install python
        uses: actions/setup-python@v6
        with:
          python-version: "3.10"
      - run: pip install build twine

      - name: Build package
        run: python -m build .

      - name: Basic package test prior to upload
        run: |
          twine check dist/*

      - name: Publish to Pypi
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{ secrets.PYPI_API_TOKEN }}


================================================
FILE: .github/workflows/release-drafter.yml
================================================
name: Release Drafter

on:
  push:
    # branches to consider in the event; optional, defaults to all
    branches:
      - master

permissions: {}
jobs:
  update_release_draft:
    permissions:
      pull-requests: write  #  to add label to PR (release-drafter/release-drafter)
      contents: write  #  to create a github release (release-drafter/release-drafter)

    runs-on: ubuntu-latest
    steps:
      # Drafts your next Release notes as Pull Requests are merged into "master"
      - uses: release-drafter/release-drafter@v6
        with:
          # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
           config-name: release-drafter-config.yml
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .github/workflows/spellcheck.yml
================================================
name: spellcheck
on:
  pull_request:
jobs:
  check-spelling:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v6
      - name: Check Spelling
        uses: rojopolis/spellcheck-github-actions@0.58.0
        with:
          config_path: .github/spellcheck-settings.yml
          task_name: Markdown


================================================
FILE: .github/workflows/stale-issues.yml
================================================
name: "Stale Issue Management"
on:
  schedule:
    # Run daily at midnight UTC
    - cron: "0 0 * * *"
  workflow_dispatch: # Allow manual triggering

env:
  # Default stale policy timeframes
  DAYS_BEFORE_STALE: 365
  DAYS_BEFORE_CLOSE: 30

  # Accelerated timeline for needs-information issues
  NEEDS_INFO_DAYS_BEFORE_STALE: 30
  NEEDS_INFO_DAYS_BEFORE_CLOSE: 7

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      # First step: Handle regular issues (excluding needs-information)
      - name: Mark regular issues as stale
        uses: actions/stale@v10
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}

          # Default stale policy
          days-before-stale: ${{ env.DAYS_BEFORE_STALE }}
          days-before-close: ${{ env.DAYS_BEFORE_CLOSE }}

          # Explicit stale label configuration
          stale-issue-label: "stale"
          stale-pr-label: "stale"

          stale-issue-message: |
            This issue has been automatically marked as stale due to inactivity.
            It will be closed in 30 days if no further activity occurs.
            If you believe this issue is still relevant, please add a comment to keep it open.

          close-issue-message: |
            This issue has been automatically closed due to inactivity.
            If you believe this issue is still relevant, please reopen it or create a new issue with updated information.

          # Exclude needs-information issues from this step
          exempt-issue-labels: 'no-stale,needs-information'

          # Remove stale label when issue/PR becomes active again
          remove-stale-when-updated: true

          # Apply to pull requests with same timeline
          days-before-pr-stale: ${{ env.DAYS_BEFORE_STALE }}
          days-before-pr-close: ${{ env.DAYS_BEFORE_CLOSE }}

          stale-pr-message: |
            This pull request has been automatically marked as stale due to inactivity.
            It will be closed in 30 days if no further activity occurs.

          close-pr-message: |
            This pull request has been automatically closed due to inactivity.
            If you would like to continue this work, please reopen the PR or create a new one.

          # Only exclude no-stale PRs (needs-information PRs follow standard timeline)
          exempt-pr-labels: 'no-stale'

      # Second step: Handle needs-information issues with accelerated timeline
      - name: Mark needs-information issues as stale
        uses: actions/stale@v10
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}

          # Accelerated timeline for needs-information
          days-before-stale: ${{ env.NEEDS_INFO_DAYS_BEFORE_STALE }}
          days-before-close: ${{ env.NEEDS_INFO_DAYS_BEFORE_CLOSE }}

          # Explicit stale label configuration
          stale-issue-label: "stale"

          # Only target ISSUES with needs-information label (not PRs)
          only-issue-labels: 'needs-information'

          stale-issue-message: |
            This issue has been marked as stale because it requires additional information
            that has not been provided for 30 days. It will be closed in 7 days if the
            requested information is not provided.

          close-issue-message: |
            This issue has been closed because the requested information was not provided within the specified timeframe.
            If you can provide the missing information, please reopen this issue or create a new one.

          # Disable PR processing for this step
          days-before-pr-stale: -1
          days-before-pr-close: -1

          # Remove stale label when issue becomes active again
          remove-stale-when-updated: true


================================================
FILE: .gitignore
================================================
*.pyc
redis.egg-info
build/
dist/
dump.rdb
_build
vagrant/.vagrant
.python-version
.cache
.eggs
.idea
.vscode
.coverage
env
venv
coverage.xml
.venv*
*.xml
.coverage*
prof
profile_output*
docker/stunnel/keys
/dockers/*/node-*/*
/dockers/*/tls/*
/dockers/standalone/
/dockers/cluster/
/dockers/replica/
/dockers/sentinel/
/dockers/redis-stack/
/experimenting/


================================================
FILE: .mypy.ini
================================================
[mypy]
#, docs/examples, tests
files = redis
check_untyped_defs = True
follow_imports_for_stubs asyncio.= True
#disallow_any_decorated = True
disallow_subclassing_any = True
#disallow_untyped_calls = True
disallow_untyped_decorators = True
#disallow_untyped_defs = True
implicit_reexport = False
no_implicit_optional = True
show_error_codes = True
strict_equality = True
warn_incomplete_stub = True
warn_redundant_casts = True
warn_unreachable = True
warn_unused_ignores = True
disallow_any_unimported = True
#warn_return_any = True

[mypy-redis.asyncio.lock]
# TODO: Remove once locks has been rewritten
ignore_errors = True


================================================
FILE: .readthedocs.yml
================================================
version: 2

python:
  install:
    - requirements: docs/requirements.txt
    - method: pip
      path: .

build:
  os: ubuntu-20.04
  tools:
    python: "3.10"

sphinx:
  configuration: docs/conf.py


================================================
FILE: CHANGES
================================================
This file contains only the changes history before redis-py version 4.0.0
After redis-py version 4.0.0 all changes can be found and tracked in the Release notes pubished in GitHub

* 3.5.3 (June 1, 2020)
  * Restore try/except clauses to __del__ methods. These will be removed
      in 4.0 when more explicit resource management if enforced. #1339
  * Update the master_address when Sentinels promote a new master. #847
  * Update SentinelConnectionPool to not forcefully disconnect other in-use
      connections which can negatively affect threaded applications. #1345
* 3.5.2 (May 14, 2020)
  * Tune the locking in ConnectionPool.get_connection so that the lock is
      not held while waiting for the socket to establish and validate the
      TCP connection.
* 3.5.1 (May 9, 2020)
  * Fix for HSET argument validation to allow any non-None key. Thanks
      @AleksMat, #1337, #1341
* 3.5.0 (April 29, 2020)
  * Removed exception trapping from __del__ methods. redis-py objects that
      hold various resources implement __del__ cleanup methods to release
      those resources when the object goes out of scope. This provides a
      fallback for when these objects aren't explicitly closed by user code.
      Prior to this change any errors encountered in closing these resources
      would be hidden from the user. Thanks @jdufresne. #1281
  * Expanded support for connection strings specifying a username connecting
      to pre-v6 servers. #1274
  * Optimized Lock's blocking_timeout and sleep. If the lock cannot be
      acquired and the sleep value would cause the loop to sleep beyond
      blocking_timeout, fail immediately. Thanks @clslgrnc. #1263
  * Added support for passing Python memoryviews to Redis command args that
      expect strings or bytes. The memoryview instance is sent directly to
      the socket such that there are zero copies made of the underlying data
      during command packing. Thanks @Cody-G. #1265, #1285
  * HSET command now can accept multiple pairs. HMSET has been marked as
      deprecated now. Thanks to @laixintao #1271
  * Don't manually DISCARD when encountering an ExecAbortError.
      Thanks @nickgaya, #1300/#1301
  * Reset the watched state of pipelines after calling exec. This saves
      a roundtrip to the server by not having to call UNWATCH within
      Pipeline.reset(). Thanks @nickgaya, #1299/#1302
  * Added the KEEPTTL option for the SET command. Thanks
      @laixintao #1304/#1280
  * Added the MEMORY STATS command. #1268
  * Lock.extend() now has a new option, `replace_ttl`. When False (the
      default), Lock.extend() adds the `additional_time` to the lock's existing
      TTL. When replace_ttl=True, the lock's existing TTL is replaced with
      the value of `additional_time`.
  * Add testing and support for PyPy.
* 3.4.1
  * Move the username argument in the Redis and Connection classes to the
      end of the argument list. This helps those poor souls that specify all
      their connection options as non-keyword arguments. #1276
  * Prior to ACL support, redis-py ignored the username component of
      Connection URLs. With ACL support, usernames are no longer ignored and
      are used to authenticate against an ACL rule. Some cloud vendors with
      managed Redis instances (like Heroku) provide connection URLs with a
      username component pre-ACL that is not intended to be used. Sending that
      username to Redis servers < 6.0.0 results in an error. Attempt to detect
      this condition and retry the AUTH command with only the password such
      that authentication continues to work for these users. #1274
  * Removed the __eq__ hooks to Redis and ConnectionPool that were added
      in 3.4.0. This ended up being a bad idea as two separate connection
      pools be considered equal yet manage a completely separate set of
      connections.
* 3.4.0
  * Allow empty pipelines to be executed if there are WATCHed keys.
      This is a convenient way to test if any of the watched keys changed
      without actually running any other commands. Thanks @brianmaissy.
      #1233, #1234
  * Removed support for end of life Python 3.4.
  * Added support for all ACL commands in Redis 6. Thanks @IAmATeaPot418
      for helping.
  * Pipeline instances now always evaluate to True. Prior to this change,
      pipeline instances relied on __len__ for boolean evaluation which
      meant that pipelines with no commands on the stack would be considered
      False. #994
  * Client instances and Connection pools now support a 'client_name'
      argument. If supplied, all connections created will call CLIENT SETNAME
      as soon as the connection is opened. Thanks to @Habbie for supplying
      the basis of this change. #802
  * Added the 'ssl_check_hostname' argument to specify whether SSL
      connections should require the server hostname to match the hostname
      specified in the SSL cert. By default 'ssl_check_hostname' is False
      for backwards compatibility. #1196
  * Slightly optimized command packing. Thanks @Deneby67. #1255
  * Added support for the TYPE argument to SCAN. Thanks @netocp. #1220
  * Better thread and fork safety in ConnectionPool and
      BlockingConnectionPool. Added better locking to synchronize critical
      sections rather than relying on CPython-specific implementation details
      relating to atomic operations. Adjusted how the pools identify and
      deal with a fork. Added a ChildDeadlockedError exception that is
      raised by child processes in the very unlikely chance that a deadlock
      is encountered. Thanks @gmbnomis, @mdellweg, @yht804421715. #1270,
      #1138, #1178, #906, #1262
  * Added __eq__ hooks to the Redis and ConnectionPool classes.
      Thanks @brainix. #1240
* 3.3.11
  * Further fix for the SSLError -> TimeoutError mapping to work
      on obscure releases of Python 2.7.
* 3.3.10
  * Fixed a potential error handling bug for the SSLError -> TimeoutError
      mapping introduced in 3.3.9. Thanks @zbristow. #1224
* 3.3.9
  * Mapped Python 2.7 SSLError to TimeoutError where appropriate. Timeouts
      should now consistently raise TimeoutErrors on Python 2.7 for both
      unsecured and secured connections. Thanks @zbristow. #1222
* 3.3.8
  * Fixed MONITOR parsing to properly parse IPv6 client addresses, unix
      socket connections and commands issued from Lua. Thanks @kukey. #1201
* 3.3.7
  * Fixed a regression introduced in 3.3.0 where socket.error exceptions
      (or subclasses) could potentially be raised instead of
      redis.exceptions.ConnectionError. #1202
* 3.3.6
  * Fixed a regression in 3.3.5 that caused PubSub.get_message() to raise
      a socket.timeout exception when passing a timeout value. #1200
* 3.3.5
  * Fix an issue where socket.timeout errors could be handled by the wrong
      exception handler in Python 2.7.
* 3.3.4
  * More specifically identify nonblocking read errors for both SSL and
      non-SSL connections. 3.3.1, 3.3.2 and 3.3.3 on Python 2.7 could
      potentially mask a ConnectionError. #1197
* 3.3.3
  * The SSL module in Python < 2.7.9 handles non-blocking sockets
      differently than 2.7.9+. This patch accommodates older versions. #1197
* 3.3.2
  * Further fixed a regression introduced in 3.3.0 involving SSL and
      non-blocking sockets. #1197
* 3.3.1
  * Fixed a regression introduced in 3.3.0 involving SSL and non-blocking
      sockets. #1197
* 3.3.0
  * Resolve a race condition with the PubSubWorkerThread. #1150
  * Cleanup socket read error messages. Thanks Vic Yu. #1159
  * Cleanup the Connection's selector correctly. Thanks Bruce Merry. #1153
  * Added a Monitor object to make working with MONITOR output easy.
      Thanks Roey Prat #1033
  * Internal cleanup: Removed the legacy Token class which was necessary
      with older version of Python that are no longer supported. #1066
  * Response callbacks are now case insensitive. This allows users that
      call Redis.execute_command() directly to pass lower-case command
      names and still get reasonable responses. #1168
  * Added support for hiredis-py 1.0.0 encoding error support. This should
      make the PythonParser and the HiredisParser behave identically
      when encountering encoding errors. Thanks Brian Candler. #1161/#1162
  * All authentication errors now properly raise AuthenticationError.
      AuthenticationError is now a subclass of ConnectionError, which will
      cause the connection to be disconnected and cleaned up appropriately.
      #923
  * Add READONLY and READWRITE commands. Thanks @theodesp. #1114
  * Remove selectors in favor of nonblocking sockets. Selectors had
      issues in some environments including eventlet and gevent. This should
      resolve those issues with no other side effects.
  * Fixed an issue with XCLAIM and previously claimed but not removed
      messages. Thanks @thomdask. #1192/#1191
  * Allow for single connection client instances. These instances
      are not thread safe but offer other benefits including a subtle
      performance increase.
  * Added extensive health checks that keep the connections lively.
      Passing the "health_check_interval=N" option to the Redis client class
      or to a ConnectionPool ensures that a round trip PING/PONG is successful
      before any command if the underlying connection has been idle for more
      than N seconds. ConnectionErrors and TimeoutErrors are automatically
      retried once for health checks.
  * Changed the PubSubWorkerThread to use a threading.Event object rather
      than a boolean to control the thread's life cycle. Thanks Timothy
      Rule. #1194/#1195.
  * Fixed a bug in Pipeline error handling that would incorrectly retry
      ConnectionErrors.
* 3.2.1
  * Fix SentinelConnectionPool to work in multiprocess/forked environments.
* 3.2.0
  * Added support for `select.poll` to test whether data can be read
      on a socket. This should allow for significantly more connections to
      be used with pubsub. Fixes #486/#1115
  * Attempt to guarantee that the ConnectionPool hands out healthy
      connections. Healthy connections are those that have an established
      socket connection to the Redis server, are ready to accept a command
      and have no data available to read. Fixes #1127/#886
  * Use the socket.IPPROTO_TCP constant instead of socket.SOL_TCP.
      IPPROTO_TCP is available on more interpreters (Jython for instance).
      Thanks @Junnplus. #1130
  * Fixed a regression introduced in 3.0 that mishandles exceptions not
      derived from the base Exception class. KeyboardInterrupt and
      gevent.timeout notable. Thanks Christian Fersch. #1128/#1129
  * Significant improvements to handing connections with forked processes.
      Parent and child processes no longer trample on each others' connections.
      Thanks to Jay Rolette for the patch and highlighting this issue.
      #504/#732/#784/#863
  * PythonParser no longer closes the associated connection's socket. The
      connection itself will close the socket. #1108/#1085
* 3.1.0
  * Connection URLs must have one of the following schemes:
      redis://, rediss://, unix://. Thanks @jdupl123. #961/#969
  * Fixed an issue with retry_on_timeout logic that caused some TimeoutErrors
      to be retried. Thanks Aaron Yang. #1022/#1023
  * Added support for SNI for SSL. Thanks @oridistor and Roey Prat. #1087
  * Fixed ConnectionPool repr for pools with no connections. Thanks
      Cody Scott. #1043/#995
  * Fixed GEOHASH to return a None value when specifying a place that
      doesn't exist on the server. Thanks @guybe7. #1126
  * Fixed XREADGROUP to return an empty dictionary for messages that
      have been deleted but still exist in the unacknowledged queue. Thanks
      @xeizmendi. #1116
  * Added an owned method to Lock objects. owned returns a boolean
      indicating whether the current lock instance still owns the lock.
      Thanks Dave Johansen. #1112
  * Allow lock.acquire() to accept an optional token argument. If
      provided, the token argument is used as the unique value used to claim
      the lock. Thankd Dave Johansen. #1112
  * Added a reacquire method to Lock objects. reacquire attempts to renew
      the lock such that the timeout is extended to the same value that the
      lock was initially acquired with. Thanks Ihor Kalnytskyi. #1014
  * Stream names found within XREAD and XREADGROUP responses now properly
      respect the decode_responses flag.
  * XPENDING_RANGE now requires the user the specify the min, max and
      count arguments. Newer versions of Redis prevent count from being
      infinite so it's left to the user to specify these values explicitly.
  * ZADD now returns None when xx=True and incr=True and an element
      is specified that doesn't exist in the sorted set. This matches
      what the server returns in this case. #1084
  * Added client_kill_filter that accepts various filters to identify
      and kill clients. Thanks Theofanis Despoudis. #1098
  * Fixed a race condition that occurred when unsubscribing and
      resubscribing to the same channel or pattern in rapid succession.
      Thanks Marcin Raczyński. #764
  * Added a LockNotOwnedError that is raised when trying to extend or
      release a lock that is no longer owned. This is a subclass of LockError
      so previous code should continue to work as expected. Thanks Joshua
      Harlow. #1095
  * Fixed a bug in GEORADIUS that forced decoding of places without
      respecting the decode_responses option. Thanks Bo Bayles. #1082
* 3.0.1
  * Fixed regression with UnixDomainSocketConnection caused by 3.0.0.
      Thanks Jyrki Muukkonen
  * Fixed an issue with the new asynchronous flag on flushdb and flushall.
      Thanks rogeryen
  * Updated Lock.locked() method to indicate whether *any* process has
      acquired the lock, not just the current one. This is in line with
      the behavior of threading.Lock. Thanks Alan Justino da Silva
* 3.0.0
  BACKWARDS INCOMPATIBLE CHANGES
  * When using a Lock as a context manager and the lock fails to be acquired
      a LockError is now raised. This prevents the code block inside the
      context manager from being executed if the lock could not be acquired.
  * Renamed LuaLock to Lock.
  * Removed the pipeline based Lock implementation in favor of the LuaLock
      implementation.
  * Only bytes, strings and numbers (ints, longs and floats) are acceptable
      for keys and values. Previously redis-py attempted to cast other types
      to str() and store the result. This caused must confusion and frustration
      when passing boolean values (cast to 'True' and 'False') or None values
      (cast to 'None'). It is now the user's responsibility to cast all
      key names and values to bytes, strings or numbers before passing the
      value to redis-py.
  * The StrictRedis class has been renamed to Redis. StrictRedis will
      continue to exist as an alias of Redis for the foreseeable future.
  * The legacy Redis client class has been removed. It caused much confusion
      to users.
  * ZINCRBY arguments 'value' and 'amount' have swapped order to match the
      the Redis server. The new argument order is: keyname, amount, value.
  * MGET no longer raises an error if zero keys are passed in. Instead an
      empty list is returned.
  * MSET and MSETNX now require all keys/values to be specified in a single
      dictionary argument named mapping. This was changed to allow for future
      options to these commands in the future.
  * ZADD now requires all element names/scores be specified in a single
      dictionary argument named mapping. This was required to allow the NX,
      XX, CH and INCR options to be specified.
  * ssl_cert_reqs now has a default value of 'required' by default. This
      should make connecting to a remote Redis server over SSL more secure.
      Thanks u2mejc
  * Removed support for EOL Python 2.6 and 3.3. Thanks jdufresne
  OTHER CHANGES
  * Added missing DECRBY command. Thanks derek-dchu
  * CLUSTER INFO and CLUSTER NODES responses are now properly decoded to
      strings.
  * Added a 'locked()' method to Lock objects. This method returns True
      if the lock has been acquired and owned by the current process,
      otherwise False.
  * EXISTS now supports multiple keys. It's return value is now the number
      of keys in the list that exist.
  * Ensure all commands can accept key names as bytes. This fixes issues
      with BLPOP, BRPOP and SORT.
  * All errors resulting from bad user input are raised as DataError
      exceptions. DataError is a subclass of RedisError so this should be
      transparent to anyone previously catching these.
  * Added support for NX, XX, CH and INCR options to ZADD
  * Added support for the MIGRATE command
  * Added support for the MEMORY USAGE and MEMORY PURGE commands. Thanks
      Itamar Haber
  * Added support for the 'asynchronous' argument to FLUSHDB and FLUSHALL
      commands. Thanks Itamar Haber
  * Added support for the BITFIELD command. Thanks Charles Leifer and
      Itamar Haber
  * Improved performance on pipeline requests with large chunks of data.
      Thanks tzickel
  * Fixed test suite to not fail if another client is connected to the
      server the tests are running against.
  * Added support for SWAPDB. Thanks Itamar Haber
  * Added support for all STREAM commands. Thanks Roey Prat and Itamar Haber
  * SHUTDOWN now accepts the 'save' and 'nosave' arguments. Thanks
      dwilliams-kenzan
  * Added support for ZPOPMAX, ZPOPMIN, BZPOPMAX, BZPOPMIN. Thanks
      Itamar Haber
  * Added support for the 'type' argument in CLIENT LIST. Thanks Roey Prat
  * Added support for CLIENT PAUSE. Thanks Roey Prat
  * Added support for CLIENT ID and CLIENT UNBLOCK. Thanks Itamar Haber
  * GEODIST now returns a None value when referencing a place that does
      not exist. Thanks qingping209
  * Added a ping() method to pubsub objects. Thanks krishan-carbon
  * Fixed a bug with keys in the INFO dict that contained ':' symbols.
      Thanks mzalimeni
  * Fixed the select system call retry compatibility with Python 2.x.
      Thanks lddubeau
  * max_connections is now a valid querystring argument for creating
      connection pools from URLs. Thanks mmaslowskicc
  * Added the UNLINK command. Thanks yozel
  * Added socket_type option to Connection for configurability.
      Thanks garlicnation
  * Lock.do_acquire now atomically sets acquires the lock and sets the
      expire value via set(nx=True, px=timeout). Thanks 23doors
  * Added 'count' argument to SPOP. Thanks AlirezaSadeghi
  * Fixed an issue parsing client_list responses that contained an '='.
      Thanks swilly22
* 2.10.6
  * Various performance improvements. Thanks cjsimpson
  * Fixed a bug with SRANDMEMBER where the behavior for `number=0` did
      not match the spec. Thanks Alex Wang
  * Added HSTRLEN command. Thanks Alexander Putilin
  * Added the TOUCH command. Thanks Anis Jonischkeit
  * Remove unnecessary calls to the server when registering Lua scripts.
      Thanks Ben Greenberg
  * SET's EX and PX arguments now allow values of zero. Thanks huangqiyin
  * Added PUBSUB {CHANNELS, NUMPAT, NUMSUB} commands. Thanks Angus Pearson
  * PubSub connections that encounter `InterruptedError`s now
      retry automatically. Thanks Carlton Gibson and Seth M. Larson
  * LPUSH and RPUSH commands run on PyPy now correctly returns the number
      of items of the list. Thanks Jeong YunWon
  * Added support to automatically retry socket EINTR errors. Thanks
      Thomas Steinacher
  * PubSubWorker threads started with `run_in_thread` are now daemonized
      so the thread shuts down when the running process goes away. Thanks
      Keith Ainsworth
  * Added support for GEO commands. Thanks Pau Freixes, Alex DeBrie and
      Abraham Toriz
  * Made client construction from URLs smarter. Thanks Tim Savage
  * Added support for CLUSTER * commands. Thanks Andy Huang
  * The RESTORE command now accepts an optional `replace` boolean.
      Thanks Yoshinari Takaoka
  * Attempt to connect to a new Sentinel if a TimeoutError occurs. Thanks
      Bo Lopker
  * Fixed a bug in the client's `__getitem__` where a KeyError would be
      raised if the value returned by the server is an empty string.
      Thanks Javier Candeira.
  * Socket timeouts when connecting to a server are now properly raised
      as TimeoutErrors.
* 2.10.5
  * Allow URL encoded parameters in Redis URLs. Characters like a "/" can
      now be URL encoded and redis-py will correctly decode them. Thanks
      Paul Keene.
  * Added support for the WAIT command. Thanks <https://github.com/eshizhan>
  * Better shutdown support for the PubSub Worker Thread. It now properly
      cleans up the connection, unsubscribes from any channels and patterns
      previously subscribed to and consumes any waiting messages on the socket.
  * Added the ability to sleep for a brief period in the event of a
      WatchError occurring. Thanks Joshua Harlow.
  * Fixed a bug with pipeline error reporting when dealing with characters
      in error messages that could not be encoded to the connection's
      character set. Thanks Hendrik Muhs.
  * Fixed a bug in Sentinel connections that would inadvertently connect
      to the master when the connection pool resets. Thanks
      <https://github.com/df3n5>
  * Better timeout support in Pubsub get_message. Thanks Andy Isaacson.
  * Fixed a bug with the HiredisParser that would cause the parser to
      get stuck in an endless loop if a specific number of bytes were
      delivered from the socket. This fix also increases performance of
      parsing large responses from the Redis server.
  * Added support for ZREVRANGEBYLEX.
  * ConnectionErrors are now raised if Redis refuses a connection due to
      the maxclients limit being exceeded. Thanks Roman Karpovich.
  * max_connections can now be set when instantiating client instances.
      Thanks Ohad Perry.
* 2.10.4
    (skipped due to a PyPI snafu)
* 2.10.3
  * Fixed a bug with the bytearray support introduced in 2.10.2. Thanks
      Josh Owen.
* 2.10.2
  * Added support for Hiredis's new bytearray support. Thanks
      <https://github.com/tzickel>
  * POSSIBLE BACKWARDS INCOMPATIBLE CHANGE: Fixed a possible race condition
      when multiple threads share the same Lock instance with a timeout. Lock
      tokens are now stored in thread local storage by default. If you have
      code that acquires a lock in one thread and passes that lock instance to
      another thread to release it, you need to disable thread local storage.
      Refer to the doc strings on the Lock class about the thread_local
      argument information.
  * Fixed a regression in from_url where "charset" and "errors" weren't
      valid options. "encoding" and "encoding_errors" are still accepted
      and preferred.
  * The "charset" and "errors" options have been deprecated. Passing
      either to StrictRedis.__init__ or from_url will still work but will
      also emit a DeprecationWarning. Instead use the "encoding" and
      "encoding_errors" options.
  * Fixed a compatibility bug with Python 3 when the server closes a
      connection.
  * Added BITPOS command. Thanks <https://github.com/jettify>.
  * Fixed a bug when attempting to send large values to Redis in a Pipeline.
* 2.10.1
  * Fixed a bug where Sentinel connections to a server that's no longer a
      master and receives a READONLY error will disconnect and reconnect to
      the master.
* 2.10.0
  * Discontinued support for Python 2.5. Upgrade. You'll be happier.
  * The HiRedis parser will now properly raise ConnectionErrors.
  * Completely refactored PubSub support. Fixes all known PubSub bugs and
      adds a bunch of new features. Docs can be found in the README under the
      new "Publish / Subscribe" section.
  * Added the new HyperLogLog commands (PFADD, PFCOUNT, PFMERGE). Thanks
      Pepijn de Vos and Vincent Ohprecio.
  * Updated TTL and PTTL commands with Redis 2.8+ semantics. Thanks Markus
      Kaiserswerth.
  * *SCAN commands now return a long (int on Python3) cursor value rather
      than the string representation. This might be slightly backwards
incompatible in code using*SCAN commands loops such as
      "while cursor != '0':".
  * Added extra *SCAN commands that return iterators instead of the normal
      [cursor, data] type. Use scan_iter, hscan_iter, sscan_iter, and
      zscan_iter for iterators. Thanks Mathieu Longtin.
  * Added support for SLOWLOG commands. Thanks Rick van Hattem.
  * Added lexicographical commands ZRANGEBYLEX, ZREMRANGEBYLEX, and ZLEXCOUNT
      for sorted sets.
  * Connection objects now support an optional argument, socket_read_size,
      indicating how much data to read during each socket.recv() call. After
      benchmarking, increased the default size to 64k, which dramatically
      improves performance when fetching large values, such as many results
      in a pipeline or a large (>1MB) string value.
  * Improved the pack_command and send_packed_command functions to increase
      performance when sending large (>1MB) values.
  * Sentinel Connections to master servers now detect when a READONLY error
      is encountered and disconnect themselves and all other active connections
      to the same master so that the new master can be discovered.
  * Fixed Sentinel state parsing on Python 3.
  * Added support for SENTINEL MONITOR, SENTINEL REMOVE, and SENTINEL SET
      commands. Thanks Greg Murphy.
  * INFO output that doesn't follow the "key:value" format will now be
      appended to a key named "__raw__" in the INFO dictionary. Thanks Pedro
      Larroy.
  * The "vagrant" directory contains a complete vagrant environment for
      redis-py developers. The environment runs a Redis master, a Redis slave,
      and 3 Sentinels. Future iterations of the test suite will incorporate
      more integration style tests, ensuring things like failover happen
      correctly.
  * It's now possible to create connection pool instances from a URL.
      StrictRedis.from_url() now uses this feature to create a connection pool
      instance and use that when creating a new client instance. Thanks
      <https://github.com/chillipino>
  * When creating client instances or connection pool instances from an URL,
      it's now possible to pass additional options to the connection pool with
      querystring arguments.
  * Fixed a bug where some encodings (like utf-16) were unusable on Python 3
      as command names and literals would get encoded.
  * Added an SSLConnection class that allows for secure connections through
      stunnel or other means. Construct an SSL connection with the ssl=True
      option on client classes, using the rediss:// scheme from an URL, or
      by passing the SSLConnection class to a connection pool's
      connection_class argument. Thanks <https://github.com/oranagra>.
  * Added a socket_connect_timeout option to control how long to wait while
      establishing a TCP connection before timing out. This lets the client
      fail fast when attempting to connect to a downed server while keeping
      a more lenient timeout for all other socket operations.
  * Added TCP Keep-alive support by passing use the socket_keepalive=True
      option. Finer grain control can be achieved using the
      socket_keepalive_options option which expects a dictionary with any of
      the keys (socket.TCP_KEEPIDLE, socket.TCP_KEEPCNT, socket.TCP_KEEPINTVL)
      and integers for values. Thanks Yossi Gottlieb.
  * Added a `retry_on_timeout` option that controls how socket.timeout errors
      are handled. By default it is set to False and will cause the client to
      raise a TimeoutError anytime a socket.timeout is encountered. If
      `retry_on_timeout` is set to True, the client will retry a command that
      timed out once like other `socket.error`s.
  * Completely refactored the Lock system. There is now a LuaLock class
      that's used when the Redis server is capable of running Lua scripts along
      with a fallback class for Redis servers < 2.6. The new locks fix several
      subtle race consider that the old lock could face. In additional, a
      new method, "extend" is available on lock instances that all a lock
      owner to extend the amount of time they have the lock for. Thanks to
      Eli Finkelshteyn and <https://github.com/chillipino> for contributions.
* 2.9.1
  * IPv6 support. Thanks <https://github.com/amashinchi>
* 2.9.0
  * Performance improvement for packing commands when using the PythonParser.
      Thanks Guillaume Viot.
  * Executing an empty pipeline transaction no longer sends MULTI/EXEC to
      the server. Thanks EliFinkelshteyn.
  * Errors when authenticating (incorrect password) and selecting a database
      now close the socket.
  * Full Sentinel support thanks to Vitja Makarov. Thanks!
  * Better repr support for client and connection pool instances. Thanks
      Mark Roberts.
  * Error messages that the server sends to the client are now included
      in the client error message. Thanks Sangjin Lim.
  * Added the SCAN, SSCAN, HSCAN, and ZSCAN commands. Thanks Jingchao Hu.
  * ResponseErrors generated by pipeline execution provide addition context
      including the position of the command in the pipeline and the actual
      command text generated the error.
  * ConnectionPools now play nicer in threaded environments that fork. Thanks
      Christian Joergensen.
* 2.8.0
  * redis-py should play better with gevent when a gevent Timeout is raised.
      Thanks leifkb.
  * Added SENTINEL command. Thanks Anna Janackova.
  * Fixed a bug where pipelines could potentially corrupt a connection
      if the MULTI command generated a ResponseError. Thanks EliFinkelshteyn
      for the report.
  * Connections now call socket.shutdown() prior to socket.close() to
      ensure communication ends immediately per the note at
      <https://docs.python.org/2/library/socket.html#socket.socket.close>
      Thanks to David Martin for pointing this out.
  * Lock checks are now based on floats rather than ints. Thanks
      Vitja Makarov.
* 2.7.6
  * Added CONFIG RESETSTAT command. Thanks Yossi Gottlieb.
  * Fixed a bug introduced in 2.7.3 that caused issues with script objects
      and pipelines. Thanks Carpentier Pierre-Francois.
  * Converted redis-py's test suite to use the awesome py.test library.
  * Fixed a bug introduced in 2.7.5 that prevented a ConnectionError from
      being raised when the Redis server is LOADING data.
  * Added a BusyLoadingError exception that's raised when the Redis server
      is starting up and not accepting commands yet. BusyLoadingError
      subclasses ConnectionError, which this state previously returned.
      Thanks Yossi Gottlieb.
* 2.7.5
  * DEL, HDEL and ZREM commands now return the numbers of keys deleted
      instead of just True/False.
  * from_url now supports URIs with a port number. Thanks Aaron Westendorf.
* 2.7.4
  * Added missing INCRBY method. Thanks Krzysztof Dorosz.
  * SET now accepts the EX, PX, NX and XX options from Redis 2.6.12. These
      options will generate errors if these options are used when connected
      to a Redis server < 2.6.12. Thanks George Yoshida.
* 2.7.3
  * Fixed a bug with BRPOPLPUSH and lists with empty strings.
  * All empty except: clauses have been replaced to only catch Exception
      subclasses. This prevents a KeyboardInterrupt from triggering exception
      handlers. Thanks Lucian Branescu Mihaila.
  * All exceptions that are the result of redis server errors now share a
      command Exception subclass, ServerError. Thanks Matt Robenolt.
  * Prevent DISCARD from being called if MULTI wasn't also called. Thanks
      Pete Aykroyd.
  * SREM now returns an integer indicating the number of items removed from
      the set. Thanks <https://github.com/ronniekk>.
  * Fixed a bug with BGSAVE and BGREWRITEAOF response callbacks with Python3.
      Thanks Nathan Wan.
  * Added CLIENT GETNAME and CLIENT SETNAME commands.
      Thanks <https://github.com/bitterb>.
  * It's now possible to use len() on a pipeline instance to determine the
      number of commands that will be executed. Thanks Jon Parise.
  * Fixed a bug in INFO's parse routine with floating point numbers. Thanks
      Ali Onur Uyar.
  * Fixed a bug with BITCOUNT to allow `start` and `end` to both be zero.
      Thanks Tim Bart.
  * The transaction() method now accepts a boolean keyword argument,
      value_from_callable. By default, or if False is passes, the transaction()
      method will return the value of the pipelines execution. Otherwise, it
      will return whatever func() returns.
  * Python3 compatibility fix ensuring we're not already bytes(). Thanks
      Salimane Adjao Moustapha.
  * Added PSETEX. Thanks YAMAMOTO Takashi.
  * Added a BlockingConnectionPool to limit the number of connections that
      can be created. Thanks James Arthur.
  * SORT now accepts a `groups` option that if specified, will return
      tuples of n-length, where n is the number of keys specified in the GET
      argument. This allows for convenient row-based iteration. Thanks
      Ionuț Arțăriși.
* 2.7.2
  * Parse errors are now *always* raised on multi/exec pipelines, regardless
      of the `raise_on_error` flag. See
      <https://groups.google.com/forum/?hl=en&fromgroups=#!topic/redis-db/VUiEFT8U8U0>
      for more info.
* 2.7.1
  * Packaged tests with source code
* 2.7.0
  * Added BITOP and BITCOUNT commands. Thanks Mark Tozzi.
  * Added the TIME command. Thanks Jason Knight.
  * Added support for LUA scripting. Thanks to Angus Peart, Drew Smathers,
      Issac Kelly, Louis-Philippe Perron, Sean Bleier, Jeffrey Kaditz, and
      Dvir Volk for various patches and contributions to this feature.
  * Changed the default error handling in pipelines. By default, the first
      error in a pipeline will now be raised. A new parameter to the
      pipeline's execute, `raise_on_error`, can be set to False to keep the
      old behavior of embeedding the exception instances in the result.
  * Fixed a bug with pipelines where parse errors won't corrupt the
      socket.
  * Added the optional `number` argument to SRANDMEMBER for use with
      Redis 2.6+ servers.
  * Added PEXPIRE/PEXPIREAT/PTTL commands. Thanks Luper Rouch.
  * Added INCRBYFLOAT/HINCRBYFLOAT commands. Thanks Nikita Uvarov.
  * High precision floating point values won't lose their precision when
      being sent to the Redis server. Thanks Jason Oster and Oleg Pudeyev.
  * Added CLIENT LIST/CLIENT KILL commands
* 2.6.2
  * `from_url` is now available as a classmethod on client classes. Thanks
      Jon Parise for the patch.
  * Fixed several encoding errors resulting from the Python 3.x support.
* 2.6.1
  * Python 3.x support! Big thanks to Alex Grönholm.
  * Fixed a bug in the PythonParser's read_response that could hide an error
      from the client (#251).
* 2.6.0
  * Changed (p)subscribe and (p)unsubscribe to no longer return messages
      indicating the channel was subscribed/unsubscribed to. These messages
      are available in the listen() loop instead. This is to prevent the
      following scenario:
    * Client A is subscribed to "foo"
    * Client B publishes message to "foo"
    * Client A subscribes to channel "bar" at the same time.
      Prior to this change, the subscribe() call would return the published
      messages on "foo" rather than the subscription confirmation to "bar".
  * Added support for GETRANGE, thanks Jean-Philippe Caruana
  * A new setting "decode_responses" specifies whether return values from
      Redis commands get decoded automatically using the client's charset
      value. Thanks to Frankie Dintino for the patch.
* 2.4.13
  * redis.from_url() can take an URL representing a Redis connection string
      and return a client object. Thanks Kenneth Reitz for the patch.
* 2.4.12
  * ConnectionPool is now fork-safe. Thanks Josiah Carson for the patch.
* 2.4.11
  * AuthenticationError will now be correctly raised if an invalid password
      is supplied.
  * If Hiredis is unavailable, the HiredisParser will raise a RedisError
      if selected manually.
  * Made the INFO command more tolerant of Redis changes formatting. Fix
      for #217.
* 2.4.10
  * Buffer reads from socket in the PythonParser. Fix for a Windows-specific
      bug (#205).
  * Added the OBJECT and DEBUG OBJECT commands.
  * Added __del__ methods for classes that hold on to resources that need to
      be cleaned up. This should prevent resource leakage when these objects
      leave scope due to misuse or unhandled exceptions. Thanks David Wolever
      for the suggestion.
  * Added the ECHO command for completeness.
  * Fixed a bug where attempting to subscribe to a PubSub channel of a Redis
      server that's down would blow out the stack. Fixes #179 and #195. Thanks
      Ovidiu Predescu for the test case.
  * StrictRedis's TTL command now returns a -1 when querying a key with no
      expiration. The Redis class continues to return None.
  * ZADD and SADD now return integer values indicating the number of items
      added. Thanks Homer Strong.
  * Renamed the base client class to StrictRedis, replacing ZADD and LREM in
      favor of their official argument order. The Redis class is now a subclass
      of StrictRedis, implementing the legacy redis-py implementations of ZADD
      and LREM. Docs have been updated to suggesting the use of StrictRedis.
  * SETEX in StrictRedis is now compliant with official Redis SETEX command.
      the name, value, time implementation moved to "Redis" for backwards
      compatibility.
* 2.4.9
  * Removed socket retry logic in Connection. This is the responsibility of
      the caller to determine if the command is safe and can be retried. Thanks
      David Wolver.
  * Added some extra guards around various types of exceptions being raised
      when sending or parsing data. Thanks David Wolver and Denis Bilenko.
* 2.4.8
  * Imported with_statement from __future__ for Python 2.5 compatibility.
* 2.4.7
  * Fixed a bug where some connections were not getting released back to the
      connection pool after pipeline execution.
  * Pipelines can now be used as context managers. This is the preferred way
      of use to ensure that connections get cleaned up properly. Thanks
      David Wolever.
  * Added a convenience method called transaction() on the base Redis class.
      This method eliminates much of the boilerplate used when using pipelines
      to watch Redis keys. See the documentation for details on usage.
* 2.4.6
  * Variadic arguments for SADD, SREM, ZREN, HDEL, LPUSH, and RPUSH. Thanks
      Raphaël Vinot.
  * (CRITICAL) Fixed an error in the Hiredis parser that occasionally caused
      the socket connection to become corrupted and unusable. This became
      noticeable once connection pools started to be used.
  * ZRANGE, ZREVRANGE, ZRANGEBYSCORE, and ZREVRANGEBYSCORE now take an
      additional optional argument, score_cast_func, which is a callable used
      to cast the score value in the return type. The default is float.
  * Removed the PUBLISH method from the PubSub class. Connections that are
      [P]SUBSCRIBEd cannot issue PUBLISH commands, so it doesn't make sense
      to have it here.
  * Pipelines now contain WATCH and UNWATCH. Calling WATCH or UNWATCH from
      the base client class will result in a deprecation warning. After
      WATCHing one or more keys, the pipeline will be placed in immediate
      execution mode until UNWATCH or MULTI are called. Refer to the new
      pipeline docs in the README for more information. Thanks to David Wolever
      and Randall Leeds for greatly helping with this.
* 2.4.5
  * The PythonParser now works better when reading zero length strings.
* 2.4.4
  * Fixed a typo introduced in 2.4.3
* 2.4.3
  * Fixed a bug in the UnixDomainSocketConnection caused when trying to
      form an error message after a socket error.
* 2.4.2
  * Fixed a bug in pipeline that caused an exception while trying to
      reconnect after a connection timeout.
* 2.4.1
  * Fixed a bug in the PythonParser if disconnect is called before connect.
* 2.4.0
  * WARNING: 2.4 contains several backwards incompatible changes.
  * Completely refactored Connection objects. Moved much of the Redis
      protocol packing for requests here, and eliminated the nasty dependencies
      it had on the client to do AUTH and SELECT commands on connect.
  * Connection objects now have a parser attribute. Parsers are responsible
      for reading data Redis sends. Two parsers ship with redis-py: a
      PythonParser and the HiRedis parser. redis-py will automatically use the
      HiRedis parser if you have the Python hiredis module installed, otherwise
      it will fall back to the PythonParser. You can force or the other, or even
      an external one by passing the `parser_class` argument to ConnectionPool.
  * Added a UnixDomainSocketConnection for users wanting to talk to the Redis
      instance running on a local machine only. You can use this connection
      by passing it to the `connection_class` argument of the ConnectionPool.
  * Connections no longer derive from threading.local. See threading.local
      note below.
  * ConnectionPool has been completely refactored. The ConnectionPool now
      maintains a list of connections. The redis-py client only hangs on to
      a ConnectionPool instance, calling get_connection() anytime it needs to
      send a command. When get_connection() is called, the command name and
      any keys involved in the command are passed as arguments. Subclasses of
      ConnectionPool could use this information to identify the shard the keys
      belong to and return a connection to it. ConnectionPool also implements
      disconnect() to force all connections in the pool to disconnect from
      the Redis server.
  * redis-py no longer support the SELECT command. You can still connect to
      a specific database by specifying it when instantiating a client instance
      or by creating a connection pool. If you need to talk to multiple
      databases within your application, you should use a separate client
      instance for each database you want to talk to.
  * Completely refactored Publish/Subscribe support. The subscribe and listen
      commands are no longer available on the redis-py Client class. Instead,
      the `pubsub` method returns an instance of the PubSub class which contains
      all publish/subscribe support. Note, you can still PUBLISH from the
      redis-py client class if you desire.
  * Removed support for all previously deprecated commands or options.
  * redis-py no longer uses threading.local in any way. Since the Client
      class no longer holds on to a connection, it's no longer needed. You can
      now pass client instances between threads, and commands run on those
      threads will retrieve an available connection from the pool, use it and
      release it. It should now be trivial to use redis-py with eventlet or
      greenlet.
  * ZADD now accepts pairs of value=score keyword arguments. This should help
      resolve the long standing #72. The older value and score arguments have
      been deprecated in favor of the keyword argument style.
  * Client instances now get their own copy of RESPONSE_CALLBACKS. The new
      set_response_callback method adds a user defined callback to the instance.
  * Support Jython, fixing #97. Thanks to Adam Vandenberg for the patch.
  * Using __getitem__ now properly raises a KeyError when the key is not
      found. Thanks Ionuț Arțăriși for the patch.
  * Newer Redis versions return a LOADING message for some commands while
      the database is loading from disk during server start. This could cause
      problems with SELECT. We now force a socket disconnection prior to
      raising a ResponseError so subsequent connections have to reconnect and
      re-select the appropriate database. Thanks to Benjamin Anderson for
      finding this and fixing.
* 2.2.4
  * WARNING: Potential backwards incompatible change - Changed order of
      parameters of ZREVRANGEBYSCORE to match those of the actual Redis command.
      This is only backwards-incompatible if you were passing max and min via
      keyword args. If passing by normal args, nothing in user code should have
      to change. Thanks Stéphane Angel for the fix.
  * Fixed INFO to properly parse the Redis data correctly for both 2.2.x and
      2.3+. Thanks Stéphane Angel for the fix.
  * Lock objects now store their timeout value as a float. This allows floats
      to be used as timeout values. No changes to existing code required.
  * WATCH now supports multiple keys. Thanks Rich Schumacher.
  * Broke out some code that was Python 2.4 incompatible. redis-py should
      now be usable on 2.4, but this hasn't actually been tested. Thanks
      Dan Colish for the patch.
  * Optimized some code using izip and islice. Should have a pretty good
      speed up on larger data sets. Thanks Dan Colish.
  * Better error handling when submitting an empty mapping to HMSET. Thanks
      Dan Colish.
  * Subscription status is now reset after every (re)connection.
* 2.2.3
  * Added support for Hiredis. To use, simply "pip install hiredis" or
      "easy_install hiredis". Thanks for Pieter Noordhuis for the hiredis-py
      bindings and the patch to redis-py.
  * The connection class is chosen based on whether hiredis is installed
      or not. To force the use of the PythonConnection, simply create
      your own ConnectionPool instance with the connection_class argument
      assigned to to PythonConnection class.
  * Added missing command ZREVRANGEBYSCORE. Thanks Jay Baird for the patch.
  * The INFO command should be parsed correctly on 2.2.x server versions
      and is backwards compatible with older versions. Thanks Brett Hoerner.
* 2.2.2
  * Fixed a bug in ZREVRANK where retrieving the rank of a value not in
      the zset would raise an error.
  * Fixed a bug in Connection.send where the errno import was getting
      overwritten by a local variable.
  * Fixed a bug in SLAVEOF when promoting an existing slave to a master.
  * Reverted change of download URL back to redis-VERSION.tar.gz. 2.2.1's
      change of this actually broke Pypi for Pip installs. Sorry!
* 2.2.1
  * Changed archive name to redis-py-VERSION.tar.gz to not conflict
      with the Redis server archive.
* 2.2.0
  * Implemented SLAVEOF
  * Implemented CONFIG as config_get and config_set
  * Implemented GETBIT/SETBIT
  * Implemented BRPOPLPUSH
  * Implemented STRLEN
  * Implemented PERSIST
  * Implemented SETRANGE
  * Changed type annotation of the `num` parameter in `zrange` from `int` to `Optional[int]

================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

## Introduction

We appreciate your interest in considering contributing to redis-py.
Community contributions mean a lot to us.

## Contributions we need

You may already know how you'd like to contribute, whether it's a fix for a bug you
encountered, or a new feature your team wants to use.

If you don't know where to start, consider improving
documentation, bug triaging, and writing tutorials are all examples of
helpful contributions that mean less work for you.

## Your First Contribution

Unsure where to begin contributing? You can start by looking through
[help-wanted
issues](https://github.com/andymccurdy/redis-py/issues?q=is%3Aopen+is%3Aissue+label%3ahelp-wanted).

Never contributed to open source before? Here are a couple of friendly
tutorials:

-   <http://makeapullrequest.com/>
-   <http://www.firsttimersonly.com/>

## Getting Started

Here's how to get started with your code contribution:

1.  Create your own fork of redis-py
2.  Do the changes in your fork
3.  Create a virtualenv and install the development dependencies from the dev_requirements.txt file:
    ```
    python -m venv .venv
    source .venv/bin/activate
    pip install -r dev_requirements.txt
    pip install -e .[jwt]
    ```

4.  If you need a development environment, run `invoke devenv`. Note: this relies on docker-compose to build environments, and assumes that you have a version supporting [docker profiles](https://docs.docker.com/compose/profiles/).
5.  While developing, make sure the tests pass by running `invoke tests`
6.  If you like the change and think the project could use it, send a
    pull request

To see what else is part of the automation, run `invoke -l`

## The Development Environment

Running `invoke devenv` installs the development dependencies specified
in the dev_requirements.txt. It starts all of the dockers used by this
project, and leaves them running. These can be easily cleaned up with
`invoke clean`. NOTE: it is assumed that the user running these tests,
can execute docker and its various commands.

-   A master Redis node
-   A Redis replica node
-   Three sentinel Redis nodes
-   A redis cluster
-   An stunnel docker, fronting the master Redis node

The replica node, is a replica of the master node, using the
[leader-follower replication](https://redis.io/topics/replication)
feature.

The sentinels monitor the master node in a [sentinel high-availability
configuration](https://redis.io/topics/sentinel).

## Testing

Call `invoke tests` to run all tests, or `invoke all-tests` to run linters
tests as well. With the 'tests' and 'all-tests' targets, all Redis and
RedisCluster tests will be run.

It is possible to run only Redis client tests (with cluster mode disabled) by
using `invoke standalone-tests`; similarly, RedisCluster tests can be run by using
`invoke cluster-tests`.

Each run of tests starts and stops the various dockers required. Sometimes
things get stuck, an `invoke clean` can help.

## Linting

Call `invoke linters` to run linters without also running tests.  
Call `invoke linters-fix` to run linters and automatically fix issues.

## Documentation

If relevant, update the code documentation, via docstrings, or in `/docs`.

You can check how the documentation looks locally by running `invoke build-docs`
and loading the generated HTML files in a browser.

Historically there is a mix of styles in the docstrings, but the preferred way
of documenting code is by applying the
[Google style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html).
Type hints should be added according to PEP484, and should not be repeated in
the docstrings.

### Docker Tips

Following are a few tips that can help you work with the Docker-based
development environment.

To get a bash shell inside of a container:

`$ docker run -it <service> /bin/bash`

Containers run a minimal Debian image that probably lacks tools you want
to use. To install packages, first get a bash session (see previous tip)
and then run:

`$ apt update && apt install <package>`

You can see the logging output of a containers like this:

`$ docker logs -f <service>`

### Troubleshooting

If you get any errors when running `make dev` or `make test`, make sure
that you are using supported versions of Docker.

Please try at least versions of Docker.

-   Docker 19.03.12

## How to Report a Bug

### Security Vulnerabilities

**NOTE**: If you find a security vulnerability, do NOT open an issue.
Email [Redis Open Source (<oss@redis.com>)](mailto:oss@redis.com) instead.

In order to determine whether you are dealing with a security issue, ask
yourself these two questions:

-   Can I access something that's not mine, or something I shouldn't
    have access to?
-   Can I disable something for other people?

If the answer to either of those two questions are *yes*, then you're
probably dealing with a security issue. Note that even if you answer
*no*  to both questions, you may still be dealing with a security
issue, so if you're unsure, just email [us](mailto:oss@redis.com).

### Everything Else

When filing an issue, make sure to answer these five questions:

1.  What version of redis-py are you using?
2.  What version of redis are you using?
3.  What did you do?
4.  What did you expect to see?
5.  What did you see instead?

## Suggest a feature or enhancement

If you'd like to contribute a new feature, make sure you check our
issue list to see if someone has already proposed it. Work may already
be underway on the feature you want or we may have rejected a
feature like it already.

If you don't see anything, open a new issue that describes the feature
you would like and how it should work.

## Code review process

The core team regularly looks at pull requests. We will provide
feedback as soon as possible. After receiving our feedback, please respond
within two weeks. After that time, we may close your PR if it isn't
showing any activity.


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2022-2023, Redis, inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# redis-py

The Python interface to the Redis key-value store.

[![CI](https://github.com/redis/redis-py/workflows/CI/badge.svg?branch=master)](https://github.com/redis/redis-py/actions?query=workflow%3ACI+branch%3Amaster)
[![docs](https://readthedocs.org/projects/redis/badge/?version=stable&style=flat)](https://redis.readthedocs.io/en/stable/)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/redis/redis-py/blob/master/LICENSE)
[![pypi](https://badge.fury.io/py/redis.svg)](https://pypi.org/project/redis/)
[![pre-release](https://img.shields.io/github/v/release/redis/redis-py?include_prereleases&label=latest-prerelease)](https://github.com/redis/redis-py/releases)
[![codecov](https://codecov.io/gh/redis/redis-py/branch/master/graph/badge.svg?token=yenl5fzxxr)](https://codecov.io/gh/redis/redis-py)

[Installation](#installation) |  [Usage](#usage) | [Advanced Topics](#advanced-topics) | [Contributing](https://github.com/redis/redis-py/blob/master/CONTRIBUTING.md)

---------------------------------------------

**Note:** redis-py 5.0 is the last version of redis-py that supports Python 3.7, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 5.1 supports Python 3.8+.<br>
**Note:** redis-py 6.1.0 is the last version of redis-py that supports Python 3.8, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 6.2.0 supports Python 3.9+.

---------------------------------------------

## How do I Redis?

[Learn for free at Redis University](https://redis.io/learn/university)

[Try the Redis Cloud](https://redis.io/try-free/)

[Dive in developer tutorials](https://redis.io/learn)

[Join the Redis community](https://redis.io/community/)

[Work at Redis](https://redis.io/careers/)

## Installation

Start a redis via docker (for Redis versions >= 8.0):

``` bash
docker run -p 6379:6379 -it redis:latest
```

Start a redis via docker (for Redis versions < 8.0):

``` bash
docker run -p 6379:6379 -it redis/redis-stack:latest
```
To install redis-py, simply:

``` bash
$ pip install redis
```

For faster performance, install redis with hiredis support, this provides a compiled response parser, and *for most cases* requires zero code changes.
By default, if hiredis >= 1.0 is available, redis-py will attempt to use it for response parsing.

``` bash
$ pip install "redis[hiredis]"
```

Looking for a high-level library to handle object mapping? See [redis-om-python](https://github.com/redis/redis-om-python)!

## Supported Redis Versions

The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES), [8.0](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES) and [8.2](https://github.com/redis/redis/blob/8.2/00-RELEASENOTES).

The table below highlights version compatibility of the most-recent library versions and redis versions.

| Library version | Supported redis versions |
|-----------------|-------------------|
| 3.5.3 | <= 6.2 Family of releases |
| >= 4.5.0 | Version 5.0 to 7.0 |
| >= 5.0.0 | Version 5.0 to 7.4 |
| >= 6.0.0 | Version 7.2 to current |


## Usage

### Basic Example

``` python
>>> import redis
>>> r = redis.Redis(host='localhost', port=6379, db=0)
>>> r.set('foo', 'bar')
True
>>> r.get('foo')
b'bar'
```

The above code connects to localhost on port 6379, sets a value in Redis, and retrieves it. All responses are returned as bytes in Python, to receive decoded strings, set *decode_responses=True*.  For this, and more connection options, see [these examples](https://redis.readthedocs.io/en/stable/examples.html).


#### RESP3 Support
To enable support for RESP3, ensure you have at least version 5.0 of the client, and change your connection object to include *protocol=3*

``` python
>>> import redis
>>> r = redis.Redis(host='localhost', port=6379, db=0, protocol=3)
```

### Connection Pools

By default, redis-py uses a connection pool to manage connections. Each instance of a Redis class receives its own connection pool. You can however define your own [redis.ConnectionPool](https://redis.readthedocs.io/en/stable/connections.html#connection-pools).

``` python
>>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
>>> r = redis.Redis(connection_pool=pool)
```

Alternatively, you might want to look at [Async connections](https://redis.readthedocs.io/en/stable/examples/asyncio_examples.html), or [Cluster connections](https://redis.readthedocs.io/en/stable/connections.html#cluster-client), or even [Async Cluster connections](https://redis.readthedocs.io/en/stable/connections.html#async-cluster-client).

### Redis Commands

There is built-in support for all of the [out-of-the-box Redis commands](https://redis.io/commands). They are exposed using the raw Redis command names (`HSET`, `HGETALL`, etc.) except where a word (i.e. del) is reserved by the language. The complete set of commands can be found [here](https://github.com/redis/redis-py/tree/master/redis/commands), or [the documentation](https://redis.readthedocs.io/en/stable/commands.html).

## Advanced Topics

The [official Redis command documentation](https://redis.io/commands)
does a great job of explaining each command in detail. redis-py attempts
to adhere to the official command syntax. There are a few exceptions:

-   **MULTI/EXEC**: These are implemented as part of the Pipeline class.
    The pipeline is wrapped with the MULTI and EXEC statements by
    default when it is executed, which can be disabled by specifying
    transaction=False. See more about Pipelines below.

-   **SUBSCRIBE/LISTEN**: Similar to pipelines, PubSub is implemented as
    a separate class as it places the underlying connection in a state
    where it can\'t execute non-pubsub commands. Calling the pubsub
    method from the Redis client will return a PubSub instance where you
    can subscribe to channels and listen for messages. You can only call
    PUBLISH from the Redis client (see [this comment on issue
    #151](https://github.com/redis/redis-py/issues/151#issuecomment-1545015)
    for details).

For more details, please see the documentation on [advanced topics page](https://redis.readthedocs.io/en/stable/advanced_features.html).

### Pipelines

The following is a basic example of a [Redis pipeline](https://redis.io/docs/manual/pipelining/), a method to optimize round-trip calls, by batching Redis commands, and receiving their results as a list.


``` python
>>> pipe = r.pipeline()
>>> pipe.set('foo', 5)
>>> pipe.set('bar', 18.5)
>>> pipe.set('blee', "hello world!")
>>> pipe.execute()
[True, True, True]
```

### PubSub

The following example shows how to utilize [Redis Pub/Sub](https://redis.io/docs/manual/pubsub/) to subscribe to specific channels.

``` python
>>> r = redis.Redis(...)
>>> p = r.pubsub()
>>> p.subscribe('my-first-channel', 'my-second-channel', ...)
>>> p.get_message()
{'pattern': None, 'type': 'subscribe', 'channel': b'my-second-channel', 'data': 1}
```

### Redis’ search and query capabilities default dialect

Release 6.0.0 introduces a client-side default dialect for Redis’ search and query capabilities.
By default, the client now overrides the server-side dialect with version 2, automatically appending *DIALECT 2* to commands like *FT.AGGREGATE* and *FT.SEARCH*.

**Important**: Be aware that the query dialect may impact the results returned. If needed, you can revert to a different dialect version by configuring the client accordingly.

``` python
>>> from redis.commands.search.field import TextField
>>> from redis.commands.search.query import Query
>>> from redis.commands.search.index_definition import IndexDefinition
>>> import redis

>>> r = redis.Redis(host='localhost', port=6379, db=0)
>>> r.ft().create_index(
>>>     (TextField("name"), TextField("lastname")),
>>>     definition=IndexDefinition(prefix=["test:"]),
>>> )

>>> r.hset("test:1", "name", "James")
>>> r.hset("test:1", "lastname", "Brown")

>>> # Query with default DIALECT 2
>>> query = "@name: James Brown"
>>> q = Query(query)
>>> res = r.ft().search(q)

>>> # Query with explicit DIALECT 1
>>> query = "@name: James Brown"
>>> q = Query(query).dialect(1)
>>> res = r.ft().search(q)
```

You can find further details in the [query dialect documentation](https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/dialects/).

### Multi-database client (Active-Active)

The multi-database client allows your application to connect to multiple Redis databases, which are typically replicas of each other. It is designed to work with Redis Software and Redis Cloud Active-Active setups. The client continuously monitors database health, detects failures, and automatically fails over to the next healthy database using a configurable strategy. When the original database becomes healthy again, the client can automatically switch back to it.<br>
This is useful when:

1. You have more than one Redis deployment. This might include two independent Redis servers or two or more Redis databases replicated across multiple [active-active Redis Enterprise](https://redis.io/docs/latest/operate/rs/databases/active-active/) clusters.
2. You want your application to connect to one deployment at a time and to fail over to the next available deployment if the first deployment becomes unavailable.

For the complete failover configuration options and examples, see the [Multi-database client docs](https://redis.readthedocs.io/en/latest/multi_database.html).

---------------------------------------------

### Author

redis-py is developed and maintained by [Redis Inc](https://redis.io). It can be found [here](
https://github.com/redis/redis-py), or downloaded from [pypi](https://pypi.org/project/redis/).

Special thanks to:

-   Andy McCurdy (<sedrik@gmail.com>) the original author of redis-py.
-   Ludovico Magnocavallo, author of the original Python Redis client,
    from which some of the socket code is still used.
-   Alexander Solovyov for ideas on the generic response callback
    system.
-   Paul Hubbard for initial packaging support.

[![Redis](https://github.com/redis/redis-py/blob/master/docs/_static/logo-redis.svg)](https://redis.io)


================================================
FILE: benchmarks/__init__.py
================================================


================================================
FILE: benchmarks/base.py
================================================
import functools
import itertools
import sys
import timeit

import redis


class Benchmark:
    ARGUMENTS = ()

    def __init__(self):
        self._client = None

    def get_client(self, **kwargs):
        # eventually make this more robust and take optional args from
        # argparse
        if self._client is None or kwargs:
            defaults = {"db": 9}
            defaults.update(kwargs)
            pool = redis.ConnectionPool(**kwargs)
            self._client = redis.Redis(connection_pool=pool)
        return self._client

    def setup(self, **kwargs):
        pass

    def run(self, **kwargs):
        pass

    def run_benchmark(self):
        group_names = [group["name"] for group in self.ARGUMENTS]
        group_values = [group["values"] for group in self.ARGUMENTS]
        for value_set in itertools.product(*group_values):
            pairs = list(zip(group_names, value_set))
            arg_string = ", ".join(f"{p[0]}={p[1]}" for p in pairs)
            sys.stdout.write(f"Benchmark: {arg_string}... ")
            sys.stdout.flush()
            kwargs = dict(pairs)
            setup = functools.partial(self.setup, **kwargs)
            run = functools.partial(self.run, **kwargs)
            t = timeit.timeit(stmt=run, setup=setup, number=1000)
            sys.stdout.write(f"{t:f}\n")
            sys.stdout.flush()


================================================
FILE: benchmarks/basic_operations.py
================================================
import time
from argparse import ArgumentParser
from functools import wraps

import redis


def parse_args():
    parser = ArgumentParser()
    parser.add_argument(
        "-n", type=int, help="Total number of requests (default 100000)", default=100000
    )
    parser.add_argument(
        "-P",
        type=int,
        help=("Pipeline <numreq> requests. Default 1 (no pipeline)."),
        default=1,
    )
    parser.add_argument(
        "-s",
        type=int,
        help="Data size of SET/GET value in bytes (default 2)",
        default=2,
    )

    args = parser.parse_args()
    return args


def run():
    args = parse_args()
    r = redis.Redis()
    r.flushall()
    set_str(conn=r, num=args.n, pipeline_size=args.P, data_size=args.s)
    set_int(conn=r, num=args.n, pipeline_size=args.P, data_size=args.s)
    get_str(conn=r, num=args.n, pipeline_size=args.P, data_size=args.s)
    get_int(conn=r, num=args.n, pipeline_size=args.P, data_size=args.s)
    incr(conn=r, num=args.n, pipeline_size=args.P, data_size=args.s)
    lpush(conn=r, num=args.n, pipeline_size=args.P, data_size=args.s)
    lrange_300(conn=r, num=args.n, pipeline_size=args.P, data_size=args.s)
    lpop(conn=r, num=args.n, pipeline_size=args.P, data_size=args.s)
    hmset(conn=r, num=args.n, pipeline_size=args.P, data_size=args.s)


def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.monotonic()
        ret = func(*args, **kwargs)
        duration = time.monotonic() - start
        if "num" in kwargs:
            count = kwargs["num"]
        else:
            count = args[1]
        print(f"{func.__name__} - {count} Requests")
        print(f"Duration  = {duration}")
        print(f"Rate = {count / duration}")
        print()
        return ret

    return wrapper


@timer
def set_str(conn, num, pipeline_size, data_size):
    if pipeline_size > 1:
        conn = conn.pipeline()

    set_data = "a".ljust(data_size, "0")
    for i in range(num):
        conn.set(f"set_str:{i}", set_data)
        if pipeline_size > 1 and i % pipeline_size == 0:
            conn.execute()

    if pipeline_size > 1:
        conn.execute()


@timer
def set_int(conn, num, pipeline_size, data_size):
    if pipeline_size > 1:
        conn = conn.pipeline()

    set_data = 10 ** (data_size - 1)
    for i in range(num):
        conn.set(f"set_int:{i}", set_data)
        if pipeline_size > 1 and i % pipeline_size == 0:
            conn.execute()

    if pipeline_size > 1:
        conn.execute()


@timer
def get_str(conn, num, pipeline_size, data_size):
    if pipeline_size > 1:
        conn = conn.pipeline()

    for i in range(num):
        conn.get(f"set_str:{i}")
        if pipeline_size > 1 and i % pipeline_size == 0:
            conn.execute()

    if pipeline_size > 1:
        conn.execute()


@timer
def get_int(conn, num, pipeline_size, data_size):
    if pipeline_size > 1:
        conn = conn.pipeline()

    for i in range(num):
        conn.get(f"set_int:{i}")
        if pipeline_size > 1 and i % pipeline_size == 0:
            conn.execute()

    if pipeline_size > 1:
        conn.execute()


@timer
def incr(conn, num, pipeline_size, *args, **kwargs):
    if pipeline_size > 1:
        conn = conn.pipeline()

    for i in range(num):
        conn.incr("incr_key")
        if pipeline_size > 1 and i % pipeline_size == 0:
            conn.execute()

    if pipeline_size > 1:
        conn.execute()


@timer
def lpush(conn, num, pipeline_size, data_size):
    if pipeline_size > 1:
        conn = conn.pipeline()

    set_data = 10 ** (data_size - 1)
    for i in range(num):
        conn.lpush("lpush_key", set_data)
        if pipeline_size > 1 and i % pipeline_size == 0:
            conn.execute()

    if pipeline_size > 1:
        conn.execute()


@timer
def lrange_300(conn, num, pipeline_size, data_size):
    if pipeline_size > 1:
        conn = conn.pipeline()

    for i in range(num):
        conn.lrange("lpush_key", i, i + 300)
        if pipeline_size > 1 and i % pipeline_size == 0:
            conn.execute()

    if pipeline_size > 1:
        conn.execute()


@timer
def lpop(conn, num, pipeline_size, data_size):
    if pipeline_size > 1:
        conn = conn.pipeline()
    for i in range(num):
        conn.lpop("lpush_key")
        if pipeline_size > 1 and i % pipeline_size == 0:
            conn.execute()
    if pipeline_size > 1:
        conn.execute()


@timer
def hmset(conn, num, pipeline_size, data_size):
    if pipeline_size > 1:
        conn = conn.pipeline()

    set_data = {"str_value": "string", "int_value": 123456, "float_value": 123456.0}
    for i in range(num):
        conn.hmset("hmset_key", set_data)
        if pipeline_size > 1 and i % pipeline_size == 0:
            conn.execute()

    if pipeline_size > 1:
        conn.execute()


if __name__ == "__main__":
    run()


================================================
FILE: benchmarks/cluster_async.py
================================================
import asyncio
import functools
import time

import aioredis_cluster
import aredis
import uvloop

import redis.asyncio as redispy


def timer(func):
    @functools.wraps(func)
    async def wrapper(*args, **kwargs):
        tic = time.perf_counter()
        await func(*args, **kwargs)
        toc = time.perf_counter()
        return f"{toc - tic:.4f}"

    return wrapper


@timer
async def set_str(client, gather, data):
    if gather:
        for _ in range(count // 100):
            await asyncio.gather(
                *(
                    asyncio.create_task(client.set(f"bench:str_{i}", data))
                    for i in range(100)
                )
            )
    else:
        for i in range(count):
            await client.set(f"bench:str_{i}", data)


@timer
async def set_int(client, gather, data):
    if gather:
        for _ in range(count // 100):
            await asyncio.gather(
                *(
                    asyncio.create_task(client.set(f"bench:int_{i}", data))
                    for i in range(100)
                )
            )
    else:
        for i in range(count):
            await client.set(f"bench:int_{i}", data)


@timer
async def get_str(client, gather):
    if gather:
        for _ in range(count // 100):
            await asyncio.gather(
                *(asyncio.create_task(client.get(f"bench:str_{i}")) for i in range(100))
            )
    else:
        for i in range(count):
            await client.get(f"bench:str_{i}")


@timer
async def get_int(client, gather):
    if gather:
        for _ in range(count // 100):
            await asyncio.gather(
                *(asyncio.create_task(client.get(f"bench:int_{i}")) for i in range(100))
            )
    else:
        for i in range(count):
            await client.get(f"bench:int_{i}")


@timer
async def hset(client, gather, data):
    if gather:
        for _ in range(count // 100):
            await asyncio.gather(
                *(
                    asyncio.create_task(client.hset("bench:hset", str(i), data))
                    for i in range(100)
                )
            )
    else:
        for i in range(count):
            await client.hset("bench:hset", str(i), data)


@timer
async def hget(client, gather):
    if gather:
        for _ in range(count // 100):
            await asyncio.gather(
                *(
                    asyncio.create_task(client.hget("bench:hset", str(i)))
                    for i in range(100)
                )
            )
    else:
        for i in range(count):
            await client.hget("bench:hset", str(i))


@timer
async def incr(client, gather):
    if gather:
        for _ in range(count // 100):
            await asyncio.gather(
                *(asyncio.create_task(client.incr("bench:incr")) for i in range(100))
            )
    else:
        for i in range(count):
            await client.incr("bench:incr")


@timer
async def lpush(client, gather, data):
    if gather:
        for _ in range(count // 100):
            await asyncio.gather(
                *(
                    asyncio.create_task(client.lpush("bench:lpush", data))
                    for i in range(100)
                )
            )
    else:
        for i in range(count):
            await client.lpush("bench:lpush", data)


@timer
async def lrange_300(client, gather):
    if gather:
        for _ in range(count // 100):
            await asyncio.gather(
                *(
                    asyncio.create_task(client.lrange("bench:lpush", i, i + 300))
                    for i in range(100)
                )
            )
    else:
        for i in range(count):
            await client.lrange("bench:lpush", i, i + 300)


@timer
async def lpop(client, gather):
    if gather:
        for _ in range(count // 100):
            await asyncio.gather(
                *(asyncio.create_task(client.lpop("bench:lpush")) for i in range(100))
            )
    else:
        for i in range(count):
            await client.lpop("bench:lpush")


@timer
async def warmup(client):
    await asyncio.gather(
        *(asyncio.create_task(client.exists(f"bench:warmup_{i}")) for i in range(100))
    )


@timer
async def run(client, gather):
    data_str = "a" * size
    data_int = int("1" * size)

    if gather is False:
        for ret in await asyncio.gather(
            asyncio.create_task(set_str(client, gather, data_str)),
            asyncio.create_task(set_int(client, gather, data_int)),
            asyncio.create_task(hset(client, gather, data_str)),
            asyncio.create_task(incr(client, gather)),
            asyncio.create_task(lpush(client, gather, data_int)),
        ):
            print(ret)
        for ret in await asyncio.gather(
            asyncio.create_task(get_str(client, gather)),
            asyncio.create_task(get_int(client, gather)),
            asyncio.create_task(hget(client, gather)),
            asyncio.create_task(lrange_300(client, gather)),
            asyncio.create_task(lpop(client, gather)),
        ):
            print(ret)
    else:
        print(await set_str(client, gather, data_str))
        print(await set_int(client, gather, data_int))
        print(await hset(client, gather, data_str))
        print(await incr(client, gather))
        print(await lpush(client, gather, data_int))

        print(await get_str(client, gather))
        print(await get_int(client, gather))
        print(await hget(client, gather))
        print(await lrange_300(client, gather))
        print(await lpop(client, gather))


async def main(loop, gather=None):
    arc = aredis.StrictRedisCluster(
        host=host,
        port=port,
        password=password,
        max_connections=2**31,
        max_connections_per_node=2**31,
        readonly=False,
        reinitialize_steps=count,
        skip_full_coverage_check=True,
        decode_responses=False,
        max_idle_time=count,
        idle_check_interval=count,
    )
    print(f"{loop} {gather} {await warmup(arc)} aredis")
    print(await run(arc, gather=gather))
    arc.connection_pool.disconnect()

    aiorc = await aioredis_cluster.create_redis_cluster(
        [(host, port)],
        password=password,
        state_reload_interval=count,
        idle_connection_timeout=count,
        pool_maxsize=2**31,
    )
    print(f"{loop} {gather} {await warmup(aiorc)} aioredis-cluster")
    print(await run(aiorc, gather=gather))
    aiorc.close()
    await aiorc.wait_closed()

    async with redispy.RedisCluster(
        host=host,
        port=port,
        password=password,
        reinitialize_steps=count,
        read_from_replicas=False,
        decode_responses=False,
        max_connections=2**31,
    ) as rca:
        print(f"{loop} {gather} {await warmup(rca)} redispy")
        print(await run(rca, gather=gather))


if __name__ == "__main__":
    host = "localhost"
    port = 16379
    password = None

    count = 10000
    size = 256

    asyncio.run(main("asyncio"))
    asyncio.run(main("asyncio", gather=False))
    asyncio.run(main("asyncio", gather=True))

    uvloop.install()

    asyncio.run(main("uvloop"))
    asyncio.run(main("uvloop", gather=False))
    asyncio.run(main("uvloop", gather=True))


================================================
FILE: benchmarks/cluster_async_pipeline.py
================================================
import asyncio
import functools
import time

import aioredis_cluster
import aredis
import uvloop

import redis.asyncio as redispy


def timer(func):
    @functools.wraps(func)
    async def wrapper(*args, **kwargs):
        tic = time.perf_counter()
        await func(*args, **kwargs)
        toc = time.perf_counter()
        return f"{toc - tic:.4f}"

    return wrapper


@timer
async def warmup(client):
    await asyncio.gather(
        *(asyncio.create_task(client.exists(f"bench:warmup_{i}")) for i in range(100))
    )


@timer
async def run(client):
    data_str = "a" * size
    data_int = int("1" * size)

    for i in range(count):
        with client.pipeline() as pipe:
            await (
                pipe.set(f"bench:str_{i}", data_str)
                .set(f"bench:int_{i}", data_int)
                .get(f"bench:str_{i}")
                .get(f"bench:int_{i}")
                .hset("bench:hset", str(i), data_str)
                .hget("bench:hset", str(i))
                .incr("bench:incr")
                .lpush("bench:lpush", data_int)
                .lrange("bench:lpush", 0, 300)
                .lpop("bench:lpush")
                .execute()
            )


async def main(loop):
    arc = aredis.StrictRedisCluster(
        host=host,
        port=port,
        password=password,
        max_connections=2**31,
        max_connections_per_node=2**31,
        readonly=False,
        reinitialize_steps=count,
        skip_full_coverage_check=True,
        decode_responses=False,
        max_idle_time=count,
        idle_check_interval=count,
    )
    print(f"{loop} {await warmup(arc)} aredis")
    print(await run(arc))
    arc.connection_pool.disconnect()

    aiorc = await aioredis_cluster.create_redis_cluster(
        [(host, port)],
        password=password,
        state_reload_interval=count,
        idle_connection_timeout=count,
        pool_maxsize=2**31,
    )
    print(f"{loop} {await warmup(aiorc)} aioredis-cluster")
    print(await run(aiorc))
    aiorc.close()
    await aiorc.wait_closed()

    async with redispy.RedisCluster(
        host=host,
        port=port,
        password=password,
        reinitialize_steps=count,
        read_from_replicas=False,
        decode_responses=False,
        max_connections=2**31,
    ) as rca:
        print(f"{loop} {await warmup(rca)} redispy")
        print(await run(rca))


if __name__ == "__main__":
    host = "localhost"
    port = 16379
    password = None

    count = 10000
    size = 256

    asyncio.run(main("asyncio"))

    uvloop.install()

    asyncio.run(main("uvloop"))


================================================
FILE: benchmarks/command_packer_benchmark.py
================================================
from base import Benchmark

from redis.connection import SYM_CRLF, SYM_DOLLAR, SYM_EMPTY, SYM_STAR, Connection


class StringJoiningConnection(Connection):
    def send_packed_command(self, command, check_health=True):
        "Send an already packed command to the Redis server"
        if not self._sock:
            self.connect()
        try:
            self._sock.sendall(command)
        except OSError as e:
            self.disconnect()
            if len(e.args) == 1:
                _errno, errmsg = "UNKNOWN", e.args[0]
            else:
                _errno, errmsg = e.args
            raise ConnectionError(f"Error {_errno} while writing to socket. {errmsg}.")
        except Exception:
            self.disconnect()
            raise

    def pack_command(self, *args):
        "Pack a series of arguments into a value Redis command"
        args_output = SYM_EMPTY.join(
            [
                SYM_EMPTY.join(
                    (SYM_DOLLAR, str(len(k)).encode(), SYM_CRLF, k, SYM_CRLF)
                )
                for k in map(self.encoder.encode, args)
            ]
        )
        output = SYM_EMPTY.join(
            (SYM_STAR, str(len(args)).encode(), SYM_CRLF, args_output)
        )
        return output


class ListJoiningConnection(Connection):
    def send_packed_command(self, command, check_health=True):
        if not self._sock:
            self.connect()
        try:
            if isinstance(command, str):
                command = [command]
            for item in command:
                self._sock.sendall(item)
        except OSError as e:
            self.disconnect()
            if len(e.args) == 1:
                _errno, errmsg = "UNKNOWN", e.args[0]
            else:
                _errno, errmsg = e.args
            raise ConnectionError(f"Error {_errno} while writing to socket. {errmsg}.")
        except Exception:
            self.disconnect()
            raise

    def pack_command(self, *args):
        output = []
        buff = SYM_EMPTY.join((SYM_STAR, str(len(args)).encode(), SYM_CRLF))

        for k in map(self.encoder.encode, args):
            if len(buff) > 6000 or len(k) > 6000:
                buff = SYM_EMPTY.join(
                    (buff, SYM_DOLLAR, str(len(k)).encode(), SYM_CRLF)
                )
                output.append(buff)
                output.append(k)
                buff = SYM_CRLF
            else:
                buff = SYM_EMPTY.join(
                    (buff, SYM_DOLLAR, str(len(k)).encode(), SYM_CRLF, k, SYM_CRLF)
                )
        output.append(buff)
        return output


class CommandPackerBenchmark(Benchmark):
    ARGUMENTS = (
        {
            "name": "connection_class",
            "values": [StringJoiningConnection, ListJoiningConnection],
        },
        {
            "name": "value_size",
            "values": [10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000],
        },
    )

    def setup(self, connection_class, value_size):
        self.get_client(connection_class=connection_class)

    def run(self, connection_class, value_size):
        r = self.get_client()
        x = "a" * value_size
        r.set("benchmark", x)


if __name__ == "__main__":
    CommandPackerBenchmark().run_benchmark()


================================================
FILE: benchmarks/otel_benchmark.py
================================================
#!/usr/bin/env python3
"""
OpenTelemetry Benchmark for redis-py.

This module provides benchmarking infrastructure to measure the performance impact
of OpenTelemetry instrumentation on redis-py operations.

The benchmark uses a comprehensive load generator that exercises all Redis operations
(commands, pubsub, streaming, CSC, connections) in each iteration. This ensures
consistent test conditions when comparing different OTel configurations.

Run one scenario at a time (sync client - default):
    python -m benchmarks.otel_benchmark --scenario baseline --baseline-tag v5.2.1
    python -m benchmarks.otel_benchmark --scenario otel_disabled
    python -m benchmarks.otel_benchmark --scenario otel_noop
    python -m benchmarks.otel_benchmark --scenario otel_inmemory
    python -m benchmarks.otel_benchmark --scenario otel_enabled_http
    python -m benchmarks.otel_benchmark --scenario otel_enabled_grpc

Run with async client:
    python -m benchmarks.otel_benchmark --scenario baseline --baseline-tag v5.2.1 --async
    python -m benchmarks.otel_benchmark --scenario otel_disabled --async
    python -m benchmarks.otel_benchmark --scenario otel_enabled_http --async

Specify which OTel metric groups to enable:
    python -m benchmarks.otel_benchmark --scenario otel_enabled_http --metric-groups command,pubsub
    python -m benchmarks.otel_benchmark --scenario otel_enabled_http --metric-groups all
"""

import argparse
import asyncio
import json
import os
import statistics
import subprocess
import sys
import time
from dataclasses import dataclass, field, asdict
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Dict, List, Optional, Any

try:
    import psutil
    PSUTIL_AVAILABLE = True
except ImportError:
    PSUTIL_AVAILABLE = False


@dataclass
class BenchmarkResult:
    """Results from a single benchmark run."""
    scenario: str
    duration_seconds: float
    total_operations: int
    operations_per_second: float
    avg_latency_ms: float
    p50_latency_ms: float
    p95_latency_ms: float
    p99_latency_ms: float
    min_latency_ms: float
    max_latency_ms: float
    errors: int = 0
    first_error: Optional[str] = None
    # Resource usage metrics
    cpu_percent_avg: Optional[float] = None
    cpu_percent_max: Optional[float] = None
    memory_mb_avg: Optional[float] = None
    memory_mb_max: Optional[float] = None
    metadata: Dict = field(default_factory=dict)


@dataclass
class LoadGeneratorConfig:
    """Configuration for the load generator."""
    duration_seconds: float = 30.0
    value_size_bytes: int = 100
    key_prefix: str = "otel_bench"
    warmup_seconds: float = 5.0
    redis_host: str = "localhost"
    redis_port: int = 6379


class ComprehensiveLoadGenerator:
    """
    Comprehensive load generator that exercises all Redis operations.

    Each iteration performs:
    - Command operations (SET/GET) - triggers COMMAND metrics
    - PubSub operations (PUBLISH) - triggers PUBSUB metrics
    - Streaming operations (XADD/XREAD) - triggers STREAMING metrics
    - Connection pool operations - triggers CONNECTION metrics

    CSC (Client-Side Caching) requires RESP3 protocol and is tested separately
    when available.

    This ensures consistent test conditions across all OTel configurations.
    """

    def __init__(self, config: LoadGeneratorConfig, redis_module: Any = None):
        """
        Initialize the comprehensive load generator.

        Args:
            config: Load generator configuration
            redis_module: Optional redis module to use (for baseline testing with
                         a different redis-py version). If None, imports redis normally.
        """
        self.config = config
        self.redis_module = redis_module
        self.latencies: List[float] = []
        self.errors: int = 0
        self.first_error: Optional[str] = None
        self._value = "x" * config.value_size_bytes
        self._key_counter = 0
        self._message_counter = 0

        # Resources (initialized in setup)
        self.client = None
        self.pubsub_publisher = None
        self.pubsub = None
        self.stream_name = f"{config.key_prefix}:stream"
        self.pubsub_channel = f"{config.key_prefix}:channel"
        self.consumer_group = "benchmark_group"
        self.consumer_name = "benchmark_consumer"

        # CSC client (optional, requires RESP3)
        self.csc_client = None

        # Resource tracking
        self.cpu_samples: List[float] = []
        self.memory_samples: List[float] = []
        self._process = psutil.Process() if PSUTIL_AVAILABLE else None

    def _get_redis_module(self) -> Any:
        """Get the redis module to use."""
        if self.redis_module is not None:
            return self.redis_module
        import redis
        return redis

    def _get_key(self) -> str:
        """Generate a key for the current operation."""
        key = f"{self.config.key_prefix}:{self._key_counter % 1000}"
        self._key_counter += 1
        return key

    def setup(self) -> None:
        """Set up all Redis resources."""
        redis = self._get_redis_module()

        # Main client for commands
        self.client = redis.Redis(
            host=self.config.redis_host,
            port=self.config.redis_port,
            decode_responses=True
        )

        # PubSub publisher (separate connection)
        self.pubsub_publisher = redis.Redis(
            host=self.config.redis_host,
            port=self.config.redis_port,
            decode_responses=True
        )

        # PubSub subscriber
        subscriber = redis.Redis(
            host=self.config.redis_host,
            port=self.config.redis_port,
            decode_responses=True
        )
        self.pubsub = subscriber.pubsub()
        self.pubsub.subscribe(self.pubsub_channel)
        # Consume subscription confirmation
        self.pubsub.get_message(timeout=1.0)

        # Create stream and consumer group
        try:
            self.client.xgroup_create(
                self.stream_name,
                self.consumer_group,
                id="0",
                mkstream=True
            )
        except Exception:
            # Group may already exist
            pass

        # Try to set up CSC client (requires RESP3, may not be available)
        try:
            from redis.cache import CacheConfig
            self.csc_client = redis.Redis(
                host=self.config.redis_host,
                port=self.config.redis_port,
                decode_responses=True,
                protocol=3,
                cache_config=CacheConfig(max_size=1000)
            )
            # Test that CSC works
            self.csc_client.ping()
        except Exception:
            # CSC not available (old redis-py version or server doesn't support RESP3)
            self.csc_client = None

    def teardown(self) -> None:
        """Clean up all Redis resources."""
        if self.pubsub:
            try:
                self.pubsub.unsubscribe()
                self.pubsub.close()
            except Exception:
                pass

        if self.pubsub_publisher:
            try:
                self.pubsub_publisher.close()
            except Exception:
                pass

        if self.client:
            try:
                self.client.delete(self.stream_name)
            except Exception:
                pass
            try:
                self.client.close()
            except Exception:
                pass

        if self.csc_client:
            try:
                self.csc_client.close()
            except Exception:
                pass

    def _run_operation(self) -> float:
        """
        Run a comprehensive operation cycle and return latency in ms.

        Each cycle includes:
        1. SET + GET (command metrics)
        2. PUBLISH + get_message (pubsub metrics)
        3. XADD + XREADGROUP + XACK (streaming metrics)
        4. CSC GET operations if available (csc metrics)
        """
        key = self._get_key()
        start = time.perf_counter()

        try:
            # 1. Command operations (SET/GET)
            self.client.set(key, self._value)
            self.client.get(key)

            # 2. PubSub operations
            self._message_counter += 1
            self.pubsub_publisher.publish(self.pubsub_channel, f"msg:{self._message_counter}")
            self.pubsub.get_message(timeout=0.001)  # Non-blocking receive

            # 3. Streaming operations
            entry_id = self.client.xadd(
                self.stream_name,
                {"data": self._value[:50]},  # Smaller payload for streams
                maxlen=1000
            )
            messages = self.client.xreadgroup(
                self.consumer_group,
                self.consumer_name,
                {self.stream_name: ">"},
                count=1,
                block=1  # 1ms timeout
            )
            if messages:
                for stream_name, entries in messages:
                    for eid, _ in entries:
                        self.client.xack(self.stream_name, self.consumer_group, eid)

            # 4. CSC operations (if available)
            if self.csc_client:
                csc_key = f"{self.config.key_prefix}:csc:{self._key_counter % 100}"
                self.csc_client.set(csc_key, self._value)
                self.csc_client.get(csc_key)  # Cache miss
                self.csc_client.get(csc_key)  # Cache hit

        except Exception as e:
            if self.first_error is None:
                self.first_error = str(e)
            self.errors += 1

        end = time.perf_counter()
        return (end - start) * 1000  # Convert to milliseconds

    def warmup(self) -> None:
        """Run warmup operations to stabilize connections."""
        print(f"  Warming up for {self.config.warmup_seconds}s...")
        end_time = time.monotonic() + self.config.warmup_seconds
        while time.monotonic() < end_time:
            self._run_operation()
        self.latencies.clear()
        self.errors = 0
        self.first_error = None
        self._key_counter = 0
        self._message_counter = 0

    def _sample_resources(self) -> None:
        """Sample current CPU and memory usage."""
        if self._process is None:
            return
        try:
            # CPU percent since last call (non-blocking)
            cpu = self._process.cpu_percent(interval=None)
            if cpu > 0:  # Skip first sample which is always 0
                self.cpu_samples.append(cpu)
            # Memory in MB
            mem_info = self._process.memory_info()
            self.memory_samples.append(mem_info.rss / (1024 * 1024))
        except Exception:
            pass  # Ignore errors in resource sampling

    def run(self) -> BenchmarkResult:
        """Run the load generator for the configured duration."""
        self.latencies = []
        self.errors = 0
        self.first_error = None
        self.cpu_samples = []
        self.memory_samples = []

        # Initialize CPU percent tracking
        if self._process:
            try:
                self._process.cpu_percent(interval=None)
            except Exception:
                pass

        print(f"  Running load for {self.config.duration_seconds}s...")
        start_time = time.monotonic()
        end_time = start_time + self.config.duration_seconds
        last_sample_time = start_time
        sample_interval = 0.5  # Sample resources every 500ms

        while time.monotonic() < end_time:
            latency = self._run_operation()
            if latency > 0:
                self.latencies.append(latency)

            # Sample resources periodically
            current_time = time.monotonic()
            if current_time - last_sample_time >= sample_interval:
                self._sample_resources()
                last_sample_time = current_time

        # Final resource sample
        self._sample_resources()

        actual_duration = time.monotonic() - start_time
        return self._calculate_results(actual_duration)

    def _calculate_results(self, duration: float) -> BenchmarkResult:
        """Calculate benchmark results from collected latencies."""
        if not self.latencies:
            return BenchmarkResult(
                scenario="unknown", duration_seconds=duration, total_operations=0,
                operations_per_second=0, avg_latency_ms=0, p50_latency_ms=0,
                p95_latency_ms=0, p99_latency_ms=0, min_latency_ms=0,
                max_latency_ms=0, errors=self.errors, first_error=self.first_error,
            )

        sorted_latencies = sorted(self.latencies)
        # Count operations per cycle:
        # - 2 command ops (SET + GET)
        # - 2 pubsub ops (PUBLISH + get_message)
        # - 3 stream ops (XADD + XREADGROUP + XACK)
        # - 3 CSC ops if available (SET + 2x GET)
        ops_per_cycle = 7 + (3 if self.csc_client else 0)
        total_ops = len(self.latencies) * ops_per_cycle

        # Calculate resource usage stats
        cpu_avg = statistics.mean(self.cpu_samples) if self.cpu_samples else None
        cpu_max = max(self.cpu_samples) if self.cpu_samples else None
        mem_avg = statistics.mean(self.memory_samples) if self.memory_samples else None
        mem_max = max(self.memory_samples) if self.memory_samples else None

        return BenchmarkResult(
            scenario="unknown",
            duration_seconds=duration,
            total_operations=total_ops,
            operations_per_second=total_ops / duration,
            avg_latency_ms=statistics.mean(self.latencies),
            p50_latency_ms=sorted_latencies[len(sorted_latencies) // 2],
            p95_latency_ms=sorted_latencies[int(len(sorted_latencies) * 0.95)],
            p99_latency_ms=sorted_latencies[int(len(sorted_latencies) * 0.99)],
            min_latency_ms=min(self.latencies),
            max_latency_ms=max(self.latencies),
            errors=self.errors,
            first_error=self.first_error,
            cpu_percent_avg=cpu_avg,
            cpu_percent_max=cpu_max,
            memory_mb_avg=mem_avg,
            memory_mb_max=mem_max,
        )


class AsyncComprehensiveLoadGenerator:
    """
    Async comprehensive load generator that exercises all Redis operations concurrently.

    Each iteration performs operations concurrently where possible:
    - Command operations (SET/GET) - triggers COMMAND metrics
    - PubSub operations (PUBLISH) - triggers PUBSUB metrics
    - Streaming operations (XADD/XREAD) - triggers STREAMING metrics
    - Connection pool operations - triggers CONNECTION metrics

    This ensures consistent test conditions across all OTel configurations
    while maximizing throughput through concurrent execution.
    """

    def __init__(self, config: LoadGeneratorConfig, redis_module: Any = None):
        """
        Initialize the async comprehensive load generator.

        Args:
            config: Load generator configuration
            redis_module: Optional redis module to use (for baseline testing with
                         a different redis-py version). If None, imports redis normally.
        """
        self.config = config
        self.redis_module = redis_module
        self.latencies: List[float] = []
        self.errors: int = 0
        self.first_error: Optional[str] = None
        self._value = "x" * config.value_size_bytes
        self._key_counter = 0
        self._message_counter = 0

        # Resources (initialized in setup)
        self.client = None
        self.pubsub_publisher = None
        self.pubsub = None
        self.stream_name = f"{config.key_prefix}:stream"
        self.pubsub_channel = f"{config.key_prefix}:channel"
        self.consumer_group = "benchmark_group"
        self.consumer_name = "benchmark_consumer"

        # Resource tracking
        self.cpu_samples: List[float] = []
        self.memory_samples: List[float] = []
        self._process = psutil.Process() if PSUTIL_AVAILABLE else None

    def _get_redis_module(self) -> Any:
        """Get the redis module to use."""
        if self.redis_module is not None:
            return self.redis_module
        import redis
        return redis

    def _get_key(self) -> str:
        """Generate a key for the current operation."""
        key = f"{self.config.key_prefix}:{self._key_counter % 1000}"
        self._key_counter += 1
        return key

    async def setup(self) -> None:
        """Set up all Redis resources."""
        redis_mod = self._get_redis_module()
        Redis = redis_mod.asyncio.Redis

        # Main client for commands
        self.client = Redis(
            host=self.config.redis_host,
            port=self.config.redis_port,
            decode_responses=True
        )

        # PubSub publisher (separate connection)
        self.pubsub_publisher = Redis(
            host=self.config.redis_host,
            port=self.config.redis_port,
            decode_responses=True
        )

        # PubSub subscriber
        subscriber = Redis(
            host=self.config.redis_host,
            port=self.config.redis_port,
            decode_responses=True
        )
        self.pubsub = subscriber.pubsub()
        await self.pubsub.subscribe(self.pubsub_channel)
        # Consume subscription confirmation
        await self.pubsub.get_message(timeout=1.0)

        # Create stream and consumer group
        try:
            await self.client.xgroup_create(
                self.stream_name,
                self.consumer_group,
                id="0",
                mkstream=True
            )
        except Exception:
            # Group may already exist
            pass

    async def teardown(self) -> None:
        """Clean up all Redis resources."""
        if self.pubsub:
            try:
                await self.pubsub.unsubscribe()
                await self.pubsub.aclose()
            except Exception:
                pass

        if self.pubsub_publisher:
            try:
                await self.pubsub_publisher.aclose()
            except Exception:
                pass

        if self.client:
            try:
                await self.client.delete(self.stream_name)
            except Exception:
                pass
            try:
                await self.client.aclose()
            except Ex
Download .txt
gitextract_8s9rmjqg/

├── .agent/
│   └── sync_async_type_hints_overload_guide.md
├── .dockerignore
├── .github/
│   ├── CODEOWNERS
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── actions/
│   │   └── run-tests/
│   │       └── action.yml
│   ├── dependabot.yml
│   ├── release-drafter-config.yml
│   ├── spellcheck-settings.yml
│   ├── wordlist.txt
│   └── workflows/
│       ├── codeql-analysis.yml
│       ├── docs.yaml
│       ├── hiredis-py-integration.yaml
│       ├── install_and_test.sh
│       ├── integration.yaml
│       ├── pypi-publish.yaml
│       ├── release-drafter.yml
│       ├── spellcheck.yml
│       └── stale-issues.yml
├── .gitignore
├── .mypy.ini
├── .readthedocs.yml
├── CHANGES
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── benchmarks/
│   ├── __init__.py
│   ├── base.py
│   ├── basic_operations.py
│   ├── cluster_async.py
│   ├── cluster_async_pipeline.py
│   ├── command_packer_benchmark.py
│   ├── otel_benchmark.py
│   └── socket_read_size.py
├── codecov.yml
├── dev_requirements.txt
├── docker-compose.yml
├── dockers/
│   └── sentinel.conf
├── docs/
│   ├── Makefile
│   ├── _static/
│   │   └── .keep
│   ├── _templates/
│   │   └── .keep
│   ├── advanced_features.rst
│   ├── backoff.rst
│   ├── clustering.rst
│   ├── commands.rst
│   ├── conf.py
│   ├── connections.rst
│   ├── examples/
│   │   ├── README.md
│   │   ├── asyncio_examples.ipynb
│   │   ├── connection_examples.ipynb
│   │   ├── opentelemetry/
│   │   │   ├── README.md
│   │   │   ├── config/
│   │   │   │   ├── alertmanager.yml
│   │   │   │   ├── otel-collector.yaml
│   │   │   │   └── vector.toml
│   │   │   ├── docker-compose.yml
│   │   │   ├── main.py
│   │   │   ├── requirements.txt
│   │   │   └── uptrace.yml
│   │   ├── opentelemetry_api_examples.ipynb
│   │   ├── pipeline_examples.ipynb
│   │   ├── redis-stream-example.ipynb
│   │   ├── search_json_examples.ipynb
│   │   ├── search_vector_similarity_examples.ipynb
│   │   ├── set_and_get_examples.ipynb
│   │   ├── ssl_connection_examples.ipynb
│   │   └── timeseries_examples.ipynb
│   ├── examples.rst
│   ├── exceptions.rst
│   ├── genindex.rst
│   ├── geographic_failover.rst
│   ├── index.rst
│   ├── lock.rst
│   ├── lua_scripting.rst
│   ├── opentelemetry.rst
│   ├── redismodules.rst
│   ├── requirements.txt
│   ├── resp3_features.rst
│   └── retry.rst
├── doctests/
│   ├── README.md
│   ├── cmds_cnxmgmt.py
│   ├── cmds_generic.py
│   ├── cmds_hash.py
│   ├── cmds_list.py
│   ├── cmds_servermgmt.py
│   ├── cmds_set.py
│   ├── cmds_sorted_set.py
│   ├── cmds_string.py
│   ├── data/
│   │   ├── query_em.json
│   │   └── query_vector.json
│   ├── dt_bitfield.py
│   ├── dt_bitmap.py
│   ├── dt_bloom.py
│   ├── dt_cms.py
│   ├── dt_cuckoo.py
│   ├── dt_geo.py
│   ├── dt_hash.py
│   ├── dt_hll.py
│   ├── dt_json.py
│   ├── dt_list.py
│   ├── dt_set.py
│   ├── dt_ss.py
│   ├── dt_stream.py
│   ├── dt_string.py
│   ├── dt_tdigest.py
│   ├── dt_time_series.py
│   ├── dt_topk.py
│   ├── dt_vec_set.py
│   ├── geo_index.py
│   ├── home_json.py
│   ├── home_prob_dts.py
│   ├── query_agg.py
│   ├── query_combined.py
│   ├── query_em.py
│   ├── query_ft.py
│   ├── query_geo.py
│   ├── query_range.py
│   ├── requirements.txt
│   ├── run_examples.sh
│   ├── search_quickstart.py
│   ├── search_vss.py
│   ├── string_set_get.py
│   └── trans_pipe.py
├── pyproject.toml
├── redis/
│   ├── __init__.py
│   ├── _parsers/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── commands.py
│   │   ├── encoders.py
│   │   ├── helpers.py
│   │   ├── hiredis.py
│   │   ├── resp2.py
│   │   ├── resp3.py
│   │   └── socket.py
│   ├── asyncio/
│   │   ├── __init__.py
│   │   ├── client.py
│   │   ├── cluster.py
│   │   ├── connection.py
│   │   ├── http/
│   │   │   ├── __init__.py
│   │   │   └── http_client.py
│   │   ├── lock.py
│   │   ├── multidb/
│   │   │   ├── __init__.py
│   │   │   ├── client.py
│   │   │   ├── command_executor.py
│   │   │   ├── config.py
│   │   │   ├── database.py
│   │   │   ├── event.py
│   │   │   ├── failover.py
│   │   │   ├── failure_detector.py
│   │   │   └── healthcheck.py
│   │   ├── observability/
│   │   │   ├── __init__.py
│   │   │   └── recorder.py
│   │   ├── retry.py
│   │   ├── sentinel.py
│   │   └── utils.py
│   ├── auth/
│   │   ├── __init__.py
│   │   ├── err.py
│   │   ├── idp.py
│   │   ├── token.py
│   │   └── token_manager.py
│   ├── background.py
│   ├── backoff.py
│   ├── cache.py
│   ├── client.py
│   ├── cluster.py
│   ├── commands/
│   │   ├── __init__.py
│   │   ├── bf/
│   │   │   ├── __init__.py
│   │   │   ├── commands.py
│   │   │   └── info.py
│   │   ├── cluster.py
│   │   ├── core.py
│   │   ├── helpers.py
│   │   ├── json/
│   │   │   ├── __init__.py
│   │   │   ├── _util.py
│   │   │   ├── commands.py
│   │   │   ├── decoders.py
│   │   │   └── path.py
│   │   ├── policies.py
│   │   ├── redismodules.py
│   │   ├── search/
│   │   │   ├── __init__.py
│   │   │   ├── _util.py
│   │   │   ├── aggregation.py
│   │   │   ├── commands.py
│   │   │   ├── dialect.py
│   │   │   ├── document.py
│   │   │   ├── field.py
│   │   │   ├── hybrid_query.py
│   │   │   ├── hybrid_result.py
│   │   │   ├── index_definition.py
│   │   │   ├── profile_information.py
│   │   │   ├── query.py
│   │   │   ├── querystring.py
│   │   │   ├── reducers.py
│   │   │   ├── result.py
│   │   │   └── suggestion.py
│   │   ├── sentinel.py
│   │   ├── timeseries/
│   │   │   ├── __init__.py
│   │   │   ├── commands.py
│   │   │   ├── info.py
│   │   │   └── utils.py
│   │   └── vectorset/
│   │       ├── __init__.py
│   │       ├── commands.py
│   │       └── utils.py
│   ├── connection.py
│   ├── crc.py
│   ├── credentials.py
│   ├── data_structure.py
│   ├── driver_info.py
│   ├── event.py
│   ├── exceptions.py
│   ├── http/
│   │   ├── __init__.py
│   │   └── http_client.py
│   ├── lock.py
│   ├── maint_notifications.py
│   ├── multidb/
│   │   ├── __init__.py
│   │   ├── circuit.py
│   │   ├── client.py
│   │   ├── command_executor.py
│   │   ├── config.py
│   │   ├── database.py
│   │   ├── event.py
│   │   ├── exception.py
│   │   ├── failover.py
│   │   └── failure_detector.py
│   ├── observability/
│   │   ├── __init__.py
│   │   ├── attributes.py
│   │   ├── config.py
│   │   ├── metrics.py
│   │   ├── providers.py
│   │   ├── recorder.py
│   │   └── registry.py
│   ├── ocsp.py
│   ├── py.typed
│   ├── retry.py
│   ├── sentinel.py
│   ├── typing.py
│   └── utils.py
├── specs/
│   ├── commands_overload_inventory.md
│   └── sync_async_deduplication_analysis.md
├── tasks.py
├── tests/
│   ├── __init__.py
│   ├── conftest.py
│   ├── entraid_utils.py
│   ├── helpers.py
│   ├── maint_notifications/
│   │   ├── proxy_server_helpers.py
│   │   ├── test_cluster_maint_notifications_handling.py
│   │   ├── test_maint_notifications.py
│   │   └── test_maint_notifications_handling.py
│   ├── mocks.py
│   ├── ssl_utils.py
│   ├── test_asyncio/
│   │   ├── __init__.py
│   │   ├── compat.py
│   │   ├── conftest.py
│   │   ├── helpers.py
│   │   ├── mocks.py
│   │   ├── test_bloom.py
│   │   ├── test_client.py
│   │   ├── test_cluster.py
│   │   ├── test_cluster_transaction.py
│   │   ├── test_command_parser.py
│   │   ├── test_command_policies.py
│   │   ├── test_commands.py
│   │   ├── test_connect.py
│   │   ├── test_connection.py
│   │   ├── test_connection_pool.py
│   │   ├── test_credentials.py
│   │   ├── test_cwe_404.py
│   │   ├── test_encoding.py
│   │   ├── test_hash.py
│   │   ├── test_json.py
│   │   ├── test_lock.py
│   │   ├── test_monitor.py
│   │   ├── test_multidb/
│   │   │   ├── __init__.py
│   │   │   ├── conftest.py
│   │   │   ├── test_client.py
│   │   │   ├── test_command_executor.py
│   │   │   ├── test_config.py
│   │   │   ├── test_failover.py
│   │   │   ├── test_failure_detector.py
│   │   │   ├── test_healthcheck.py
│   │   │   └── test_pipeline.py
│   │   ├── test_observability/
│   │   │   ├── __init__.py
│   │   │   ├── test_cluster_metrics_error_handling.py
│   │   │   └── test_recorder.py
│   │   ├── test_pipeline.py
│   │   ├── test_pubsub.py
│   │   ├── test_retry.py
│   │   ├── test_scenario/
│   │   │   ├── __init__.py
│   │   │   ├── conftest.py
│   │   │   └── test_active_active.py
│   │   ├── test_scripting.py
│   │   ├── test_search.py
│   │   ├── test_sentinel.py
│   │   ├── test_sentinel_managed_connection.py
│   │   ├── test_ssl.py
│   │   ├── test_timeseries.py
│   │   ├── test_usage_counter.py
│   │   ├── test_utils.py
│   │   ├── test_vsets.py
│   │   └── testdata/
│   │       ├── jsontestdata.py
│   │       ├── titles.csv
│   │       └── will_play_text.csv.bz2
│   ├── test_auth/
│   │   ├── __init__.py
│   │   ├── test_token.py
│   │   └── test_token_manager.py
│   ├── test_background.py
│   ├── test_backoff.py
│   ├── test_bloom.py
│   ├── test_cache.py
│   ├── test_client.py
│   ├── test_cluster.py
│   ├── test_cluster_transaction.py
│   ├── test_command_parser.py
│   ├── test_command_policies.py
│   ├── test_commands.py
│   ├── test_connect.py
│   ├── test_connection.py
│   ├── test_connection_pool.py
│   ├── test_credentials.py
│   ├── test_data_structure.py
│   ├── test_driver_info.py
│   ├── test_encoding.py
│   ├── test_event.py
│   ├── test_function.py
│   ├── test_hash.py
│   ├── test_helpers.py
│   ├── test_http/
│   │   ├── __init__.py
│   │   └── test_http_client.py
│   ├── test_json.py
│   ├── test_lock.py
│   ├── test_max_connections_error.py
│   ├── test_monitor.py
│   ├── test_multidb/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── test_circuit.py
│   │   ├── test_client.py
│   │   ├── test_command_executor.py
│   │   ├── test_config.py
│   │   ├── test_failover.py
│   │   ├── test_failure_detector.py
│   │   └── test_pipeline.py
│   ├── test_multiprocessing.py
│   ├── test_observability/
│   │   ├── __init__.py
│   │   ├── test_cluster_metrics_error_handling.py
│   │   ├── test_config.py
│   │   ├── test_metrics_connection_attributes.py
│   │   ├── test_provider.py
│   │   ├── test_public_api.py
│   │   └── test_recorder.py
│   ├── test_parsers/
│   │   ├── test_errors.py
│   │   └── test_helpers.py
│   ├── test_pipeline.py
│   ├── test_pubsub.py
│   ├── test_retry.py
│   ├── test_scenario/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── fault_injector_client.py
│   │   ├── maint_notifications_helpers.py
│   │   ├── test_active_active.py
│   │   └── test_maint_notifications.py
│   ├── test_scripting.py
│   ├── test_search.py
│   ├── test_sentinel.py
│   ├── test_sentinel_managed_connection.py
│   ├── test_ssl.py
│   ├── test_timeseries.py
│   ├── test_utils.py
│   ├── test_vsets.py
│   └── testdata/
│       ├── jsontestdata.py
│       ├── titles.csv
│       └── will_play_text.csv.bz2
├── util/
│   └── wait-for-it.sh
└── whitelist.py
Download .txt
Showing preview only (733K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (8361 symbols across 227 files)

FILE: benchmarks/base.py
  class Benchmark (line 9) | class Benchmark:
    method __init__ (line 12) | def __init__(self):
    method get_client (line 15) | def get_client(self, **kwargs):
    method setup (line 25) | def setup(self, **kwargs):
    method run (line 28) | def run(self, **kwargs):
    method run_benchmark (line 31) | def run_benchmark(self):

FILE: benchmarks/basic_operations.py
  function parse_args (line 8) | def parse_args():
  function run (line 30) | def run():
  function timer (line 45) | def timer(func):
  function set_str (line 65) | def set_str(conn, num, pipeline_size, data_size):
  function set_int (line 80) | def set_int(conn, num, pipeline_size, data_size):
  function get_str (line 95) | def get_str(conn, num, pipeline_size, data_size):
  function get_int (line 109) | def get_int(conn, num, pipeline_size, data_size):
  function incr (line 123) | def incr(conn, num, pipeline_size, *args, **kwargs):
  function lpush (line 137) | def lpush(conn, num, pipeline_size, data_size):
  function lrange_300 (line 152) | def lrange_300(conn, num, pipeline_size, data_size):
  function lpop (line 166) | def lpop(conn, num, pipeline_size, data_size):
  function hmset (line 178) | def hmset(conn, num, pipeline_size, data_size):

FILE: benchmarks/cluster_async.py
  function timer (line 12) | def timer(func):
  function set_str (line 24) | async def set_str(client, gather, data):
  function set_int (line 39) | async def set_int(client, gather, data):
  function get_str (line 54) | async def get_str(client, gather):
  function get_int (line 66) | async def get_int(client, gather):
  function hset (line 78) | async def hset(client, gather, data):
  function hget (line 93) | async def hget(client, gather):
  function incr (line 108) | async def incr(client, gather):
  function lpush (line 120) | async def lpush(client, gather, data):
  function lrange_300 (line 135) | async def lrange_300(client, gather):
  function lpop (line 150) | async def lpop(client, gather):
  function warmup (line 162) | async def warmup(client):
  function run (line 169) | async def run(client, gather):
  function main (line 204) | async def main(loop, gather=None):

FILE: benchmarks/cluster_async_pipeline.py
  function timer (line 12) | def timer(func):
  function warmup (line 24) | async def warmup(client):
  function run (line 31) | async def run(client):
  function main (line 52) | async def main(loop):

FILE: benchmarks/command_packer_benchmark.py
  class StringJoiningConnection (line 6) | class StringJoiningConnection(Connection):
    method send_packed_command (line 7) | def send_packed_command(self, command, check_health=True):
    method pack_command (line 24) | def pack_command(self, *args):
  class ListJoiningConnection (line 40) | class ListJoiningConnection(Connection):
    method send_packed_command (line 41) | def send_packed_command(self, command, check_health=True):
    method pack_command (line 60) | def pack_command(self, *args):
  class CommandPackerBenchmark (line 80) | class CommandPackerBenchmark(Benchmark):
    method setup (line 92) | def setup(self, connection_class, value_size):
    method run (line 95) | def run(self, connection_class, value_size):

FILE: benchmarks/otel_benchmark.py
  class BenchmarkResult (line 51) | class BenchmarkResult:
  class LoadGeneratorConfig (line 74) | class LoadGeneratorConfig:
  class ComprehensiveLoadGenerator (line 84) | class ComprehensiveLoadGenerator:
    method __init__ (line 100) | def __init__(self, config: LoadGeneratorConfig, redis_module: Any = No...
    method _get_redis_module (line 135) | def _get_redis_module(self) -> Any:
    method _get_key (line 142) | def _get_key(self) -> str:
    method setup (line 148) | def setup(self) -> None:
    method teardown (line 205) | def teardown(self) -> None:
    method _run_operation (line 236) | def _run_operation(self) -> float:
    method warmup (line 292) | def warmup(self) -> None:
    method _sample_resources (line 304) | def _sample_resources(self) -> None:
    method run (line 319) | def run(self) -> BenchmarkResult:
    method _calculate_results (line 357) | def _calculate_results(self, duration: float) -> BenchmarkResult:
  class AsyncComprehensiveLoadGenerator (line 402) | class AsyncComprehensiveLoadGenerator:
    method __init__ (line 416) | def __init__(self, config: LoadGeneratorConfig, redis_module: Any = No...
    method _get_redis_module (line 448) | def _get_redis_module(self) -> Any:
    method _get_key (line 455) | def _get_key(self) -> str:
    method setup (line 461) | async def setup(self) -> None:
    method teardown (line 503) | async def teardown(self) -> None:
    method _run_operation (line 528) | async def _run_operation(self) -> float:
    method warmup (line 594) | async def warmup(self) -> None:
    method _sample_resources (line 606) | def _sample_resources(self) -> None:
    method run (line 619) | async def run(self) -> BenchmarkResult:
    method _calculate_results (line 657) | def _calculate_results(self, duration: float) -> BenchmarkResult:
  function print_result (line 702) | def print_result(result: BenchmarkResult, iterations: int = 1) -> None:
  function average_results (line 735) | def average_results(results: List[BenchmarkResult]) -> BenchmarkResult:
  function parse_args (line 777) | def parse_args() -> argparse.Namespace:
  function _clear_redis_modules (line 839) | def _clear_redis_modules() -> None:
  function run_baseline_scenario (line 846) | def run_baseline_scenario(tag: str, config: LoadGeneratorConfig) -> Opti...
  function run_baseline_scenario_async (line 902) | async def run_baseline_scenario_async(tag: str, config: LoadGeneratorCon...
  function _get_metric_groups_for_benchmark (line 959) | def _get_metric_groups_for_benchmark(metric_group_names: Optional[List[s...
  function setup_scenario (line 997) | def setup_scenario(
  function run_iteration (line 1104) | def run_iteration(scenario: str, config: LoadGeneratorConfig, descriptio...
  function run_iteration_async (line 1128) | async def run_iteration_async(scenario: str, config: LoadGeneratorConfig...
  function main (line 1153) | def main() -> int:
  function _run_sync_benchmark (line 1215) | def _run_sync_benchmark(args: argparse.Namespace, config: LoadGeneratorC...
  function _run_async_benchmark (line 1244) | async def _run_async_benchmark(args: argparse.Namespace, config: LoadGen...

FILE: benchmarks/socket_read_size.py
  class SocketReadBenchmark (line 6) | class SocketReadBenchmark(Benchmark):
    method setup (line 16) | def setup(self, value_size, read_size, parser):
    method run (line 20) | def run(self, value_size, read_size, parser):

FILE: docs/examples/opentelemetry/main.py
  function main (line 13) | def main():
  function handle_request (line 31) | def handle_request(client):

FILE: doctests/query_combined.py
  function embed_text (line 14) | def embed_text(model, text):

FILE: doctests/search_vss.py
  function create_query_table (line 260) | def create_query_table(query, queries, encoded_queries, extra_params=None):

FILE: doctests/trans_pipe.py
  function watched_sequence (line 80) | def watched_sequence(pipe):

FILE: redis/__init__.py
  function int_or_str (line 43) | def int_or_str(value):

FILE: redis/_parsers/base.py
  class BaseParser (line 73) | class BaseParser(ABC):
    method parse_error (line 110) | def parse_error(cls, response):
    method on_disconnect (line 121) | def on_disconnect(self):
    method on_connect (line 124) | def on_connect(self, connection):
  class _RESPBase (line 128) | class _RESPBase(BaseParser):
    method __init__ (line 131) | def __init__(self, socket_read_size):
    method __del__ (line 137) | def __del__(self):
    method on_connect (line 143) | def on_connect(self, connection):
    method on_disconnect (line 151) | def on_disconnect(self):
    method can_read (line 159) | def can_read(self, timeout):
  class AsyncBaseParser (line 163) | class AsyncBaseParser(BaseParser):
    method __init__ (line 168) | def __init__(self, socket_read_size: int):
    method can_read_destructive (line 172) | async def can_read_destructive(self) -> bool:
    method read_response (line 175) | async def read_response(
  class MaintenanceNotificationsParser (line 181) | class MaintenanceNotificationsParser:
    method parse_oss_maintenance_start_msg (line 185) | def parse_oss_maintenance_start_msg(response):
    method parse_oss_maintenance_completed_msg (line 193) | def parse_oss_maintenance_completed_msg(response):
    method parse_maintenance_start_msg (line 219) | def parse_maintenance_start_msg(response, notification_type):
    method parse_maintenance_completed_msg (line 229) | def parse_maintenance_completed_msg(response, notification_type):
    method parse_moving_msg (line 238) | def parse_moving_msg(response):
  class PushNotificationsParser (line 303) | class PushNotificationsParser(Protocol):
    method handle_pubsub_push_response (line 312) | def handle_pubsub_push_response(self, response):
    method handle_push_response (line 316) | def handle_push_response(self, response, **kwargs):
    method set_pubsub_push_handler (line 379) | def set_pubsub_push_handler(self, pubsub_push_handler_func):
    method set_invalidation_push_handler (line 382) | def set_invalidation_push_handler(self, invalidation_push_handler_func):
    method set_node_moving_push_handler (line 385) | def set_node_moving_push_handler(self, node_moving_push_handler_func):
    method set_maintenance_push_handler (line 388) | def set_maintenance_push_handler(self, maintenance_push_handler_func):
    method set_oss_cluster_maint_push_handler (line 391) | def set_oss_cluster_maint_push_handler(self, oss_cluster_maint_push_ha...
  class AsyncPushNotificationsParser (line 395) | class AsyncPushNotificationsParser(Protocol):
    method handle_pubsub_push_response (line 404) | async def handle_pubsub_push_response(self, response):
    method handle_push_response (line 408) | async def handle_push_response(self, response, **kwargs):
    method set_pubsub_push_handler (line 471) | def set_pubsub_push_handler(self, pubsub_push_handler_func):
    method set_invalidation_push_handler (line 475) | def set_invalidation_push_handler(self, invalidation_push_handler_func):
    method set_node_moving_push_handler (line 479) | def set_node_moving_push_handler(self, node_moving_push_handler_func):
    method set_maintenance_push_handler (line 482) | def set_maintenance_push_handler(self, maintenance_push_handler_func):
    method set_oss_cluster_maint_push_handler (line 485) | def set_oss_cluster_maint_push_handler(self, oss_cluster_maint_push_ha...
  class _AsyncRESPBase (line 489) | class _AsyncRESPBase(AsyncBaseParser):
    method __init__ (line 494) | def __init__(self, socket_read_size: int):
    method _clear (line 501) | def _clear(self):
    method on_connect (line 505) | def on_connect(self, connection):
    method on_disconnect (line 514) | def on_disconnect(self):
    method can_read_destructive (line 518) | async def can_read_destructive(self) -> bool:
    method _read (line 529) | async def _read(self, length: int) -> bytes:
    method _readline (line 549) | async def _readline(self) -> bytes:

FILE: redis/_parsers/commands.py
  class RequestPolicy (line 11) | class RequestPolicy(Enum):
  class ResponsePolicy (line 22) | class ResponsePolicy(Enum):
  class CommandPolicies (line 35) | class CommandPolicies:
    method __init__ (line 36) | def __init__(
  class AbstractCommandsParser (line 48) | class AbstractCommandsParser:
    method _get_pubsub_keys (line 49) | def _get_pubsub_keys(self, *args):
    method parse_subcommand (line 78) | def parse_subcommand(self, command, **options):
  class CommandsParser (line 94) | class CommandsParser(AbstractCommandsParser):
    method __init__ (line 103) | def __init__(self, redis_connection):
    method initialize (line 108) | def initialize(self, r):
    method get_keys (line 121) | def get_keys(self, redis_conn, *args):
    method _get_moveable_keys (line 188) | def _get_moveable_keys(self, redis_conn, *args):
    method _is_keyless_command (line 213) | def _is_keyless_command(
    method get_command_policies (line 255) | def get_command_policies(self) -> PolicyRecords:
  class AsyncCommandsParser (line 398) | class AsyncCommandsParser(AbstractCommandsParser):
    method __init__ (line 416) | def __init__(self) -> None:
    method initialize (line 419) | async def initialize(self, node: Optional["ClusterNode"] = None) -> None:
    method get_keys (line 429) | async def get_keys(self, *args: Any) -> Optional[Tuple[str, ...]]:
    method _get_moveable_keys (line 496) | async def _get_moveable_keys(self, *args: Any) -> Optional[Tuple[str, ...
    method _is_keyless_command (line 510) | async def _is_keyless_command(
    method get_command_policies (line 552) | async def get_command_policies(self) -> Awaitable[PolicyRecords]:

FILE: redis/_parsers/encoders.py
  class Encoder (line 4) | class Encoder:
    method __init__ (line 9) | def __init__(self, encoding, encoding_errors, decode_responses):
    method encode (line 14) | def encode(self, value):
    method decode (line 37) | def decode(self, value, force=False):

FILE: redis/_parsers/helpers.py
  function timestamp_to_datetime (line 6) | def timestamp_to_datetime(response):
  function parse_debug_object (line 17) | def parse_debug_object(response):
  function parse_info (line 35) | def parse_info(response):
  function parse_memory_stats (line 86) | def parse_memory_stats(response, **kwargs):
  function parse_sentinel_state (line 124) | def parse_sentinel_state(item):
  function parse_sentinel_master (line 140) | def parse_sentinel_master(response, **options):
  function parse_sentinel_state_resp3 (line 144) | def parse_sentinel_state_resp3(response, **options):
  function parse_sentinel_masters (line 157) | def parse_sentinel_masters(response, **options):
  function parse_sentinel_masters_resp3 (line 165) | def parse_sentinel_masters_resp3(response, **options):
  function parse_sentinel_slaves_and_sentinels (line 169) | def parse_sentinel_slaves_and_sentinels(response, **options):
  function parse_sentinel_slaves_and_sentinels_resp3 (line 173) | def parse_sentinel_slaves_and_sentinels_resp3(response, **options):
  function parse_sentinel_get_master (line 177) | def parse_sentinel_get_master(response, **options):
  function pairs_to_dict (line 181) | def pairs_to_dict(response, decode_keys=False, decode_string_values=False):
  function pairs_to_dict_typed (line 200) | def pairs_to_dict_typed(response, type_info):
  function zset_score_pairs (line 215) | def zset_score_pairs(response, **options):
  function zset_score_for_rank (line 227) | def zset_score_for_rank(response, **options):
  function zset_score_pairs_resp3 (line 238) | def zset_score_pairs_resp3(response, **options):
  function zset_score_for_rank_resp3 (line 249) | def zset_score_for_rank_resp3(response, **options):
  function sort_return_tuples (line 260) | def sort_return_tuples(response, **options):
  function parse_stream_list (line 271) | def parse_stream_list(response, **options):
  function pairs_to_dict_with_str_keys (line 286) | def pairs_to_dict_with_str_keys(response):
  function parse_list_of_dicts (line 290) | def parse_list_of_dicts(response):
  function parse_xclaim (line 294) | def parse_xclaim(response, **options):
  function parse_xautoclaim (line 300) | def parse_xautoclaim(response, **options):
  function parse_xinfo_stream (line 307) | def parse_xinfo_stream(response, **options):
  function parse_xread (line 338) | def parse_xread(response, **options):
  function parse_xread_resp3 (line 344) | def parse_xread_resp3(response, **options):
  function parse_xpending (line 352) | def parse_xpending(response, **options):
  function parse_xpending_range (line 364) | def parse_xpending_range(response):
  function float_or_none (line 369) | def float_or_none(response):
  function bool_ok (line 375) | def bool_ok(response, **options):
  function parse_zadd (line 379) | def parse_zadd(response, **options):
  function parse_client_list (line 387) | def parse_client_list(response, **options):
  function parse_config_get (line 409) | def parse_config_get(response, **options):
  function parse_scan (line 414) | def parse_scan(response, **options):
  function parse_hscan (line 419) | def parse_hscan(response, **options):
  function parse_zscan (line 429) | def parse_zscan(response, **options):
  function parse_zmscore (line 436) | def parse_zmscore(response, **options):
  function parse_slowlog_get (line 441) | def parse_slowlog_get(response, **options):
  function parse_stralgo (line 470) | def parse_stralgo(response, **options):
  function parse_cluster_info (line 499) | def parse_cluster_info(response, **options):
  function _parse_node_line (line 504) | def _parse_node_line(line):
  function _parse_slots (line 527) | def _parse_slots(slot_ranges):
  function parse_cluster_nodes (line 547) | def parse_cluster_nodes(response, **options):
  function parse_geosearch_generic (line 557) | def parse_geosearch_generic(response, **options):
  function parse_command (line 593) | def parse_command(response, **options):
  function parse_command_resp3 (line 612) | def parse_command_resp3(response, **options):
  function parse_pubsub_numsub (line 633) | def parse_pubsub_numsub(response, **options):
  function parse_client_kill (line 637) | def parse_client_kill(response, **options):
  function parse_acl_getuser (line 643) | def parse_acl_getuser(response, **options):
  function parse_acl_log (line 686) | def parse_acl_log(response, **options):
  function parse_client_info (line 704) | def parse_client_info(value):
  function parse_set_result (line 736) | def parse_set_result(response, **options):
  function string_keys_to_dict (line 750) | def string_keys_to_dict(key_string, callback):

FILE: redis/_parsers/hiredis.py
  class _HiredisReaderArgs (line 34) | class _HiredisReaderArgs(TypedDict, total=False):
  class _HiredisParser (line 41) | class _HiredisParser(BaseParser, PushNotificationsParser):
    method __init__ (line 44) | def __init__(self, socket_read_size):
    method __del__ (line 56) | def __del__(self):
    method handle_pubsub_push_response (line 62) | def handle_pubsub_push_response(self, response):
    method on_connect (line 67) | def on_connect(self, connection, **kwargs):
    method on_disconnect (line 90) | def on_disconnect(self):
    method can_read (line 95) | def can_read(self, timeout):
    method read_from_socket (line 105) | def read_from_socket(self, timeout=SENTINEL, raise_on_timeout=True):
    method read_response (line 135) | def read_response(
  class _AsyncHiredisParser (line 200) | class _AsyncHiredisParser(AsyncBaseParser, AsyncPushNotificationsParser):
    method __init__ (line 205) | def __init__(self, socket_read_size: int):
    method handle_pubsub_push_response (line 214) | async def handle_pubsub_push_response(self, response):
    method on_connect (line 219) | def on_connect(self, connection):
    method on_disconnect (line 243) | def on_disconnect(self):
    method can_read_destructive (line 246) | async def can_read_destructive(self):
    method read_from_socket (line 257) | async def read_from_socket(self):
    method read_response (line 266) | async def read_response(

FILE: redis/_parsers/resp2.py
  class _RESP2Parser (line 9) | class _RESP2Parser(_RESPBase):
    method read_response (line 12) | def read_response(
    method _read_response (line 28) | def _read_response(
  class _AsyncRESP2Parser (line 77) | class _AsyncRESP2Parser(_AsyncRESPBase):
    method read_response (line 80) | async def read_response(self, disable_decoding: bool = False):
    method _read_response (line 93) | async def _read_response(

FILE: redis/_parsers/resp3.py
  class _RESP3Parser (line 15) | class _RESP3Parser(_RESPBase, PushNotificationsParser):
    method __init__ (line 18) | def __init__(self, socket_read_size):
    method handle_pubsub_push_response (line 26) | def handle_pubsub_push_response(self, response):
    method read_response (line 31) | def read_response(
    method _read_response (line 58) | def _read_response(
  class _AsyncRESP3Parser (line 165) | class _AsyncRESP3Parser(_AsyncRESPBase, AsyncPushNotificationsParser):
    method __init__ (line 166) | def __init__(self, socket_read_size):
    method handle_pubsub_push_response (line 171) | async def handle_pubsub_push_response(self, response):
    method read_response (line 176) | async def read_response(
    method _read_response (line 191) | async def _read_response(

FILE: redis/_parsers/socket.py
  class SocketBuffer (line 29) | class SocketBuffer:
    method __init__ (line 30) | def __init__(
    method unread_bytes (line 38) | def unread_bytes(self) -> int:
    method _read_from_socket (line 47) | def _read_from_socket(
    method can_read (line 94) | def can_read(self, timeout: float) -> bool:
    method read (line 99) | def read(self, length: int, timeout: Union[float, object] = SENTINEL) ...
    method readline (line 110) | def readline(self, timeout: Union[float, object] = SENTINEL) -> bytes:
    method get_pos (line 120) | def get_pos(self) -> int:
    method rewind (line 126) | def rewind(self, pos: int) -> None:
    method purge (line 132) | def purge(self) -> None:
    method close (line 151) | def close(self) -> None:

FILE: redis/asyncio/client.py
  class ResponseCallbackProtocol (line 107) | class ResponseCallbackProtocol(Protocol):
    method __call__ (line 108) | def __call__(self, response: Any, **kwargs): ...
  class AsyncResponseCallbackProtocol (line 111) | class AsyncResponseCallbackProtocol(Protocol):
    method __call__ (line 112) | async def __call__(self, response: Any, **kwargs): ...
  class Redis (line 118) | class Redis(
    method from_url (line 139) | def from_url(
    method from_pool (line 208) | def from_pool(
    method __init__ (line 233) | def __init__(
    method __repr__ (line 427) | def __repr__(self):
    method __await__ (line 433) | def __await__(self):
    method initialize (line 436) | async def initialize(self: _RedisT) -> _RedisT:
    method set_response_callback (line 449) | def set_response_callback(self, command: str, callback: ResponseCallba...
    method get_encoder (line 453) | def get_encoder(self):
    method get_connection_kwargs (line 457) | def get_connection_kwargs(self):
    method get_retry (line 461) | def get_retry(self) -> Optional[Retry]:
    method set_retry (line 464) | def set_retry(self, retry: Retry) -> None:
    method load_external_module (line 468) | def load_external_module(self, funcname, func):
    method pipeline (line 491) | def pipeline(
    method transaction (line 505) | async def transaction(
    method lock (line 534) | def lock(
    method pubsub (line 615) | def pubsub(self, **kwargs) -> "PubSub":
    method monitor (line 625) | def monitor(self) -> "Monitor":
    method client (line 628) | def client(self) -> "Redis":
    method __aenter__ (line 633) | async def __aenter__(self: _RedisT) -> _RedisT:
    method _increment_usage (line 648) | async def _increment_usage(self) -> int:
    method _decrement_usage (line 657) | async def _decrement_usage(self) -> int:
    method __aexit__ (line 666) | async def __aexit__(self, exc_type, exc_value, traceback):
    method __del__ (line 680) | def __del__(
    method aclose (line 694) | async def aclose(self, close_connection_pool: Optional[bool] = None) -...
    method close (line 715) | async def close(self, close_connection_pool: Optional[bool] = None) ->...
    method _send_command_parse_response (line 721) | async def _send_command_parse_response(self, conn, command_name, *args...
    method _close_connection (line 728) | async def _close_connection(
    method execute_command (line 763) | async def execute_command(self, *args, **options):
    method parse_response (line 818) | async def parse_response(
  class MonitorCommandInfo (line 850) | class MonitorCommandInfo(TypedDict):
  class Monitor (line 859) | class Monitor:
    method __init__ (line 869) | def __init__(self, connection_pool: ConnectionPool):
    method connect (line 873) | async def connect(self):
    method __aenter__ (line 877) | async def __aenter__(self):
    method __aexit__ (line 886) | async def __aexit__(self, *args):
    method next_command (line 890) | async def next_command(self) -> MonitorCommandInfo:
    method listen (line 926) | async def listen(self) -> AsyncIterator[MonitorCommandInfo]:
  class PubSub (line 932) | class PubSub:
    method __init__ (line 945) | def __init__(
    method __aenter__ (line 986) | async def __aenter__(self):
    method __aexit__ (line 989) | async def __aexit__(self, exc_type, exc_value, traceback):
    method __del__ (line 992) | def __del__(self):
    method aclose (line 996) | async def aclose(self):
    method close (line 1014) | async def close(self) -> None:
    method reset (line 1019) | async def reset(self) -> None:
    method on_connect (line 1023) | async def on_connect(self, connection: Connection):
    method subscribed (line 1062) | def subscribed(self):
    method execute_command (line 1066) | async def execute_command(self, *args: EncodableT):
    method connect (line 1078) | async def connect(self):
    method _reconnect (line 1098) | async def _reconnect(
    method _execute (line 1130) | async def _execute(self, conn, command, *args, **kwargs):
    method parse_response (line 1182) | async def parse_response(self, block: bool = True, timeout: float = 0):
    method check_health (line 1247) | async def check_health(self):
    method _normalize_keys (line 1263) | def _normalize_keys(self, data: _NormalizeKeysT) -> _NormalizeKeysT:
    method psubscribe (line 1273) | async def psubscribe(self, *args: ChannelT, **kwargs: PubSubHandler):
    method punsubscribe (line 1294) | def punsubscribe(self, *args: ChannelT) -> Awaitable:
    method subscribe (line 1309) | async def subscribe(self, *args: ChannelT, **kwargs: Callable):
    method unsubscribe (line 1330) | def unsubscribe(self, *args) -> Awaitable:
    method listen (line 1344) | async def listen(self) -> AsyncIterator:
    method get_message (line 1351) | async def get_message(
    method ping (line 1366) | def ping(self, message=None) -> Awaitable[bool]:
    method handle_message (line 1376) | async def handle_message(self, response, ignore_subscribe_messages=Fal...
    method run (line 1449) | async def run(
  class PubsubWorkerExceptionHandler (line 1500) | class PubsubWorkerExceptionHandler(Protocol):
    method __call__ (line 1501) | def __call__(self, e: BaseException, pubsub: PubSub): ...
  class AsyncPubsubWorkerExceptionHandler (line 1504) | class AsyncPubsubWorkerExceptionHandler(Protocol):
    method __call__ (line 1505) | async def __call__(self, e: BaseException, pubsub: PubSub): ...
  class Pipeline (line 1517) | class Pipeline(Redis):  # lgtm [py/init-calls-subclass]
    method __init__ (line 1539) | def __init__(
    method __aenter__ (line 1556) | async def __aenter__(self: _RedisT) -> _RedisT:
    method __aexit__ (line 1559) | async def __aexit__(self, exc_type, exc_value, traceback):
    method __await__ (line 1562) | def __await__(self):
    method __len__ (line 1567) | def __len__(self):
    method __bool__ (line 1570) | def __bool__(self):
    method _async_self (line 1574) | async def _async_self(self):
    method reset (line 1577) | async def reset(self):
    method aclose (line 1601) | async def aclose(self) -> None:
    method multi (line 1605) | def multi(self):
    method execute_command (line 1618) | def execute_command(
    method _disconnect_reset_raise_on_watching (line 1625) | async def _disconnect_reset_raise_on_watching(
    method immediate_execute_command (line 1667) | async def immediate_execute_command(self, *args, **options):
    method pipeline_execute_command (line 1723) | def pipeline_execute_command(self, *args, **options):
    method _execute_transaction (line 1738) | async def _execute_transaction(  # noqa: C901
    method _execute_pipeline (line 1816) | async def _execute_pipeline(
    method raise_first_error (line 1836) | def raise_first_error(self, commands: CommandStackT, response: Iterabl...
    method annotate_exception (line 1842) | def annotate_exception(
    method parse_response (line 1852) | async def parse_response(
    method load_scripts (line 1862) | async def load_scripts(self):
    method _disconnect_raise_on_watching (line 1875) | async def _disconnect_raise_on_watching(
    method execute (line 1915) | async def execute(self, raise_on_error: bool = True) -> List[Any]:
    method discard (line 1978) | async def discard(self):
    method watch (line 1984) | async def watch(self, *names: KeyT):
    method unwatch (line 1990) | async def unwatch(self):

FILE: redis/asyncio/cluster.py
  class RedisCluster (line 107) | class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisCluste...
    method from_url (line 210) | def from_url(cls, url: str, **kwargs: Any) -> "RedisCluster":
    method __init__ (line 274) | def __init__(
    method initialize (line 486) | async def initialize(self) -> "RedisCluster":
    method aclose (line 505) | async def aclose(self) -> None:
    method close (line 517) | async def close(self) -> None:
    method __aenter__ (line 521) | async def __aenter__(self) -> "RedisCluster":
    method _increment_usage (line 536) | async def _increment_usage(self) -> int:
    method _decrement_usage (line 545) | async def _decrement_usage(self) -> int:
    method __aexit__ (line 554) | async def __aexit__(self, exc_type, exc_value, traceback):
    method __await__ (line 564) | def __await__(self) -> Generator[Any, None, "RedisCluster"]:
    method __del__ (line 569) | def __del__(
    method on_connect (line 582) | async def on_connect(self, connection: Connection) -> None:
    method get_nodes (line 594) | def get_nodes(self) -> List["ClusterNode"]:
    method get_primaries (line 598) | def get_primaries(self) -> List["ClusterNode"]:
    method get_replicas (line 602) | def get_replicas(self) -> List["ClusterNode"]:
    method get_random_node (line 606) | def get_random_node(self) -> "ClusterNode":
    method get_default_node (line 610) | def get_default_node(self) -> "ClusterNode":
    method set_default_node (line 614) | def set_default_node(self, node: "ClusterNode") -> None:
    method get_node (line 625) | def get_node(
    method get_node_from_key (line 634) | def get_node_from_key(
    method get_random_primary_or_all_nodes (line 662) | def get_random_primary_or_all_nodes(self, command_name):
    method get_random_primary_node (line 671) | def get_random_primary_node(self) -> "ClusterNode":
    method get_nodes_from_slot (line 677) | async def get_nodes_from_slot(self, command: str, *args):
    method get_special_nodes (line 690) | def get_special_nodes(self) -> Optional[list["ClusterNode"]]:
    method keyslot (line 701) | def keyslot(self, key: EncodableT) -> int:
    method get_encoder (line 709) | def get_encoder(self) -> Encoder:
    method get_connection_kwargs (line 713) | def get_connection_kwargs(self) -> Dict[str, Optional[Any]]:
    method set_retry (line 717) | def set_retry(self, retry: Retry) -> None:
    method set_response_callback (line 720) | def set_response_callback(self, command: str, callback: ResponseCallba...
    method _determine_nodes (line 724) | async def _determine_nodes(
    method _determine_slot (line 754) | async def _determine_slot(self, command: str, *args: Any) -> int:
    method _is_node_flag (line 805) | def _is_node_flag(self, target_nodes: Any) -> bool:
    method _parse_target_nodes (line 808) | def _parse_target_nodes(self, target_nodes: Any) -> List["ClusterNode"]:
    method _record_error_metric (line 828) | async def _record_error_metric(
    method _record_command_metric (line 849) | async def _record_command_metric(
    method execute_command (line 874) | async def execute_command(self, *args: EncodableT, **kwargs: Any) -> Any:
    method _execute_command (line 1017) | async def _execute_command(
    method pipeline (line 1203) | def pipeline(
    method lock (line 1218) | def lock(
    method transaction (line 1299) | async def transaction(
  class ClusterNode (line 1324) | class ClusterNode:
    method __init__ (line 1347) | def __init__(
    method __repr__ (line 1378) | def __repr__(self) -> str:
    method __eq__ (line 1384) | def __eq__(self, obj: Any) -> bool:
    method __hash__ (line 1387) | def __hash__(self) -> int:
    method __del__ (line 1392) | def __del__(
    method disconnect (line 1408) | async def disconnect(self) -> None:
    method acquire_connection (line 1420) | def acquire_connection(self) -> Connection:
    method disconnect_if_needed (line 1447) | async def disconnect_if_needed(self, connection: Connection) -> None:
    method release (line 1456) | def release(self, connection: Connection) -> None:
    method update_active_connections_for_reconnect (line 1464) | def update_active_connections_for_reconnect(self) -> None:
    method disconnect_free_connections (line 1475) | async def disconnect_free_connections(self) -> None:
    method parse_response (line 1489) | async def parse_response(
    method execute_command (line 1515) | async def execute_command(self, *args: Any, **kwargs: Any) -> Any:
    method execute_pipeline (line 1532) | async def execute_pipeline(self, commands: List["PipelineCommand"]) ->...
    method re_auth_callback (line 1560) | async def re_auth_callback(self, token: TokenInterface):
    method _mock (line 1579) | async def _mock(self, error: RedisError):
  class NodesManager (line 1588) | class NodesManager:
    method __init__ (line 1605) | def __init__(
    method get_node (line 1633) | def get_node(
    method set_nodes (line 1651) | def set_nodes(
    method move_node_to_end_of_cached_nodes (line 1691) | def move_node_to_end_of_cached_nodes(self, node_name: str) -> None:
    method move_slot (line 1708) | def move_slot(self, e: AskError | MovedError):
    method get_node_from_slot (line 1745) | def get_node_from_slot(
    method get_nodes_by_server_type (line 1769) | def get_nodes_by_server_type(self, server_type: str) -> List["ClusterN...
    method initialize (line 1776) | async def initialize(self) -> None:
    method aclose (line 1926) | async def aclose(self, attr: str = "nodes_cache") -> None:
    method remap_host_port (line 1935) | def remap_host_port(self, host: str, port: int) -> Tuple[str, int]:
  class ClusterPipeline (line 1946) | class ClusterPipeline(AbstractRedis, AbstractRedisCluster, AsyncRedisClu...
    method __init__ (line 1992) | def __init__(
    method nodes_manager (line 2004) | def nodes_manager(self) -> "NodesManager":
    method set_response_callback (line 2008) | def set_response_callback(self, command: str, callback: ResponseCallba...
    method initialize (line 2012) | async def initialize(self) -> "ClusterPipeline":
    method __aenter__ (line 2016) | async def __aenter__(self) -> "ClusterPipeline":
    method __aexit__ (line 2019) | async def __aexit__(self, exc_type: None, exc_value: None, traceback: ...
    method __await__ (line 2022) | def __await__(self) -> Generator[Any, None, "ClusterPipeline"]:
    method __bool__ (line 2025) | def __bool__(self) -> bool:
    method __len__ (line 2029) | def __len__(self) -> int:
    method execute_command (line 2032) | def execute_command(
    method execute (line 2048) | async def execute(
    method _split_command_across_slots (line 2073) | def _split_command_across_slots(
    method reset (line 2081) | async def reset(self):
    method multi (line 2087) | def multi(self):
    method discard (line 2094) | async def discard(self):
    method watch (line 2098) | async def watch(self, *names):
    method unwatch (line 2102) | async def unwatch(self):
    method unlink (line 2106) | async def unlink(self, *names):
    method mset_nonatomic (line 2109) | def mset_nonatomic(
  class PipelineCommand (line 2123) | class PipelineCommand:
    method __init__ (line 2124) | def __init__(self, position: int, *args: Any, **kwargs: Any) -> None:
    method __repr__ (line 2131) | def __repr__(self) -> str:
  class ExecutionStrategy (line 2135) | class ExecutionStrategy(ABC):
    method initialize (line 2137) | async def initialize(self) -> "ClusterPipeline":
    method execute_command (line 2146) | def execute_command(
    method execute (line 2157) | async def execute(
    method mset_nonatomic (line 2171) | def mset_nonatomic(
    method reset (line 2182) | async def reset(self):
    method multi (line 2191) | def multi(self):
    method watch (line 2200) | async def watch(self, *names):
    method unwatch (line 2209) | async def unwatch(self):
    method discard (line 2218) | async def discard(self):
    method unlink (line 2222) | async def unlink(self, *names):
    method __len__ (line 2231) | def __len__(self) -> int:
  class AbstractStrategy (line 2235) | class AbstractStrategy(ExecutionStrategy):
    method __init__ (line 2236) | def __init__(self, pipe: ClusterPipeline) -> None:
    method initialize (line 2240) | async def initialize(self) -> "ClusterPipeline":
    method execute_command (line 2246) | def execute_command(
    method _annotate_exception (line 2254) | def _annotate_exception(self, exception, number, command):
    method mset_nonatomic (line 2266) | def mset_nonatomic(
    method execute (line 2272) | async def execute(
    method reset (line 2278) | async def reset(self):
    method multi (line 2282) | def multi(self):
    method watch (line 2286) | async def watch(self, *names):
    method unwatch (line 2290) | async def unwatch(self):
    method discard (line 2294) | async def discard(self):
    method unlink (line 2298) | async def unlink(self, *names):
    method __len__ (line 2301) | def __len__(self) -> int:
  class PipelineStrategy (line 2305) | class PipelineStrategy(AbstractStrategy):
    method __init__ (line 2306) | def __init__(self, pipe: ClusterPipeline) -> None:
    method mset_nonatomic (line 2309) | def mset_nonatomic(
    method execute (line 2324) | async def execute(
    method _execute (line 2356) | async def _execute(
    method reset (line 2497) | async def reset(self):
    method multi (line 2503) | def multi(self):
    method watch (line 2508) | async def watch(self, *names):
    method unwatch (line 2513) | async def unwatch(self):
    method discard (line 2518) | async def discard(self):
    method unlink (line 2523) | async def unlink(self, *names):
  class TransactionStrategy (line 2532) | class TransactionStrategy(AbstractStrategy):
    method __init__ (line 2544) | def __init__(self, pipe: ClusterPipeline) -> None:
    method _get_client_and_connection_for_transaction (line 2557) | def _get_client_and_connection_for_transaction(
    method execute_command (line 2584) | def execute_command(self, *args: Union[KeyT, EncodableT], **kwargs: An...
    method _execute_command (line 2606) | async def _execute_command(
    method _validate_watch (line 2642) | def _validate_watch(self):
    method _immediate_execute_command (line 2648) | async def _immediate_execute_command(self, *args, **options):
    method _get_connection_and_send_command (line 2655) | async def _get_connection_and_send_command(self, *args, **options):
    method _send_command_parse_response (line 2690) | async def _send_command_parse_response(
    method _reinitialize_on_error (line 2709) | async def _reinitialize_on_error(self, error, failure_count):
    method _raise_first_error (line 2750) | async def _raise_first_error(self, responses, stack, start_time):
    method mset_nonatomic (line 2769) | def mset_nonatomic(
    method execute (line 2774) | async def execute(
    method _execute_transaction_with_retries (line 2783) | async def _execute_transaction_with_retries(
    method _execute_transaction (line 2794) | async def _execute_transaction(
    method reset (line 2913) | async def reset(self):
    method multi (line 2943) | def multi(self):
    method watch (line 2952) | async def watch(self, *names):
    method unwatch (line 2958) | async def unwatch(self):
    method discard (line 2964) | async def discard(self):
    method unlink (line 2967) | async def unlink(self, *names):

FILE: redis/asyncio/connection.py
  class _Sentinel (line 100) | class _Sentinel(enum.Enum):
  class ConnectCallbackProtocol (line 114) | class ConnectCallbackProtocol(Protocol):
    method __call__ (line 115) | def __call__(self, connection: "AbstractConnection"): ...
  class AsyncConnectCallbackProtocol (line 118) | class AsyncConnectCallbackProtocol(Protocol):
    method __call__ (line 119) | async def __call__(self, connection: "AbstractConnection"): ...
  class AbstractConnection (line 125) | class AbstractConnection:
    method __init__ (line 162) | def __init__(
    method __del__ (line 269) | def __del__(self, _warnings: Any = warnings):
    method _close (line 285) | def _close(self):
    method __repr__ (line 293) | def __repr__(self):
    method repr_pieces (line 298) | def repr_pieces(self):
    method is_connected (line 302) | def is_connected(self):
    method register_connect_callback (line 305) | def register_connect_callback(self, callback):
    method deregister_connect_callback (line 317) | def deregister_connect_callback(self, callback):
    method set_parser (line 328) | def set_parser(self, parser_class: Type[BaseParser]) -> None:
    method connect (line 336) | async def connect(self):
    method connect_check_health (line 350) | async def connect_check_health(
    method mark_for_reconnect (line 427) | def mark_for_reconnect(self):
    method should_reconnect (line 430) | def should_reconnect(self):
    method reset_should_reconnect (line 433) | def reset_should_reconnect(self):
    method _connect (line 437) | async def _connect(self):
    method _host_error (line 441) | def _host_error(self) -> str:
    method _error_message (line 444) | def _error_message(self, exception: BaseException) -> str:
    method get_protocol (line 447) | def get_protocol(self):
    method on_connect (line 450) | async def on_connect(self) -> None:
    method on_connect_check_health (line 454) | async def on_connect_check_health(self, check_health: bool = True) -> ...
    method disconnect (line 569) | async def disconnect(
    method _send_ping (line 625) | async def _send_ping(self):
    method _ping_failed (line 631) | async def _ping_failed(self, error, failure_count):
    method check_health (line 637) | async def check_health(self):
    method _send_packed_command (line 647) | async def _send_packed_command(self, command: Iterable[bytes]) -> None:
    method send_packed_command (line 651) | async def send_packed_command(
    method send_command (line 692) | async def send_command(self, *args: Any, **kwargs: Any) -> None:
    method can_read_destructive (line 698) | async def can_read_destructive(self):
    method read_response (line 707) | async def read_response(
    method pack_command (line 765) | def pack_command(self, *args: EncodableT) -> List[bytes]:
    method pack_commands (line 811) | def pack_commands(self, commands: Iterable[Iterable[EncodableT]]) -> L...
    method _socket_is_empty (line 841) | def _socket_is_empty(self):
    method process_invalidation_messages (line 845) | async def process_invalidation_messages(self):
    method set_re_auth_token (line 849) | def set_re_auth_token(self, token: TokenInterface):
    method re_auth (line 852) | async def re_auth(self):
  class Connection (line 863) | class Connection(AbstractConnection):
    method __init__ (line 866) | def __init__(
    method repr_pieces (line 883) | def repr_pieces(self):
    method _connection_arguments (line 889) | def _connection_arguments(self) -> Mapping:
    method _connect (line 892) | async def _connect(self):
    method _host_error (line 916) | def _host_error(self) -> str:
  class SSLConnection (line 920) | class SSLConnection(Connection):
    method __init__ (line 926) | def __init__(
    method _connection_arguments (line 961) | def _connection_arguments(self) -> Mapping:
    method keyfile (line 967) | def keyfile(self):
    method certfile (line 971) | def certfile(self):
    method cert_reqs (line 975) | def cert_reqs(self):
    method include_verify_flags (line 979) | def include_verify_flags(self):
    method exclude_verify_flags (line 983) | def exclude_verify_flags(self):
    method ca_certs (line 987) | def ca_certs(self):
    method ca_data (line 991) | def ca_data(self):
    method check_hostname (line 995) | def check_hostname(self):
    method min_version (line 999) | def min_version(self):
  class RedisSSLContext (line 1003) | class RedisSSLContext:
    method __init__ (line 1020) | def __init__(
    method get (line 1067) | def get(self) -> SSLContext:
  class UnixDomainSocketConnection (line 1096) | class UnixDomainSocketConnection(AbstractConnection):
    method __init__ (line 1099) | def __init__(self, *, path: str = "", **kwargs):
    method repr_pieces (line 1103) | def repr_pieces(self) -> Iterable[Tuple[str, Union[str, int]]]:
    method _connect (line 1109) | async def _connect(self):
    method _host_error (line 1116) | def _host_error(self) -> str:
  function to_bool (line 1123) | def to_bool(value) -> Optional[bool]:
  function parse_ssl_verify_flags (line 1131) | def parse_ssl_verify_flags(value):
  class ConnectKwargs (line 1162) | class ConnectKwargs(TypedDict, total=False):
  function parse_url (line 1172) | def parse_url(url: str) -> ConnectKwargs:
  class ConnectionPool (line 1228) | class ConnectionPool:
    method from_url (line 1244) | def from_url(cls: Type[_CP], url: str, **kwargs) -> _CP:
    method __init__ (line 1290) | def __init__(
    method __repr__ (line 1322) | def __repr__(self):
    method reset (line 1335) | def reset(self):
    method __del__ (line 1366) | def __del__(self) -> None:
    method can_get_connection (line 1397) | def can_get_connection(self) -> bool:
    method get_connection (line 1409) | async def get_connection(self, command_name=None, *keys, **options):
    method get_available_connection (line 1461) | def get_available_connection(self):
    method get_encoder (line 1472) | def get_encoder(self):
    method make_connection (line 1481) | def make_connection(self):
    method ensure_connection (line 1487) | async def ensure_connection(self, connection: AbstractConnection):
    method release (line 1503) | async def release(self, connection: AbstractConnection):
    method disconnect (line 1530) | async def disconnect(self, inuse_connections: bool = True):
    method update_active_connections_for_reconnect (line 1553) | async def update_active_connections_for_reconnect(self):
    method aclose (line 1561) | async def aclose(self) -> None:
    method set_retry (line 1565) | def set_retry(self, retry: "Retry") -> None:
    method re_auth_callback (line 1571) | async def re_auth_callback(self, token: TokenInterface):
    method _mock (line 1586) | async def _mock(self, error: RedisError):
    method get_connection_count (line 1594) | def get_connection_count(self) -> List[tuple[int, dict]]:
  class BlockingConnectionPool (line 1616) | class BlockingConnectionPool(ConnectionPool):
    method __init__ (line 1650) | def __init__(
    method get_connection (line 1671) | async def get_connection(self, command_name=None, *keys, **options):
    method release (line 1713) | async def release(self, connection: AbstractConnection):

FILE: redis/asyncio/http/http_client.py
  class AsyncHTTPClient (line 13) | class AsyncHTTPClient(ABC):
    method get (line 15) | async def get(
    method delete (line 30) | async def delete(
    method post (line 45) | async def post(
    method put (line 62) | async def put(
    method patch (line 79) | async def patch(
    method request (line 96) | async def request(
  class AsyncHTTPClientWrapper (line 112) | class AsyncHTTPClientWrapper(AsyncHTTPClient):
    method __init__ (line 117) | def __init__(self, client: HttpClient, max_workers: int = 10) -> None:
    method get (line 133) | async def get(
    method delete (line 148) | async def delete(
    method post (line 169) | async def post(
    method put (line 194) | async def put(
    method patch (line 219) | async def patch(
    method request (line 244) | async def request(

FILE: redis/asyncio/lock.py
  class Lock (line 17) | class Lock:
    method __init__ (line 82) | def __init__(
    method register_scripts (line 160) | def register_scripts(self):
    method __aenter__ (line 170) | async def __aenter__(self):
    method __aexit__ (line 175) | async def __aexit__(self, exc_type, exc_value, traceback):
    method acquire (line 185) | async def acquire(
    method do_acquire (line 234) | async def do_acquire(self, token: Union[str, bytes]) -> bool:
    method locked (line 244) | async def locked(self) -> bool:
    method owned (line 250) | async def owned(self) -> bool:
    method release (line 266) | async def release(self) -> None:
    method do_release (line 288) | async def do_release(self, expected_token: bytes) -> None:
    method extend (line 296) | def extend(
    method do_extend (line 315) | async def do_extend(self, additional_time, replace_ttl) -> bool:
    method reacquire (line 327) | def reacquire(self) -> Awaitable[bool]:
    method do_reacquire (line 337) | async def do_reacquire(self) -> bool:

FILE: redis/asyncio/multidb/client.py
  class MultiDBClient (line 35) | class MultiDBClient(AsyncRedisModuleCommands, AsyncCoreCommands):
    method __init__ (line 41) | def __init__(self, config: MultiDbConfig):
    method __aenter__ (line 86) | async def __aenter__(self: "MultiDBClient") -> "MultiDBClient":
    method aclose (line 91) | async def aclose(self):
    method __aexit__ (line 107) | async def __aexit__(self, exc_type, exc_value, traceback):
    method initialize (line 110) | async def initialize(self):
    method get_databases (line 146) | def get_databases(self) -> Databases:
    method set_active_database (line 152) | async def set_active_database(self, database: AsyncDatabase) -> None:
    method add_database (line 179) | async def add_database(
    method _change_active_database (line 228) | async def _change_active_database(
    method remove_database (line 239) | async def remove_database(self, database: AsyncDatabase):
    method update_database_weight (line 254) | async def update_database_weight(self, database: AsyncDatabase, weight...
    method add_failure_detector (line 273) | def add_failure_detector(self, failure_detector: AsyncFailureDetector):
    method add_health_check (line 279) | async def add_health_check(self, healthcheck: HealthCheck):
    method execute_command (line 286) | async def execute_command(self, *args, **options):
    method pipeline (line 295) | def pipeline(self):
    method transaction (line 301) | async def transaction(
    method pubsub (line 323) | async def pubsub(self, **kwargs):
    method _check_databases_health (line 334) | async def _check_databases_health(self) -> dict[Database, bool]:
    method _perform_initial_health_check (line 368) | async def _perform_initial_health_check(self):
    method _check_db_health (line 392) | async def _check_db_health(self, database: AsyncDatabase) -> bool:
    method _on_circuit_state_change_callback (line 410) | def _on_circuit_state_change_callback(
  function _half_open_circuit (line 431) | def _half_open_circuit(circuit: CircuitBreaker):
  class Pipeline (line 435) | class Pipeline(AsyncRedisModuleCommands, AsyncCoreCommands):
    method __init__ (line 442) | def __init__(self, client: MultiDBClient):
    method __aenter__ (line 446) | async def __aenter__(self: "Pipeline") -> "Pipeline":
    method __aexit__ (line 449) | async def __aexit__(self, exc_type, exc_value, traceback):
    method __await__ (line 453) | def __await__(self):
    method _async_self (line 456) | async def _async_self(self):
    method __len__ (line 459) | def __len__(self) -> int:
    method __bool__ (line 462) | def __bool__(self) -> bool:
    method reset (line 466) | async def reset(self) -> None:
    method aclose (line 469) | async def aclose(self) -> None:
    method pipeline_execute_command (line 473) | def pipeline_execute_command(self, *args, **options) -> "Pipeline":
    method execute_command (line 488) | def execute_command(self, *args, **kwargs):
    method execute (line 492) | async def execute(self) -> List[Any]:
  class PubSub (line 505) | class PubSub:
    method __init__ (line 510) | def __init__(self, client: MultiDBClient, **kwargs):
    method __aenter__ (line 521) | async def __aenter__(self) -> "PubSub":
    method __aexit__ (line 524) | async def __aexit__(self, exc_type, exc_value, traceback) -> None:
    method aclose (line 527) | async def aclose(self):
    method subscribed (line 531) | def subscribed(self) -> bool:
    method execute_command (line 534) | async def execute_command(self, *args: EncodableT):
    method psubscribe (line 539) | async def psubscribe(self, *args: ChannelT, **kwargs: PubSubHandler):
    method punsubscribe (line 551) | async def punsubscribe(self, *args: ChannelT):
    method subscribe (line 560) | async def subscribe(self, *args: ChannelT, **kwargs: Callable):
    method unsubscribe (line 572) | async def unsubscribe(self, *args):
    method get_message (line 581) | async def get_message(
    method run (line 597) | async def run(

FILE: redis/asyncio/multidb/command_executor.py
  class AsyncCommandExecutor (line 33) | class AsyncCommandExecutor(CommandExecutor):
    method databases (line 36) | def databases(self) -> Databases:
    method failure_detectors (line 42) | def failure_detectors(self) -> List[AsyncFailureDetector]:
    method add_failure_detector (line 47) | def add_failure_detector(self, failure_detector: AsyncFailureDetector)...
    method active_database (line 53) | def active_database(self) -> Optional[AsyncDatabase]:
    method set_active_database (line 58) | async def set_active_database(
    method active_pubsub (line 71) | def active_pubsub(self) -> Optional[PubSub]:
    method active_pubsub (line 77) | def active_pubsub(self, pubsub: PubSub) -> None:
    method failover_strategy_executor (line 83) | def failover_strategy_executor(self) -> FailoverStrategyExecutor:
    method command_retry (line 89) | def command_retry(self) -> Retry:
    method pubsub (line 94) | async def pubsub(self, **kwargs):
    method execute_command (line 99) | async def execute_command(self, *args, **options):
    method execute_pipeline (line 104) | async def execute_pipeline(self, command_stack: tuple):
    method execute_transaction (line 109) | async def execute_transaction(
    method execute_pubsub_method (line 116) | async def execute_pubsub_method(self, method_name: str, *args, **kwargs):
    method execute_pubsub_run (line 121) | async def execute_pubsub_run(self, sleep_time: float, **kwargs) -> Any:
  class DefaultCommandExecutor (line 126) | class DefaultCommandExecutor(BaseCommandExecutor, AsyncCommandExecutor):
    method __init__ (line 127) | def __init__(
    method databases (line 170) | def databases(self) -> Databases:
    method failure_detectors (line 174) | def failure_detectors(self) -> List[AsyncFailureDetector]:
    method add_failure_detector (line 177) | def add_failure_detector(self, failure_detector: AsyncFailureDetector)...
    method active_database (line 181) | def active_database(self) -> Optional[AsyncDatabase]:
    method set_active_database (line 184) | async def set_active_database(
    method active_pubsub (line 206) | def active_pubsub(self) -> Optional[PubSub]:
    method active_pubsub (line 210) | def active_pubsub(self, pubsub: PubSub) -> None:
    method failover_strategy_executor (line 214) | def failover_strategy_executor(self) -> FailoverStrategyExecutor:
    method command_retry (line 218) | def command_retry(self) -> Retry:
    method pubsub (line 221) | def pubsub(self, **kwargs):
    method execute_command (line 229) | async def execute_command(self, *args, **options):
    method execute_pipeline (line 239) | async def execute_pipeline(self, command_stack: tuple):
    method execute_transaction (line 251) | async def execute_transaction(
    method execute_pubsub_method (line 272) | async def execute_pubsub_method(self, method_name: str, *args, **kwargs):
    method execute_pubsub_run (line 285) | async def execute_pubsub_run(
    method _execute_with_failure_detection (line 297) | async def _execute_with_failure_detection(
    method _check_active_database (line 314) | async def _check_active_database(self):
    method _on_command_fail (line 332) | async def _on_command_fail(self, error, *args):
    method _register_command_execution (line 337) | async def _register_command_execution(self, cmd: tuple):
    method _setup_event_dispatcher (line 341) | def _setup_event_dispatcher(self):

FILE: redis/asyncio/multidb/config.py
  class InitialHealthCheck (line 48) | class InitialHealthCheck(Enum):
  function default_event_dispatcher (line 54) | def default_event_dispatcher() -> EventDispatcherInterface:
  class DatabaseConfig (line 59) | class DatabaseConfig:
    method default_circuit_breaker (line 92) | def default_circuit_breaker(self) -> CircuitBreaker:
  class MultiDbConfig (line 98) | class MultiDbConfig:
    method databases (line 166) | def databases(self) -> Databases:
    method default_failure_detectors (line 207) | def default_failure_detectors(self) -> List[AsyncFailureDetector]:
    method default_health_checks (line 218) | def default_health_checks(self) -> List[HealthCheck]:
    method default_failover_strategy (line 227) | def default_failover_strategy(self) -> AsyncFailoverStrategy:

FILE: redis/asyncio/multidb/database.py
  class AsyncDatabase (line 11) | class AsyncDatabase(AbstractDatabase):
    method client (line 16) | def client(self) -> Union[Redis, RedisCluster]:
    method client (line 22) | def client(self, client: Union[Redis, RedisCluster]):
    method circuit (line 28) | def circuit(self) -> CircuitBreaker:
    method circuit (line 34) | def circuit(self, circuit: CircuitBreaker):
  class Database (line 42) | class Database(BaseDatabase, AsyncDatabase):
    method __init__ (line 43) | def __init__(
    method client (line 56) | def client(self) -> Union[Redis, RedisCluster]:
    method client (line 60) | def client(self, client: Union[Redis, RedisCluster]):
    method circuit (line 64) | def circuit(self) -> CircuitBreaker:
    method circuit (line 68) | def circuit(self, circuit: CircuitBreaker):
    method __repr__ (line 71) | def __repr__(self):

FILE: redis/asyncio/multidb/event.py
  class AsyncActiveDatabaseChanged (line 9) | class AsyncActiveDatabaseChanged:
    method __init__ (line 14) | def __init__(
    method old_database (line 27) | def old_database(self) -> AsyncDatabase:
    method new_database (line 31) | def new_database(self) -> AsyncDatabase:
    method command_executor (line 35) | def command_executor(self):
    method kwargs (line 39) | def kwargs(self):
  class ResubscribeOnActiveDatabaseChanged (line 43) | class ResubscribeOnActiveDatabaseChanged(AsyncEventListenerInterface):
    method listen (line 48) | async def listen(self, event: AsyncActiveDatabaseChanged):
  class CloseConnectionOnActiveDatabaseChanged (line 61) | class CloseConnectionOnActiveDatabaseChanged(AsyncEventListenerInterface):
    method listen (line 66) | async def listen(self, event: AsyncActiveDatabaseChanged):
  class RegisterCommandFailure (line 74) | class RegisterCommandFailure(AsyncEventListenerInterface):
    method __init__ (line 79) | def __init__(self, failure_detectors: List[AsyncFailureDetector]):
    method listen (line 82) | async def listen(self, event: AsyncOnCommandsFailEvent) -> None:

FILE: redis/asyncio/multidb/failover.py
  class AsyncFailoverStrategy (line 16) | class AsyncFailoverStrategy(ABC):
    method database (line 18) | async def database(self) -> AsyncDatabase:
    method set_databases (line 23) | def set_databases(self, databases: Databases) -> None:
  class FailoverStrategyExecutor (line 28) | class FailoverStrategyExecutor(ABC):
    method failover_attempts (line 31) | def failover_attempts(self) -> int:
    method failover_delay (line 37) | def failover_delay(self) -> float:
    method strategy (line 43) | def strategy(self) -> AsyncFailoverStrategy:
    method execute (line 48) | async def execute(self) -> AsyncDatabase:
  class WeightBasedFailoverStrategy (line 53) | class WeightBasedFailoverStrategy(AsyncFailoverStrategy):
    method __init__ (line 58) | def __init__(self):
    method database (line 61) | async def database(self) -> AsyncDatabase:
    method set_databases (line 68) | def set_databases(self, databases: Databases) -> None:
  class DefaultFailoverStrategyExecutor (line 72) | class DefaultFailoverStrategyExecutor(FailoverStrategyExecutor):
    method __init__ (line 77) | def __init__(
    method failover_attempts (line 90) | def failover_attempts(self) -> int:
    method failover_delay (line 94) | def failover_delay(self) -> float:
    method strategy (line 98) | def strategy(self) -> AsyncFailoverStrategy:
    method execute (line 101) | async def execute(self) -> AsyncDatabase:
    method _reset (line 123) | def _reset(self) -> None:

FILE: redis/asyncio/multidb/failure_detector.py
  class AsyncFailureDetector (line 6) | class AsyncFailureDetector(ABC):
    method register_failure (line 8) | async def register_failure(self, exception: Exception, cmd: tuple) -> ...
    method register_command_execution (line 13) | async def register_command_execution(self, cmd: tuple) -> None:
    method set_command_executor (line 18) | def set_command_executor(self, command_executor) -> None:
  class FailureDetectorAsyncWrapper (line 23) | class FailureDetectorAsyncWrapper(AsyncFailureDetector):
    method __init__ (line 28) | def __init__(self, failure_detector: FailureDetector) -> None:
    method register_failure (line 31) | async def register_failure(self, exception: Exception, cmd: tuple) -> ...
    method register_command_execution (line 34) | async def register_command_execution(self, cmd: tuple) -> None:
    method set_command_executor (line 37) | def set_command_executor(self, command_executor) -> None:

FILE: redis/asyncio/multidb/healthcheck.py
  function _get_init_params (line 22) | def _get_init_params(cls: Type) -> frozenset:
  function _filter_kwargs (line 37) | def _filter_kwargs(kwargs: dict, cls: Type) -> dict:
  class HealthCheck (line 52) | class HealthCheck(ABC):
    method health_check_probes (line 59) | def health_check_probes(self) -> int:
    method health_check_delay (line 65) | def health_check_delay(self) -> float:
    method health_check_timeout (line 71) | def health_check_timeout(self) -> float:
    method check_health (line 76) | async def check_health(self, database, hc_client: AsyncRedisClientT) -...
  class HealthCheckPolicy (line 91) | class HealthCheckPolicy(ABC):
    method execute (line 97) | async def execute(self, health_checks: List[HealthCheck], database) ->...
    method _execute (line 102) | async def _execute(self, health_check: HealthCheck, database) -> bool:
    method get_client (line 109) | async def get_client(self, database) -> AsyncRedisClientT:
    method close (line 116) | async def close(self) -> None:
  class AbstractHealthCheckPolicy (line 121) | class AbstractHealthCheckPolicy(HealthCheckPolicy):
    method __init__ (line 126) | def __init__(self):
    method execute (line 130) | async def execute(self, health_checks: List[HealthCheck], database) ->...
    method get_client (line 163) | async def get_client(self, database) -> AsyncRedisClientT:
    method close (line 208) | async def close(self) -> None:
    method _execute (line 220) | async def _execute(self, health_check: HealthCheck, database) -> bool:
  class HealthyAllPolicy (line 227) | class HealthyAllPolicy(AbstractHealthCheckPolicy):
    method _execute (line 232) | async def _execute(self, health_check: HealthCheck, database) -> bool:
  class HealthyMajorityPolicy (line 252) | class HealthyMajorityPolicy(AbstractHealthCheckPolicy):
    method _execute (line 262) | async def _execute(self, health_check: HealthCheck, database) -> bool:
  class HealthyAnyPolicy (line 296) | class HealthyAnyPolicy(AbstractHealthCheckPolicy):
    method _execute (line 301) | async def _execute(self, health_check: HealthCheck, database) -> bool:
  class HealthCheckPolicies (line 330) | class HealthCheckPolicies(Enum):
  class AbstractHealthCheck (line 339) | class AbstractHealthCheck(HealthCheck):
    method __init__ (line 340) | def __init__(
    method health_check_probes (line 353) | def health_check_probes(self) -> int:
    method health_check_delay (line 357) | def health_check_delay(self) -> float:
    method health_check_timeout (line 361) | def health_check_timeout(self) -> float:
    method check_health (line 365) | async def check_health(self, database, hc_client: AsyncRedisClientT) -...
  class PingHealthCheck (line 369) | class PingHealthCheck(AbstractHealthCheck):
    method check_health (line 374) | async def check_health(self, database, hc_client: AsyncRedisClientT) -...
  class LagAwareHealthCheck (line 387) | class LagAwareHealthCheck(AbstractHealthCheck):
    method __init__ (line 393) | def __init__(
    method check_health (line 451) | async def check_health(self, database, hc_client: AsyncRedisClientT) -...

FILE: redis/asyncio/observability/recorder.py
  function _get_or_create_collector (line 47) | def _get_or_create_collector() -> Optional[RedisMetricsCollector]:
  function _get_config (line 80) | async def _get_config() -> Optional["OTelConfig"]:
  function record_operation_duration (line 96) | async def record_operation_duration(
  function record_connection_create_time (line 148) | async def record_connection_create_time(
  function record_connection_count (line 172) | async def record_connection_count(
  function init_connection_count (line 204) | async def init_connection_count() -> None:
  function register_pools_connection_count (line 235) | async def register_pools_connection_count(
  function record_connection_timeout (line 264) | async def record_connection_timeout(
  function record_connection_wait_time (line 285) | async def record_connection_wait_time(
  function record_connection_closed (line 309) | async def record_connection_closed(
  function record_connection_relaxed_timeout (line 333) | async def record_connection_relaxed_timeout(
  function record_connection_handoff (line 360) | async def record_connection_handoff(
  function record_error_count (line 381) | async def record_error_count(
  function record_pubsub_message (line 420) | async def record_pubsub_message(
  function record_streaming_lag (line 457) | async def record_streaming_lag(
  function record_streaming_lag_from_response (line 491) | async def record_streaming_lag_from_response(
  function record_maint_notification_count (line 559) | async def record_maint_notification_count(
  function record_geo_failover (line 592) | async def record_geo_failover(
  function reset_collector (line 619) | def reset_collector() -> None:
  function is_enabled (line 627) | async def is_enabled() -> bool:

FILE: redis/asyncio/retry.py
  class Retry (line 23) | class Retry(AbstractRetry[RedisError]):
    method __init__ (line 26) | def __init__(
    method __eq__ (line 37) | def __eq__(self, other: Any) -> bool:
    method call_with_retry (line 47) | async def call_with_retry(

FILE: redis/asyncio/sentinel.py
  class MasterNotFoundError (line 22) | class MasterNotFoundError(ConnectionError):
  class SlaveNotFoundError (line 26) | class SlaveNotFoundError(ConnectionError):
  class SentinelManagedConnection (line 30) | class SentinelManagedConnection(Connection):
    method __init__ (line 31) | def __init__(self, **kwargs):
    method __repr__ (line 35) | def __repr__(self):
    method connect_to (line 42) | async def connect_to(self, address):
    method _connect_retry (line 49) | async def _connect_retry(self):
    method connect (line 62) | async def connect(self):
    method read_response (line 68) | async def read_response(
  class SentinelManagedSSLConnection (line 95) | class SentinelManagedSSLConnection(SentinelManagedConnection, SSLConnect...
  class SentinelConnectionPool (line 99) | class SentinelConnectionPool(ConnectionPool):
    method __init__ (line 107) | def __init__(self, service_name, sentinel_manager, **kwargs):
    method __repr__ (line 125) | def __repr__(self):
    method reset (line 131) | def reset(self):
    method owns_connection (line 136) | def owns_connection(self, connection: Connection):
    method get_master_address (line 142) | async def get_master_address(self):
    method rotate_slaves (line 152) | async def rotate_slaves(self) -> AsyncIterator:
  class Sentinel (line 170) | class Sentinel(AsyncSentinelCommands):
    method __init__ (line 199) | def __init__(
    method execute_command (line 223) | async def execute_command(self, *args, **kwargs):
    method __repr__ (line 255) | def __repr__(self):
    method check_master_state (line 267) | def check_master_state(self, state: dict, service_name: str) -> bool:
    method discover_master (line 275) | async def discover_master(self, service_name: str):
    method filter_slaves (line 310) | def filter_slaves(
    method discover_slaves (line 321) | async def discover_slaves(
    method master_for (line 335) | def master_for(
    method slave_for (line 374) | def slave_for(

FILE: redis/asyncio/utils.py
  function from_url (line 7) | def from_url(url: str, **kwargs: Any) -> "Redis":
  class pipeline (line 19) | class pipeline:  # noqa: N801
    method __init__ (line 20) | def __init__(self, redis_obj: "Redis"):
    method __aenter__ (line 23) | async def __aenter__(self) -> "Pipeline":
    method __aexit__ (line 26) | async def __aexit__(self, exc_type, exc_value, traceback):

FILE: redis/auth/err.py
  class RequestTokenErr (line 4) | class RequestTokenErr(Exception):
    method __init__ (line 9) | def __init__(self, *args):
  class InvalidTokenSchemaErr (line 13) | class InvalidTokenSchemaErr(Exception):
    method __init__ (line 18) | def __init__(self, missing_fields: Iterable[str] = []):
  class TokenRenewalErr (line 25) | class TokenRenewalErr(Exception):
    method __init__ (line 30) | def __init__(self, *args):

FILE: redis/auth/idp.py
  class IdentityProviderInterface (line 10) | class IdentityProviderInterface(ABC):
    method request_token (line 17) | def request_token(self, force_refresh=False) -> TokenInterface:
  class IdentityProviderConfigInterface (line 21) | class IdentityProviderConfigInterface(ABC):
    method get_provider (line 27) | def get_provider(self) -> IdentityProviderInterface:

FILE: redis/auth/token.py
  class TokenInterface (line 7) | class TokenInterface(ABC):
    method is_expired (line 9) | def is_expired(self) -> bool:
    method ttl (line 13) | def ttl(self) -> float:
    method try_get (line 17) | def try_get(self, key: str) -> str:
    method get_value (line 21) | def get_value(self) -> str:
    method get_expires_at_ms (line 25) | def get_expires_at_ms(self) -> float:
    method get_received_at_ms (line 29) | def get_received_at_ms(self) -> float:
  class TokenResponse (line 33) | class TokenResponse:
    method __init__ (line 34) | def __init__(self, token: TokenInterface):
    method get_token (line 37) | def get_token(self) -> TokenInterface:
    method get_ttl_ms (line 40) | def get_ttl_ms(self) -> float:
  class SimpleToken (line 44) | class SimpleToken(TokenInterface):
    method __init__ (line 45) | def __init__(
    method ttl (line 53) | def ttl(self) -> float:
    method is_expired (line 59) | def is_expired(self) -> bool:
    method try_get (line 65) | def try_get(self, key: str) -> str:
    method get_value (line 68) | def get_value(self) -> str:
    method get_expires_at_ms (line 71) | def get_expires_at_ms(self) -> float:
    method get_received_at_ms (line 74) | def get_received_at_ms(self) -> float:
  class JWToken (line 78) | class JWToken(TokenInterface):
    method __init__ (line 81) | def __init__(self, token: str):
    method is_expired (line 96) | def is_expired(self) -> bool:
    method ttl (line 105) | def ttl(self) -> float:
    method try_get (line 114) | def try_get(self, key: str) -> str:
    method get_value (line 117) | def get_value(self) -> str:
    method get_expires_at_ms (line 120) | def get_expires_at_ms(self) -> float:
    method get_received_at_ms (line 123) | def get_received_at_ms(self) -> float:
    method _validate_token (line 126) | def _validate_token(self):

FILE: redis/auth/token_manager.py
  class CredentialsListener (line 15) | class CredentialsListener:
    method __init__ (line 21) | def __init__(self):
    method on_next (line 26) | def on_next(self) -> Union[Callable[[Any], None], Awaitable]:
    method on_next (line 30) | def on_next(self, callback: Union[Callable[[Any], None], Awaitable]) -...
    method on_error (line 34) | def on_error(self) -> Union[Callable[[Exception], None], Awaitable]:
    method on_error (line 38) | def on_error(self, callback: Union[Callable[[Exception], None], Awaita...
  class RetryPolicy (line 42) | class RetryPolicy:
    method __init__ (line 43) | def __init__(self, max_attempts: int, delay_in_ms: float):
    method get_max_attempts (line 47) | def get_max_attempts(self) -> int:
    method get_delay_in_ms (line 55) | def get_delay_in_ms(self) -> float:
  class TokenManagerConfig (line 64) | class TokenManagerConfig:
    method __init__ (line 65) | def __init__(
    method get_expiration_refresh_ratio (line 79) | def get_expiration_refresh_ratio(self) -> float:
    method get_lower_refresh_bound_millis (line 90) | def get_lower_refresh_bound_millis(self) -> int:
    method get_token_request_execution_timeout_in_ms (line 103) | def get_token_request_execution_timeout_in_ms(self) -> int:
    method get_retry_policy (line 112) | def get_retry_policy(self) -> RetryPolicy:
  class TokenManager (line 121) | class TokenManager:
    method __init__ (line 122) | def __init__(
    method __del__ (line 132) | def __del__(self):
    method start (line 136) | def start(
    method start_async (line 184) | async def start_async(
    method stop (line 208) | def stop(self):
    method acquire_token (line 214) | def acquire_token(self, force_refresh=False) -> TokenResponse:
    method acquire_token_async (line 228) | async def acquire_token_async(self, force_refresh=False) -> TokenRespo...
    method _calculate_renewal_delay (line 244) | def _calculate_renewal_delay(self, expire_date: float, issue_date: flo...
    method _delay_for_lower_refresh (line 251) | def _delay_for_lower_refresh(self, expire_date: float):
    method _delay_for_ratio_refresh (line 258) | def _delay_for_ratio_refresh(self, expire_date: float, issue_date: flo...
    method _renew_token (line 270) | def _renew_token(self, skip_initial: bool = False):
    method _renew_token_async (line 311) | async def _renew_token_async(
  function _async_to_sync_wrapper (line 358) | def _async_to_sync_wrapper(loop, coro_func, *args, **kwargs):

FILE: redis/background.py
  class BackgroundScheduler (line 7) | class BackgroundScheduler:
    method __init__ (line 12) | def __init__(self):
    method __del__ (line 23) | def __del__(self):
    method stop (line 26) | def stop(self):
    method run_once (line 46) | def run_once(self, delay: float, callback: Callable, *args):
    method run_recurring (line 67) | def run_recurring(self, interval: float, callback: Callable, *args):
    method run_recurring_coro (line 88) | def run_recurring_coro(
    method run_coro_sync (line 112) | def run_coro_sync(
    method run_coro_fire_and_forget (line 160) | def run_coro_fire_and_forget(
    method _ensure_health_check_loop (line 198) | def _ensure_health_check_loop(self, timeout: float = 5.0):
    method _run_health_check_loop (line 258) | def _run_health_check_loop(self):
    method _call_later_recurring_coro (line 281) | def _call_later_recurring_coro(
    method _execute_recurring_coro (line 296) | def _execute_recurring_coro(
    method run_recurring_async (line 357) | async def run_recurring_async(
    method _call_later (line 418) | def _call_later(
    method _call_later_recurring (line 426) | def _call_later_recurring(
    method _execute_recurring (line 440) | def _execute_recurring(
  function _start_event_loop_in_thread (line 469) | def _start_event_loop_in_thread(

FILE: redis/backoff.py
  class AbstractBackoff (line 10) | class AbstractBackoff(ABC):
    method reset (line 13) | def reset(self):
    method compute (line 22) | def compute(self, failures: int) -> float:
  class ConstantBackoff (line 27) | class ConstantBackoff(AbstractBackoff):
    method __init__ (line 30) | def __init__(self, backoff: float) -> None:
    method __hash__ (line 34) | def __hash__(self) -> int:
    method __eq__ (line 37) | def __eq__(self, other) -> bool:
    method compute (line 43) | def compute(self, failures: int) -> float:
  class NoBackoff (line 47) | class NoBackoff(ConstantBackoff):
    method __init__ (line 50) | def __init__(self) -> None:
  class ExponentialBackoff (line 54) | class ExponentialBackoff(AbstractBackoff):
    method __init__ (line 57) | def __init__(self, cap: float = DEFAULT_CAP, base: float = DEFAULT_BASE):
    method __hash__ (line 65) | def __hash__(self) -> int:
    method __eq__ (line 68) | def __eq__(self, other) -> bool:
    method compute (line 74) | def compute(self, failures: int) -> float:
  class FullJitterBackoff (line 78) | class FullJitterBackoff(AbstractBackoff):
    method __init__ (line 81) | def __init__(self, cap: float = DEFAULT_CAP, base: float = DEFAULT_BAS...
    method __hash__ (line 89) | def __hash__(self) -> int:
    method __eq__ (line 92) | def __eq__(self, other) -> bool:
    method compute (line 98) | def compute(self, failures: int) -> float:
  class EqualJitterBackoff (line 102) | class EqualJitterBackoff(AbstractBackoff):
    method __init__ (line 105) | def __init__(self, cap: float = DEFAULT_CAP, base: float = DEFAULT_BAS...
    method __hash__ (line 113) | def __hash__(self) -> int:
    method __eq__ (line 116) | def __eq__(self, other) -> bool:
    method compute (line 122) | def compute(self, failures: int) -> float:
  class DecorrelatedJitterBackoff (line 127) | class DecorrelatedJitterBackoff(AbstractBackoff):
    method __init__ (line 130) | def __init__(self, cap: float = DEFAULT_CAP, base: float = DEFAULT_BAS...
    method __hash__ (line 139) | def __hash__(self) -> int:
    method __eq__ (line 142) | def __eq__(self, other) -> bool:
    method reset (line 148) | def reset(self) -> None:
    method compute (line 151) | def compute(self, failures: int) -> float:
  class ExponentialWithJitterBackoff (line 158) | class ExponentialWithJitterBackoff(AbstractBackoff):
    method __init__ (line 161) | def __init__(self, cap: float = DEFAULT_CAP, base: float = DEFAULT_BAS...
    method __hash__ (line 169) | def __hash__(self) -> int:
    method __eq__ (line 172) | def __eq__(self, other) -> bool:
    method compute (line 178) | def compute(self, failures: int) -> float:
  function default_backoff (line 182) | def default_backoff():

FILE: redis/cache.py
  class CacheEntryStatus (line 10) | class CacheEntryStatus(Enum):
  class EvictionPolicyType (line 15) | class EvictionPolicyType(Enum):
  class CacheKey (line 21) | class CacheKey:
  class CacheEntry (line 39) | class CacheEntry:
    method __init__ (line 40) | def __init__(
    method __hash__ (line 52) | def __hash__(self):
    method __eq__ (line 57) | def __eq__(self, other):
  class EvictionPolicyInterface (line 61) | class EvictionPolicyInterface(ABC):
    method cache (line 64) | def cache(self):
    method cache (line 69) | def cache(self, value):
    method type (line 74) | def type(self) -> EvictionPolicyType:
    method evict_next (line 78) | def evict_next(self) -> CacheKey:
    method evict_many (line 82) | def evict_many(self, count: int) -> List[CacheKey]:
    method touch (line 86) | def touch(self, cache_key: CacheKey) -> None:
  class CacheConfigurationInterface (line 90) | class CacheConfigurationInterface(ABC):
    method get_cache_class (line 92) | def get_cache_class(self):
    method get_max_size (line 96) | def get_max_size(self) -> int:
    method get_eviction_policy (line 100) | def get_eviction_policy(self):
    method is_exceeds_max_size (line 104) | def is_exceeds_max_size(self, count: int) -> bool:
    method is_allowed_to_cache (line 108) | def is_allowed_to_cache(self, command: str) -> bool:
  class CacheInterface (line 112) | class CacheInterface(ABC):
    method collection (line 115) | def collection(self) -> OrderedDict:
    method config (line 120) | def config(self) -> CacheConfigurationInterface:
    method eviction_policy (line 125) | def eviction_policy(self) -> EvictionPolicyInterface:
    method size (line 130) | def size(self) -> int:
    method get (line 134) | def get(self, key: CacheKey) -> Union[CacheEntry, None]:
    method set (line 138) | def set(self, entry: CacheEntry) -> bool:
    method delete_by_cache_keys (line 142) | def delete_by_cache_keys(self, cache_keys: List[CacheKey]) -> List[bool]:
    method delete_by_redis_keys (line 146) | def delete_by_redis_keys(self, redis_keys: List[bytes]) -> List[bool]:
    method flush (line 150) | def flush(self) -> int:
    method is_cachable (line 154) | def is_cachable(self, key: CacheKey) -> bool:
  class DefaultCache (line 158) | class DefaultCache(CacheInterface):
    method __init__ (line 159) | def __init__(
    method collection (line 169) | def collection(self) -> OrderedDict:
    method config (line 173) | def config(self) -> CacheConfigurationInterface:
    method eviction_policy (line 177) | def eviction_policy(self) -> EvictionPolicyInterface:
    method size (line 181) | def size(self) -> int:
    method set (line 184) | def set(self, entry: CacheEntry) -> bool:
    method get (line 193) | def get(self, key: CacheKey) -> Union[CacheEntry, None]:
    method delete_by_cache_keys (line 202) | def delete_by_cache_keys(self, cache_keys: List[CacheKey]) -> List[bool]:
    method delete_by_redis_keys (line 214) | def delete_by_redis_keys(
    method flush (line 241) | def flush(self) -> int:
    method is_cachable (line 246) | def is_cachable(self, key: CacheKey) -> bool:
  class CacheProxy (line 250) | class CacheProxy(CacheInterface):
    method __init__ (line 255) | def __init__(self, cache: CacheInterface):
    method collection (line 259) | def collection(self) -> OrderedDict:
    method config (line 263) | def config(self) -> CacheConfigurationInterface:
    method eviction_policy (line 267) | def eviction_policy(self) -> EvictionPolicyInterface:
    method size (line 271) | def size(self) -> int:
    method get (line 274) | def get(self, key: CacheKey) -> Union[CacheEntry, None]:
    method set (line 277) | def set(self, entry: CacheEntry) -> bool:
    method delete_by_cache_keys (line 292) | def delete_by_cache_keys(self, cache_keys: List[CacheKey]) -> List[bool]:
    method delete_by_redis_keys (line 295) | def delete_by_redis_keys(self, redis_keys: List[bytes]) -> List[bool]:
    method flush (line 298) | def flush(self) -> int:
    method is_cachable (line 301) | def is_cachable(self, key: CacheKey) -> bool:
  class LRUPolicy (line 305) | class LRUPolicy(EvictionPolicyInterface):
    method __init__ (line 306) | def __init__(self):
    method cache (line 310) | def cache(self):
    method cache (line 314) | def cache(self, cache: CacheInterface):
    method type (line 318) | def type(self) -> EvictionPolicyType:
    method evict_next (line 321) | def evict_next(self) -> CacheKey:
    method evict_many (line 326) | def evict_many(self, count: int) -> List[CacheKey]:
    method touch (line 339) | def touch(self, cache_key: CacheKey) -> None:
    method _assert_cache (line 347) | def _assert_cache(self):
  class EvictionPolicy (line 352) | class EvictionPolicy(Enum):
  class CacheConfig (line 356) | class CacheConfig(CacheConfigurationInterface):
    method __init__ (line 438) | def __init__(
    method get_cache_class (line 448) | def get_cache_class(self):
    method get_max_size (line 451) | def get_max_size(self) -> int:
    method get_eviction_policy (line 454) | def get_eviction_policy(self) -> EvictionPolicy:
    method is_exceeds_max_size (line 457) | def is_exceeds_max_size(self, count: int) -> bool:
    method is_allowed_to_cache (line 460) | def is_allowed_to_cache(self, command: str) -> bool:
  class CacheFactoryInterface (line 464) | class CacheFactoryInterface(ABC):
    method get_cache (line 466) | def get_cache(self) -> CacheInterface:
  class CacheFactory (line 470) | class CacheFactory(CacheFactoryInterface):
    method __init__ (line 471) | def __init__(self, cache_config: Optional[CacheConfig] = None):
    method get_cache (line 477) | def get_cache(self) -> CacheInterface:

FILE: redis/client.py
  function is_debug_log_enabled (line 98) | def is_debug_log_enabled():
  function add_debug_log_for_operation_failure (line 102) | def add_debug_log_for_operation_failure(connection: "AbstractConnection"):
  class CaseInsensitiveDict (line 109) | class CaseInsensitiveDict(dict):
    method __init__ (line 112) | def __init__(self, data: Dict[str, str]) -> None:
    method __contains__ (line 116) | def __contains__(self, k):
    method __delitem__ (line 119) | def __delitem__(self, k):
    method __getitem__ (line 122) | def __getitem__(self, k):
    method get (line 125) | def get(self, k, default=None):
    method __setitem__ (line 128) | def __setitem__(self, k, v):
    method update (line 131) | def update(self, data):
  class AbstractRedis (line 136) | class AbstractRedis:
  class Redis (line 140) | class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
    method from_url (line 159) | def from_url(cls, url: str, **kwargs) -> "Redis":
    method from_pool (line 210) | def from_pool(
    method __init__ (line 235) | def __init__(
    method __repr__ (line 486) | def __repr__(self) -> str:
    method get_encoder (line 492) | def get_encoder(self) -> "Encoder":
    method get_connection_kwargs (line 496) | def get_connection_kwargs(self) -> Dict:
    method get_retry (line 500) | def get_retry(self) -> Optional[Retry]:
    method set_retry (line 503) | def set_retry(self, retry: Retry) -> None:
    method set_response_callback (line 507) | def set_response_callback(self, command: str, callback: Callable) -> N...
    method load_external_module (line 511) | def load_external_module(self, funcname, func) -> None:
    method pipeline (line 534) | def pipeline(self, transaction=True, shard_hint=None) -> "Pipeline":
    method transaction (line 546) | def transaction(
    method lock (line 570) | def lock(
    method pubsub (line 651) | def pubsub(self, **kwargs):
    method monitor (line 661) | def monitor(self):
    method client (line 664) | def client(self):
    method __enter__ (line 670) | def __enter__(self):
    method __exit__ (line 673) | def __exit__(self, exc_type, exc_value, traceback):
    method __del__ (line 676) | def __del__(self):
    method close (line 682) | def close(self) -> None:
    method _send_command_parse_response (line 697) | def _send_command_parse_response(self, conn, command_name, *args, **op...
    method _close_connection (line 704) | def _close_connection(
    method execute_command (line 735) | def execute_command(self, *args, **options):
    method _execute_command (line 738) | def _execute_command(self, *args, **options):
    method parse_response (line 795) | def parse_response(self, connection, command_name, **options):
    method get_cache (line 818) | def get_cache(self) -> Optional[CacheInterface]:
  class Monitor (line 825) | class Monitor:
    method __init__ (line 835) | def __init__(self, connection_pool):
    method __enter__ (line 839) | def __enter__(self):
    method __exit__ (line 843) | def __exit__(self, *args):
    method next_command (line 847) | def next_command(self):
    method listen (line 892) | def listen(self):
    method _start_monitor (line 897) | def _start_monitor(self):
  class PubSub (line 906) | class PubSub:
    method __init__ (line 919) | def __init__(
    method __enter__ (line 954) | def __enter__(self) -> "PubSub":
    method __exit__ (line 957) | def __exit__(self, exc_type, exc_value, traceback) -> None:
    method __del__ (line 960) | def __del__(self) -> None:
    method reset (line 969) | def reset(self) -> None:
    method close (line 984) | def close(self) -> None:
    method on_connect (line 987) | def on_connect(self, connection) -> None:
    method subscribed (line 1033) | def subscribed(self) -> bool:
    method execute_command (line 1037) | def execute_command(self, *args):
    method clean_health_check_responses (line 1063) | def clean_health_check_responses(self) -> None:
    method _reconnect (line 1081) | def _reconnect(
    method _execute (line 1109) | def _execute(self, conn, command, *args, **kwargs):
    method parse_response (line 1164) | def parse_response(self, block=True, timeout=0):
    method is_health_check_response (line 1232) | def is_health_check_response(self, response) -> bool:
    method check_health (line 1255) | def check_health(self) -> None:
    method _normalize_keys (line 1267) | def _normalize_keys(self, data) -> Dict:
    method psubscribe (line 1277) | def psubscribe(self, *args, **kwargs):
    method punsubscribe (line 1303) | def punsubscribe(self, *args):
    method subscribe (line 1316) | def subscribe(self, *args, **kwargs):
    method unsubscribe (line 1342) | def unsubscribe(self, *args):
    method ssubscribe (line 1355) | def ssubscribe(self, *args, target_node=None, **kwargs):
    method sunsubscribe (line 1381) | def sunsubscribe(self, *args, target_node=None):
    method listen (line 1394) | def listen(self):
    method get_message (line 1401) | def get_message(
    method ping (line 1433) | def ping(self, message: Union[str, None] = None) -> bool:
    method handle_message (line 1443) | def handle_message(self, response, ignore_subscribe_messages=False):
    method run_in_thread (line 1532) | def run_in_thread(
  class PubSubWorkerThread (line 1564) | class PubSubWorkerThread(threading.Thread):
    method __init__ (line 1565) | def __init__(
    method run (line 1583) | def run(self) -> None:
    method stop (line 1605) | def stop(self) -> None:
  class Pipeline (line 1612) | class Pipeline(Redis):
    method __init__ (line 1634) | def __init__(
    method __enter__ (line 1651) | def __enter__(self) -> "Pipeline":
    method __exit__ (line 1654) | def __exit__(self, exc_type, exc_value, traceback):
    method __del__ (line 1657) | def __del__(self):
    method __len__ (line 1663) | def __len__(self) -> int:
    method __bool__ (line 1666) | def __bool__(self) -> bool:
    method reset (line 1670) | def reset(self) -> None:
    method close (line 1694) | def close(self) -> None:
    method multi (line 1698) | def multi(self) -> None:
    method execute_command (line 1711) | def execute_command(self, *args, **kwargs):
    method _disconnect_reset_raise_on_watching (line 1716) | def _disconnect_reset_raise_on_watching(
    method immediate_execute_command (line 1755) | def immediate_execute_command(self, *args, **options):
    method pipeline_execute_command (line 1812) | def pipeline_execute_command(self, *args, **options) -> "Pipeline":
    method _execute_transaction (line 1827) | def _execute_transaction(
    method _execute_pipeline (line 1899) | def _execute_pipeline(self, connection, commands, raise_on_error):
    method raise_first_error (line 1916) | def raise_first_error(self, commands, response):
    method annotate_exception (line 1922) | def annotate_exception(self, exception, number, command):
    method parse_response (line 1930) | def parse_response(self, connection, command_name, **options):
    method load_scripts (line 1938) | def load_scripts(self):
    method _disconnect_raise_on_watching (line 1951) | def _disconnect_raise_on_watching(
    method execute (line 1987) | def execute(self, raise_on_error: bool = True) -> List[Any]:
    method discard (line 2053) | def discard(self):
    method watch (line 2060) | def watch(self, *names):
    method unwatch (line 2066) | def unwatch(self) -> bool:

FILE: redis/cluster.py
  function is_debug_log_enabled (line 90) | def is_debug_log_enabled():
  function get_node_name (line 94) | def get_node_name(host: str, port: Union[str, int]) -> str:
  function get_connection (line 103) | def get_connection(redis_node: Redis, *args, **options) -> Connection:
  function parse_scan_result (line 107) | def parse_scan_result(command, res, **options):
  function parse_pubsub_numsub (line 118) | def parse_pubsub_numsub(command, res, **options):
  function parse_cluster_slots (line 131) | def parse_cluster_slots(
  function parse_cluster_shards (line 151) | def parse_cluster_shards(resp, **options):
  function parse_cluster_myshardid (line 173) | def parse_cluster_myshardid(resp, **options):
  function cleanup_kwargs (line 231) | def cleanup_kwargs(**kwargs):
  class MaintNotificationsAbstractRedisCluster (line 244) | class MaintNotificationsAbstractRedisCluster:
    method __init__ (line 254) | def __init__(
    method _update_connection_kwargs_for_maint_notifications (line 294) | def _update_connection_kwargs_for_maint_notifications(
  class AbstractRedisCluster (line 307) | class AbstractRedisCluster:
    method replace_default_node (line 529) | def replace_default_node(self, target_node: "ClusterNode" = None) -> N...
  class RedisCluster (line 554) | class RedisCluster(
    method from_url (line 561) | def from_url(cls, url: str, **kwargs: Any) -> "RedisCluster":
    method __init__ (line 616) | def __init__(
    method __enter__ (line 874) | def __enter__(self):
    method __exit__ (line 877) | def __exit__(self, exc_type, exc_value, traceback):
    method __del__ (line 880) | def __del__(self):
    method disconnect_connection_pools (line 886) | def disconnect_connection_pools(self):
    method on_connect (line 895) | def on_connect(self, connection):
    method get_redis_connection (line 915) | def get_redis_connection(self, node: "ClusterNode") -> Redis:
    method get_node (line 922) | def get_node(self, host=None, port=None, node_name=None):
    method get_primaries (line 925) | def get_primaries(self):
    method get_replicas (line 928) | def get_replicas(self):
    method get_random_node (line 931) | def get_random_node(self):
    method get_random_primary_or_all_nodes (line 934) | def get_random_primary_or_all_nodes(self, command_name):
    method get_nodes (line 943) | def get_nodes(self):
    method get_node_from_key (line 946) | def get_node_from_key(self, key, replica=False):
    method get_default_node (line 966) | def get_default_node(self):
    method get_nodes_from_slot (line 972) | def get_nodes_from_slot(self, command: str, *args):
    method _split_multi_shard_command (line 985) | def _split_multi_shard_command(self, *args, **kwargs) -> list[dict]:
    method get_special_nodes (line 1002) | def get_special_nodes(self) -> Optional[list["ClusterNode"]]:
    method get_random_primary_node (line 1013) | def get_random_primary_node(self) -> "ClusterNode":
    method _evaluate_all_succeeded (line 1019) | def _evaluate_all_succeeded(self, res):
    method set_default_node (line 1043) | def set_default_node(self, node):
    method set_retry (line 1054) | def set_retry(self, retry: Retry) -> None:
    method monitor (line 1057) | def monitor(self, target_node=None):
    method pubsub (line 1074) | def pubsub(self, node=None, host=None, port=None, **kwargs):
    method pipeline (line 1081) | def pipeline(self, transaction=None, shard_hint=None):
    method lock (line 1108) | def lock(
    method set_response_callback (line 1189) | def set_response_callback(self, command, callback):
    method _determine_nodes (line 1193) | def _determine_nodes(
    method _should_reinitialized (line 1230) | def _should_reinitialized(self):
    method keyslot (line 1240) | def keyslot(self, key):
    method _get_command_keys (line 1248) | def _get_command_keys(self, *args):
    method determine_slot (line 1263) | def determine_slot(self, *args) -> Optional[int]:
    method get_encoder (line 1326) | def get_encoder(self):
    method get_connection_kwargs (line 1332) | def get_connection_kwargs(self):
    method _is_nodes_flag (line 1338) | def _is_nodes_flag(self, target_nodes):
    method _parse_target_nodes (line 1341) | def _parse_target_nodes(self, target_nodes):
    method execute_command (line 1361) | def execute_command(self, *args, **kwargs):
    method _internal_execute_command (line 1364) | def _internal_execute_command(self, *args, **kwargs):
    method _execute_command (line 1505) | def _execute_command(self, target_node, *args, **kwargs):
    method _record_command_metric (line 1777) | def _record_command_metric(
    method _record_error_metric (line 1800) | def _record_error_metric(
    method _extracts_socket_address (line 1820) | def _extracts_socket_address(
    method close (line 1834) | def close(self) -> None:
    method _process_result (line 1843) | def _process_result(self, command, res, response_policy: ResponsePolic...
    method load_external_module (line 1863) | def load_external_module(self, funcname, func):
    method transaction (line 1873) | def transaction(self, func, *watches, **kwargs):
  class ClusterNode (line 1896) | class ClusterNode:
    method __init__ (line 1897) | def __init__(self, host, port, server_type=None, redis_connection=None):
    method __repr__ (line 1907) | def __repr__(self):
    method __eq__ (line 1916) | def __eq__(self, obj):
    method __hash__ (line 1919) | def __hash__(self):
  class LoadBalancingStrategy (line 1923) | class LoadBalancingStrategy(Enum):
  class LoadBalancer (line 1929) | class LoadBalancer:
    method __init__ (line 1934) | def __init__(self, start_index: int = 0) -> None:
    method get_server_index (line 1939) | def get_server_index(
    method reset (line 1954) | def reset(self) -> None:
    method _get_random_replica_index (line 1958) | def _get_random_replica_index(self, list_size: int) -> int:
    method _get_round_robin_index (line 1961) | def _get_round_robin_index(
  class NodesManager (line 1974) | class NodesManager:
    method __init__ (line 1975) | def __init__(
    method get_node (line 2033) | def get_node(
    method move_slot (line 2056) | def move_slot(self, e: Union[AskError, MovedError]):
    method get_node_from_slot (line 2104) | def get_node_from_slot(
    method get_nodes_by_server_type (line 2145) | def get_nodes_by_server_type(self, server_type: Literal["primary", "re...
    method populate_startup_nodes (line 2162) | def populate_startup_nodes(self, nodes):
    method move_node_to_end_of_cached_nodes (line 2170) | def move_node_to_end_of_cached_nodes(self, node_name: str) -> None:
    method check_slots_coverage (line 2187) | def check_slots_coverage(self, slots_cache):
    method create_redis_connections (line 2195) | def create_redis_connections(self, nodes):
    method create_redis_node (line 2216) | def create_redis_node(
    method _get_or_create_cluster_node (line 2252) | def _get_or_create_cluster_node(self, host, port, role, tmp_nodes_cache):
    method _get_epoch (line 2273) | def _get_epoch(self) -> int:
    method initialize (line 2281) | def initialize(
    method close (line 2487) | def close(self) -> None:
    method reset (line 2495) | def reset(self):
    method remap_host_port (line 2502) | def remap_host_port(self, host: str, port: int) -> Tuple[str, int]:
    method find_connection_owner (line 2512) | def find_connection_owner(self, connection: Connection) -> Optional[Cl...
  class ClusterPubSub (line 2525) | class ClusterPubSub(PubSub):
    method __init__ (line 2534) | def __init__(
    method set_pubsub_node (line 2579) | def set_pubsub_node(self, cluster, node=None, host=None, port=None):
    method get_pubsub_node (line 2612) | def get_pubsub_node(self):
    method _raise_on_invalid_node (line 2618) | def _raise_on_invalid_node(self, redis_cluster, node, host, port):
    method execute_command (line 2628) | def execute_command(self, *args):
    method _get_node_pubsub (line 2670) | def _get_node_pubsub(self, node):
    method _sharded_message_generator (line 2680) | def _sharded_message_generator(self, timeout=0.0):
    method _pubsubs_generator (line 2692) | def _pubsubs_generator(self):
    method get_sharded_message (line 2697) | def get_sharded_message(
    method ssubscribe (line 2723) | def ssubscribe(self, *args, **kwargs):
    method sunsubscribe (line 2743) | def sunsubscribe(self, *args):
    method get_redis_connection (line 2757) | def get_redis_connection(self):
    method disconnect (line 2764) | def disconnect(self):
  class ClusterPipeline (line 2774) | class ClusterPipeline(RedisCluster):
    method __init__ (line 2799) | def __init__(
    method __repr__ (line 2889) | def __repr__(self):
    method __enter__ (line 2893) | def __enter__(self):
    method __exit__ (line 2897) | def __exit__(self, exc_type, exc_value, traceback):
    method __del__ (line 2901) | def __del__(self):
    method __len__ (line 2907) | def __len__(self):
    method __bool__ (line 2911) | def __bool__(self):
    method execute_command (line 2915) | def execute_command(self, *args, **kwargs):
    method pipeline_execute_command (line 2921) | def pipeline_execute_command(self, *args, **options):
    method annotate_exception (line 2935) | def annotate_exception(self, exception, number, command):
    method execute (line 2941) | def execute(self, raise_on_error: bool = True) -> List[Any]:
    method reset (line 2951) | def reset(self):
    method send_cluster_commands (line 2957) | def send_cluster_commands(
    method exists (line 2964) | def exists(self, *keys):
    method eval (line 2967) | def eval(self):
    method multi (line 2971) | def multi(self):
    method load_scripts (line 2978) | def load_scripts(self):
    method discard (line 2982) | def discard(self):
    method watch (line 2986) | def watch(self, *names):
    method unwatch (line 2990) | def unwatch(self):
    method script_load_for_pipeline (line 2994) | def script_load_for_pipeline(self, *args, **kwargs):
    method delete (line 2997) | def delete(self, *names):
    method unlink (line 3000) | def unlink(self, *names):
  function block_pipeline_command (line 3004) | def block_pipeline_command(name: str) -> Callable[..., Any]:
  class PipelineCommand (line 3098) | class PipelineCommand:
    method __init__ (line 3101) | def __init__(self, args, options=None, position=None):
  class NodeCommands (line 3113) | class NodeCommands:
    method __init__ (line 3116) | def __init__(
    method append (line 3125) | def append(self, c):
    method write (line 3129) | def write(self):
    method read (line 3151) | def read(self):
  class ExecutionStrategy (line 3184) | class ExecutionStrategy(ABC):
    method command_queue (line 3187) | def command_queue(self):
    method execute_command (line 3191) | def execute_command(self, *args, **kwargs):
    method annotate_exception (line 3200) | def annotate_exception(self, exception, number, command):
    method pipeline_execute_command (line 3209) | def pipeline_execute_command(self, *args, **options):
    method execute (line 3218) | def execute(self, raise_on_error: bool = True) -> List[Any]:
    method send_cluster_commands (line 3227) | def send_cluster_commands(
    method reset (line 3238) | def reset(self):
    method exists (line 3247) | def exists(self, *keys):
    method eval (line 3251) | def eval(self):
    method multi (line 3255) | def multi(self):
    method load_scripts (line 3264) | def load_scripts(self):
    method watch (line 3268) | def watch(self, *names):
    method unwatch (line 3272) | def unwatch(self):
    method script_load_for_pipeline (line 3281) | def script_load_for_pipeline(self, *args, **kwargs):
    method delete (line 3285) | def delete(self, *names):
    method unlink (line 3294) | def unlink(self, *names):
    method discard (line 3303) | def discard(self):
  class AbstractStrategy (line 3307) | class AbstractStrategy(ExecutionStrategy):
    method __init__ (line 3308) | def __init__(
    method command_queue (line 3317) | def command_queue(self):
    method command_queue (line 3321) | def command_queue(self, queue: List[PipelineCommand]):
    method execute_command (line 3325) | def execute_command(self, *args, **kwargs):
    method pipeline_execute_command (line 3328) | def pipeline_execute_command(self, *args, **options):
    method execute (line 3335) | def execute(self, raise_on_error: bool = True) -> List[Any]:
    method send_cluster_commands (line 3339) | def send_cluster_commands(
    method reset (line 3345) | def reset(self):
    method exists (line 3348) | def exists(self, *keys):
    method eval (line 3351) | def eval(self):
    method load_scripts (line 3355) | def load_scripts(self):
    method script_load_for_pipeline (line 3359) | def script_load_for_pipeline(self, *args, **kwargs):
    method annotate_exception (line 3365) | def annotate_exception(self, exception, number, command):
  class PipelineStrategy (line 3377) | class PipelineStrategy(AbstractStrategy):
    method __init__ (line 3378) | def __init__(self, pipe: ClusterPipeline):
    method execute_command (line 3382) | def execute_command(self, *args, **kwargs):
    method _raise_first_error (line 3385) | def _raise_first_error(self, stack, start_time):
    method execute (line 3402) | def execute(self, raise_on_error: bool = True) -> List[Any]:
    method reset (line 3412) | def reset(self):
    method send_cluster_commands (line 3418) | def send_cluster_commands(
    method _send_cluster_commands (line 3455) | def _send_cluster_commands(
    method _is_nodes_flag (line 3697) | def _is_nodes_flag(self, target_nodes):
    method _parse_target_nodes (line 3700) | def _parse_target_nodes(self, target_nodes):
    method _determine_nodes (line 3720) | def _determine_nodes(
    method multi (line 3759) | def multi(self):
    method discard (line 3764) | def discard(self):
    method watch (line 3769) | def watch(self, *names):
    method unwatch (line 3774) | def unwatch(self, *names):
    method delete (line 3779) | def delete(self, *names):
    method unlink (line 3787) | def unlink(self, *names):
  class TransactionStrategy (line 3796) | class TransactionStrategy(AbstractStrategy):
    method __init__ (line 3808) | def __init__(self, pipe: ClusterPipeline):
    method _get_client_and_connection_for_transaction (line 3820) | def _get_client_and_connection_for_transaction(self) -> Tuple[Redis, C...
    method execute_command (line 3853) | def execute_command(self, *args, **kwargs):
    method _validate_watch (line 3884) | def _validate_watch(self):
    method _immediate_execute_command (line 3890) | def _immediate_execute_command(self, *args, **options):
    method _get_connection_and_send_command (line 3897) | def _get_connection_and_send_command(self, *args, **options):
    method _send_command_parse_response (line 3931) | def _send_command_parse_response(
    method _reinitialize_on_error (line 3945) | def _reinitialize_on_error(self, error, failure_count):
    method _raise_first_error (line 3993) | def _raise_first_error(self, responses, stack, start_time):
    method execute (line 4011) | def execute(self, raise_on_error: bool = True) -> List[Any]:
    method _execute_transaction_with_retries (line 4018) | def _execute_transaction_with_retries(
    method _execute_transaction (line 4029) | def _execute_transaction(
    method reset (line 4142) | def reset(self):
    method send_cluster_commands (line 4183) | def send_cluster_commands(
    method multi (line 4190) | def multi(self):
    method watch (line 4199) | def watch(self, *names):
    method unwatch (line 4205) | def unwatch(self):
    method discard (line 4211) | def discard(self):
    method delete (line 4214) | def delete(self, *names):
    method unlink (line 4217) | def unlink(self, *names):

FILE: redis/commands/bf/__init__.py
  class AbstractBloom (line 8) | class AbstractBloom:
    method append_items (line 21) | def append_items(params, items):
    method append_error (line 27) | def append_error(params, error):
    method append_capacity (line 33) | def append_capacity(params, capacity):
    method append_expansion (line 39) | def append_expansion(params, expansion):
    method append_no_scale (line 45) | def append_no_scale(params, noScale):
    method append_weights (line 51) | def append_weights(params, weights):
    method append_no_create (line 58) | def append_no_create(params, noCreate):
    method append_items_and_increments (line 64) | def append_items_and_increments(params, items, increments):
    method append_values_and_weights (line 71) | def append_values_and_weights(params, items, weights):
    method append_max_iterations (line 78) | def append_max_iterations(params, max_iterations):
    method append_bucket_size (line 84) | def append_bucket_size(params, bucket_size):
  class CMSBloom (line 90) | class CMSBloom(CMSCommands, AbstractBloom):
    method __init__ (line 91) | def __init__(self, client, **kwargs):
  class TOPKBloom (line 120) | class TOPKBloom(TOPKCommands, AbstractBloom):
    method __init__ (line 121) | def __init__(self, client, **kwargs):
  class CFBloom (line 151) | class CFBloom(CFCommands, AbstractBloom):
    method __init__ (line 152) | def __init__(self, client, **kwargs):
  class TDigestBloom (line 186) | class TDigestBloom(TDigestCommands, AbstractBloom):
    method __init__ (line 187) | def __init__(self, client, **kwargs):
  class BFBloom (line 222) | class BFBloom(BFCommands, AbstractBloom):
    method __init__ (line 223) | def __init__(self, client, **kwargs):

FILE: redis/commands/bf/commands.py
  class BFCommands (line 59) | class BFCommands:
    method create (line 62) | def create(self, key, errorRate, capacity, expansion=None, noScale=None):
    method add (line 76) | def add(self, key, item):
    method madd (line 83) | def madd(self, key, *items):
    method insert (line 90) | def insert(
    method exists (line 118) | def exists(self, key, item):
    method mexists (line 125) | def mexists(self, key, *items):
    method scandump (line 132) | def scandump(self, key, iter):
    method loadchunk (line 146) | def loadchunk(self, key, iter, data):
    method info (line 157) | def info(self, key):
    method card (line 164) | def card(self, key):
  class CFCommands (line 173) | class CFCommands:
    method create (line 176) | def create(
    method add (line 191) | def add(self, key, item):
    method addnx (line 198) | def addnx(self, key, item):
    method insert (line 206) | def insert(self, key, items, capacity=None, nocreate=None):
    method insertnx (line 219) | def insertnx(self, key, items, capacity=None, nocreate=None):
    method exists (line 232) | def exists(self, key, item):
    method mexists (line 239) | def mexists(self, key, *items):
    method delete (line 246) | def delete(self, key, item):
    method count (line 253) | def count(self, key, item):
    method scandump (line 260) | def scandump(self, key, iter):
    method loadchunk (line 273) | def loadchunk(self, key, iter, data):
    method info (line 283) | def info(self, key):
  class TOPKCommands (line 292) | class TOPKCommands:
    method reserve (line 295) | def reserve(self, key, k, width, depth, decay):
    method add (line 303) | def add(self, key, *items):
    method incrby (line 310) | def incrby(self, key, items, increments):
    method query (line 324) | def query(self, key, *items):
    method count (line 332) | def count(self, key, *items):
    method list (line 339) | def list(self, key, withcount=False):
    method info (line 351) | def info(self, key):
  class TDigestCommands (line 359) | class TDigestCommands:
    method create (line 360) | def create(self, key, compression=100):
    method reset (line 367) | def reset(self, key):
    method add (line 374) | def add(self, key, values):
    method merge (line 382) | def merge(self, destination_key, num_keys, *keys, compression=None, ov...
    method min (line 399) | def min(self, key):
    method max (line 406) | def max(self, key):
    method quantile (line 413) | def quantile(self, key, quantile, *quantiles):
    method cdf (line 422) | def cdf(self, key, value, *values):
    method info (line 429) | def info(self, key):
    method trimmed_mean (line 437) | def trimmed_mean(self, key, low_cut_quantile, high_cut_quantile):
    method rank (line 447) | def rank(self, key, value, *values):
    method revrank (line 456) | def revrank(self, key, value, *values):
    method byrank (line 465) | def byrank(self, key, rank, *ranks):
    method byrevrank (line 473) | def byrevrank(self, key, rank, *ranks):
  class CMSCommands (line 482) | class CMSCommands:
    method initbydim (line 485) | def initbydim(self, key, width, depth):
    method initbyprob (line 492) | def initbyprob(self, key, error, probability):
    method incrby (line 499) | def incrby(self, key, items, increments):
    method query (line 513) | def query(self, key, *items):
    method merge (line 520) | def merge(self, destKey, numKeys, srcKeys, weights=[]):
    method info (line 533) | def info(self, key):

FILE: redis/commands/bf/info.py
  class BFInfo (line 4) | class BFInfo:
    method __init__ (line 11) | def __init__(self, args):
    method get (line 19) | def get(self, item):
    method __getitem__ (line 25) | def __getitem__(self, item):
  class CFInfo (line 29) | class CFInfo:
    method __init__ (line 39) | def __init__(self, args):
    method get (line 50) | def get(self, item):
    method __getitem__ (line 56) | def __getitem__(self, item):
  class CMSInfo (line 60) | class CMSInfo:
    method __init__ (line 65) | def __init__(self, args):
    method __getitem__ (line 71) | def __getitem__(self, item):
  class TopKInfo (line 75) | class TopKInfo:
    method __init__ (line 81) | def __init__(self, args):
    method __getitem__ (line 88) | def __getitem__(self, item):
  class TDigestInfo (line 92) | class TDigestInfo:
    method __init__ (line 102) | def __init__(self, args):
    method get (line 113) | def get(self, item):
    method __getitem__ (line 119) | def __getitem__(self, item):

FILE: redis/commands/cluster.py
  class ClusterMultiKeyCommands (line 167) | class ClusterMultiKeyCommands(ClusterCommandsProtocol):
    method _partition_keys_by_slot (line 172) | def _partition_keys_by_slot(self, keys: Iterable[KeyT]) -> Dict[int, L...
    method _partition_pairs_by_slot (line 182) | def _partition_pairs_by_slot(
    method _execute_pipeline_by_slot (line 194) | def _execute_pipeline_by_slot(
    method _reorder_keys_by_command (line 211) | def _reorder_keys_by_command(
    method mget_nonatomic (line 224) | def mget_nonatomic(self, keys: KeysT, *args: KeyT) -> List[Optional[An...
    method mset_nonatomic (line 247) | def mset_nonatomic(self, mapping: Mapping[AnyKeyT, EncodableT]) -> Lis...
    method _split_command_across_slots (line 266) | def _split_command_across_slots(self, command: str, *keys: KeyT) -> int:
    method exists (line 278) | def exists(self, *keys: KeyT) -> ResponseT:
    method delete (line 288) | def delete(self, *keys: KeyT) -> ResponseT:
    method touch (line 301) | def touch(self, *keys: KeyT) -> ResponseT:
    method unlink (line 316) | def unlink(self, *keys: KeyT) -> ResponseT:
  class AsyncClusterMultiKeyCommands (line 331) | class AsyncClusterMultiKeyCommands(ClusterMultiKeyCommands):
    method mget_nonatomic (line 336) | async def mget_nonatomic(self, keys: KeysT, *args: KeyT) -> List[Optio...
    method mset_nonatomic (line 359) | async def mset_nonatomic(self, mapping: Mapping[AnyKeyT, EncodableT]) ...
    method _split_command_across_slots (line 378) | async def _split_command_across_slots(self, command: str, *keys: KeyT)...
    method _execute_pipeline_by_slot (line 390) | async def _execute_pipeline_by_slot(
  class ClusterManagementCommands (line 410) | class ClusterManagementCommands(ManagementCommands):
    method slaveof (line 418) | def slaveof(self, *args, **kwargs) -> NoReturn:
    method replicaof (line 426) | def replicaof(self, *args, **kwargs) -> NoReturn:
    method swapdb (line 434) | def swapdb(self, *args, **kwargs) -> NoReturn:
    method cluster_myid (line 442) | def cluster_myid(self, target_node: "TargetNodesT") -> ResponseT:
    method cluster_addslots (line 453) | def cluster_addslots(
    method cluster_addslotsrange (line 468) | def cluster_addslotsrange(
    method cluster_countkeysinslot (line 486) | def cluster_countkeysinslot(self, slot_id: int) -> ResponseT:
    method cluster_count_failure_report (line 495) | def cluster_count_failure_report(self, node_id: str) -> ResponseT:
    method cluster_delslots (line 504) | def cluster_delslots(self, *slots: EncodableT) -> List[bool]:
    method cluster_delslotsrange (line 515) | def cluster_delslotsrange(self, *slots: EncodableT) -> ResponseT:
    method cluster_failover (line 526) | def cluster_failover(
    method cluster_info (line 550) | def cluster_info(self, target_nodes: Optional["TargetNodesT"] = None) ...
    method cluster_keyslot (line 560) | def cluster_keyslot(self, key: str) -> ResponseT:
    method cluster_meet (line 569) | def cluster_meet(
    method cluster_nodes (line 582) | def cluster_nodes(self) -> ResponseT:
    method cluster_replicate (line 591) | def cluster_replicate(
    method cluster_reset (line 603) | def cluster_reset(
    method cluster_save_config (line 618) | def cluster_save_config(
    method cluster_get_keys_in_slot (line 628) | def cluster_get_keys_in_slot(self, slot: int, num_keys: int) -> Respon...
    method cluster_set_config_epoch (line 636) | def cluster_set_config_epoch(
    method cluster_setslot (line 648) | def cluster_setslot(
    method cluster_setslot_stable (line 668) | def cluster_setslot_stable(self, slot_id: int) -> ResponseT:
    method cluster_replicas (line 677) | def cluster_replicas(
    method cluster_slots (line 690) | def cluster_slots(self, target_nodes: Optional["TargetNodesT"] = None)...
    method cluster_shards (line 698) | def cluster_shards(self, target_nodes=None):
    method cluster_myshardid (line 706) | def cluster_myshardid(self, target_nodes=None):
    method cluster_links (line 714) | def cluster_links(self, target_node: "TargetNodesT") -> ResponseT:
    method cluster_flushslots (line 726) | def cluster_flushslots(self, target_nodes: Optional["TargetNodesT"] = ...
    method cluster_bumpepoch (line 731) | def cluster_bumpepoch(self, target_nodes: Optional["TargetNodesT"] = N...
    method readonly (line 736) | def readonly(self, target_nodes: Optional["TargetNodesT"] = None) -> R...
    method readwrite (line 750) | def readwrite(self, target_nodes: Optional["TargetNodesT"] = None) -> ...
    method client_tracking_on (line 766) | def client_tracking_on(
    method client_tracking_off (line 801) | def client_tracking_off(
    method hotkeys_start (line 832) | def hotkeys_start(
    method hotkeys_stop (line 850) | def hotkeys_stop(self, **kwargs) -> Union[str, bytes]:
    method hotkeys_reset (line 860) | def hotkeys_reset(self, **kwargs) -> Union[str, bytes]:
    method hotkeys_get (line 870) | def hotkeys_get(self, **kwargs) -> list[dict[Union[str, bytes], Any]]:
  class AsyncClusterManagementCommands (line 881) | class AsyncClusterManagementCommands(
    method cluster_delslots (line 891) | async def cluster_delslots(self, *slots: EncodableT) -> List[bool]:
    method client_tracking_on (line 911) | async def client_tracking_on(
    method client_tracking_off (line 946) | async def client_tracking_off(
    method hotkeys_start (line 977) | async def hotkeys_start(
    method hotkeys_stop (line 995) | async def hotkeys_stop(self, **kwargs) -> Awaitable[Union[str, bytes]]:
    method hotkeys_reset (line 1005) | async def hotkeys_reset(self, **kwargs) -> Awaitable[Union[str, bytes]]:
    method hotkeys_get (line 1015) | async def hotkeys_get(
  class ClusterDataAccessCommands (line 1028) | class ClusterDataAccessCommands(DataAccessCommands):
    method stralgo (line 1036) | def stralgo(
    method scan_iter (line 1083) | def scan_iter(
  class AsyncClusterDataAccessCommands (line 1119) | class AsyncClusterDataAccessCommands(
    method scan_iter (line 1129) | async def scan_iter(
  class RedisClusterCommands (line 1167) | class RedisClusterCommands(
  class AsyncRedisClusterCommands (line 1198) | class AsyncRedisClusterCommands(

FILE: redis/commands/core.py
  class ACLCommands (line 101) | class ACLCommands(CommandsProtocol):
    method acl_cat (line 108) | def acl_cat(
    method acl_cat (line 113) | def acl_cat(
    method acl_cat (line 117) | def acl_cat(
    method acl_dryrun (line 133) | def acl_dryrun(
    method acl_dryrun (line 138) | def acl_dryrun(
    method acl_dryrun (line 142) | def acl_dryrun(self, username: str, *args: EncodableT, **kwargs) -> (
    method acl_deluser (line 153) | def acl_deluser(self: SyncClientProtocol, *username: str, **kwargs) ->...
    method acl_deluser (line 156) | def acl_deluser(
    method acl_deluser (line 160) | def acl_deluser(self, *username: str, **kwargs) -> int | Awaitable[int]:
    method acl_genpass (line 169) | def acl_genpass(
    method acl_genpass (line 174) | def acl_genpass(
    method acl_genpass (line 178) | def acl_genpass(self, bits: int | None = None, **kwargs) -> (
    method acl_getuser (line 200) | def acl_getuser(
    method acl_getuser (line 205) | def acl_getuser(
    method acl_getuser (line 209) | def acl_getuser(
    method acl_help (line 222) | def acl_help(self: SyncClientProtocol, **kwargs) -> list[bytes | str]:...
    method acl_help (line 225) | def acl_help(
    method acl_help (line 229) | def acl_help(self, **kwargs) -> list[bytes | str] | Awaitable[list[byt...
    method acl_list (line 238) | def acl_list(self: SyncClientProtocol, **kwargs) -> list[bytes | str]:...
    method acl_list (line 241) | def acl_list(
    method acl_list (line 245) | def acl_list(self, **kwargs) -> list[bytes | str] | Awaitable[list[byt...
    method acl_log (line 254) | def acl_log(
    method acl_log (line 259) | def acl_log(
    method acl_log (line 263) | def acl_log(
    method acl_log_reset (line 282) | def acl_log_reset(self: SyncClientProtocol, **kwargs) -> bool: ...
    method acl_log_reset (line 285) | def acl_log_reset(self: AsyncClientProtocol, **kwargs) -> Awaitable[bo...
    method acl_log_reset (line 287) | def acl_log_reset(self, **kwargs) -> bool | Awaitable[bool]:
    method acl_load (line 298) | def acl_load(self: SyncClientProtocol, **kwargs) -> bool: ...
    method acl_load (line 301) | def acl_load(self: AsyncClientProtocol, **kwargs) -> Awaitable[bool]: ...
    method acl_load (line 303) | def acl_load(self, **kwargs) -> bool | Awaitable[bool]:
    method acl_save (line 315) | def acl_save(self: SyncClientProtocol, **kwargs) -> bool: ...
    method acl_save (line 318) | def acl_save(self: AsyncClientProtocol, **kwargs) -> Awaitable[bool]: ...
    method acl_save (line 320) | def acl_save(self, **kwargs) -> bool | Awaitable[bool]:
    method acl_setuser (line 332) | def acl_setuser(
    method acl_setuser (line 352) | def acl_setuser(
    method acl_setuser (line 371) | def acl_setuser(
    method acl_users (line 563) | def acl_users(self: SyncClientProtocol, **kwargs) -> list[bytes | str]...
    method acl_users (line 566) | def acl_users(
    method acl_users (line 570) | def acl_users(self, **kwargs) -> list[bytes | str] | Awaitable[list[by...
    method acl_whoami (line 578) | def acl_whoami(self: SyncClientProtocol, **kwargs) -> bytes | str: ...
    method acl_whoami (line 581) | def acl_whoami(self: AsyncClientProtocol, **kwargs) -> Awaitable[bytes...
    method acl_whoami (line 583) | def acl_whoami(self, **kwargs) -> (bytes | str) | Awaitable[bytes | str]:
  class HotkeysMetricsTypes (line 594) | class HotkeysMetricsTypes(Enum):
  class ManagementCommands (line 599) | class ManagementCommands(CommandsProtocol):
    method auth (line 605) | def auth(
    method auth (line 613) | def auth(
    method auth (line 620) | def auth(
    method bgrewriteaof (line 636) | def bgrewriteaof(self: SyncClientProtocol, **kwargs) -> bool | bytes |...
    method bgrewriteaof (line 639) | def bgrewriteaof(
    method bgrewriteaof (line 643) | def bgrewriteaof(self, **kwargs) -> (bool | bytes | str) | Awaitable[
    method bgsave (line 653) | def bgsave(
    method bgsave (line 658) | def bgsave(
    method bgsave (line 662) | def bgsave(self, schedule: bool = True, **kwargs) -> (
    method role (line 677) | def role(self: SyncClientProtocol) -> list[Any]: ...
    method role (line 680) | def role(self: AsyncClientProtocol) -> Awaitable[list[Any]]: ...
    method role (line 682) | def role(self) -> list[Any] | Awaitable[list[Any]]:
    method client_kill (line 693) | def client_kill(self: SyncClientProtocol, address: str, **kwargs) -> b...
    method client_kill (line 696) | def client_kill(
    method client_kill (line 700) | def client_kill(self, address: str, **kwargs) -> (bool | int) | Awaita...
    method client_kill_filter (line 710) | def client_kill_filter(
    method client_kill_filter (line 723) | def client_kill_filter(
    method client_kill_filter (line 735) | def client_kill_filter(
    method client_info (line 790) | def client_info(self: SyncClientProtocol, **kwargs) -> dict[str, str |...
    method client_info (line 793) | def client_info(
    method client_info (line 797) | def client_info(
    method client_list (line 809) | def client_list(
    method client_list (line 817) | def client_list(
    method client_list (line 824) | def client_list(
    method client_getname (line 852) | def client_getname(self: SyncClientProtocol, **kwargs) -> bytes | str ...
    method client_getname (line 855) | def client_getname(
    method client_getname (line 859) | def client_getname(self, **kwargs) -> (bytes | str | None) | Awaitable[
    method client_getredir (line 870) | def client_getredir(self: SyncClientProtocol, **kwargs) -> int: ...
    method client_getredir (line 873) | def client_getredir(self: AsyncClientProtocol, **kwargs) -> Awaitable[...
    method client_getredir (line 875) | def client_getredir(self, **kwargs) -> int | Awaitable[int]:
    method client_reply (line 885) | def client_reply(
    method client_reply (line 892) | def client_reply(
    method client_reply (line 898) | def client_reply(self, reply: Literal["ON", "OFF", "SKIP"], **kwargs) ...
    method client_id (line 923) | def client_id(self: SyncClientProtocol, **kwargs) -> int: ...
    method client_id (line 926) | def client_id(self: AsyncClientProtocol, **kwargs) -> Awaitable[int]: ...
    method client_id (line 928) | def client_id(self, **kwargs) -> int | Awaitable[int]:
    method client_tracking_on (line 937) | def client_tracking_on(
    method client_tracking_on (line 948) | def client_tracking_on(
    method client_tracking_on (line 958) | def client_tracking_on(
    method client_tracking_off (line 978) | def client_tracking_off(
    method client_tracking_off (line 989) | def client_tracking_off(
    method client_tracking_off (line 999) | def client_tracking_off(
    method client_tracking (line 1019) | def client_tracking(
    method client_tracking (line 1032) | def client_tracking(
    method client_tracking (line 1044) | def client_tracking(
    method client_trackinginfo (line 1105) | def client_trackinginfo(
    method client_trackinginfo (line 1110) | def client_trackinginfo(
    method client_trackinginfo (line 1114) | def client_trackinginfo(
    method client_setname (line 1126) | def client_setname(self: SyncClientProtocol, name: str, **kwargs) -> b...
    method client_setname (line 1129) | def client_setname(
    method client_setname (line 1133) | def client_setname(self, name: str, **kwargs) -> bool | Awaitable[bool]:
    method client_setinfo (line 1148) | def client_setinfo(
    method client_setinfo (line 1153) | def client_setinfo(
    method client_setinfo (line 1157) | def client_setinfo(self, attr: str, value: str, **kwargs) -> bool | Aw...
    method client_unblock (line 1165) | def client_unblock(
    method client_unblock (line 1170) | def client_unblock(
    method client_unblock (line 1174) | def client_unblock(
    method client_pause (line 1191) | def client_pause(
    method client_pause (line 1196) | def client_pause(
    method client_pause (line 1200) | def client_pause(
    method client_unpause (line 1231) | def client_unpause(self: SyncClientProtocol, **kwargs) -> bytes | str:...
    method client_unpause (line 1234) | def client_unpause(
    method client_unpause (line 1238) | def client_unpause(self, **kwargs) -> (bytes | str) | Awaitable[bytes ...
    method client_no_evict (line 1247) | def client_no_evict(self: SyncClientProtocol, mode: str) -> bytes | st...
    method client_no_evict (line 1250) | def client_no_evict(
    method client_no_evict (line 1254) | def client_no_evict(self, mode: str) -> (bytes | str) | Awaitable[byte...
    method client_no_touch (line 1263) | def client_no_touch(self: SyncClientProtocol, mode: str) -> bytes | st...
    method client_no_touch (line 1266) | def client_no_touch(
    method client_no_touch (line 1270) | def client_no_touch(self, mode: str) -> (bytes | str) | Awaitable[byte...
    method command (line 1282) | def command(self: SyncClientProtocol, **kwargs) -> dict[str, dict[str,...
    method command (line 1285) | def command(
    method command (line 1289) | def command(
    method command_info (line 1299) | def command_info(self, **kwargs) -> None:
    method command_count (line 1305) | def command_count(self: SyncClientProtocol, **kwargs) -> int: ...
    method command_count (line 1308) | def command_count(self: AsyncClientProtocol, **kwargs) -> Awaitable[in...
    method command_count (line 1310) | def command_count(self, **kwargs) -> int | Awaitable[int]:
    method command_list (line 1314) | def command_list(
    method command_list (line 1322) | def command_list(
    method command_list (line 1329) | def command_list(
    method command_getkeysandflags (line 1358) | def command_getkeysandflags(
    method command_getkeysandflags (line 1363) | def command_getkeysandflags(
    method command_getkeysandflags (line 1367) | def command_getkeysandflags(
    method command_docs (line 1377) | def command_docs(self, *args):
    method config_get (line 1387) | def config_get(
    method config_get (line 1392) | def config_get(
    method config_get (line 1396) | def config_get(
    method config_set (line 1407) | def config_set(
    method config_set (line 1416) | def config_set(
    method config_set (line 1424) | def config_set(
    method config_resetstat (line 1438) | def config_resetstat(self: SyncClientProtocol, **kwargs) -> bool: ...
    method config_resetstat (line 1441) | def config_resetstat(self: AsyncClientProtocol, **kwargs) -> Awaitable...
    method config_resetstat (line 1443) | def config_resetstat(self, **kwargs) -> bool | Awaitable[bool]:
    method config_rewrite (line 1452) | def config_rewrite(self: SyncClientProtocol, **kwargs) -> bytes | str:...
    method config_rewrite (line 1455) | def config_rewrite(
    method config_rewrite (line 1459) | def config_rewrite(self, **kwargs) -> (bytes | str) | Awaitable[bytes ...
    method dbsize (line 1468) | def dbsize(self: SyncClientProtocol, **kwargs) -> int: ...
    method dbsize (line 1471) | def dbsize(self: AsyncClientProtocol, **kwargs) -> Awaitable[int]: ...
    method dbsize (line 1473) | def dbsize(self, **kwargs) -> int | Awaitable[int]:
    method debug_object (line 1482) | def debug_object(
    method debug_object (line 1487) | def debug_object(
    method debug_object (line 1491) | def debug_object(self, key: KeyT, **kwargs) -> (
    method debug_segfault (line 1501) | def debug_segfault(self, **kwargs) -> None:
    method echo (line 1511) | def echo(self: SyncClientProtocol, value: EncodableT, **kwargs) -> byt...
    method echo (line 1514) | def echo(
    method echo (line 1518) | def echo(self, value: EncodableT, **kwargs) -> (bytes | str) | Awaitable[
    method flushall (line 1529) | def flushall(
    method flushall (line 1534) | def flushall(
    method flushall (line 1538) | def flushall(self, asynchronous: bool = False, **kwargs) -> bool | Awa...
    method flushdb (line 1553) | def flushdb(
    method flushdb (line 1558) | def flushdb(
    method flushdb (line 1562) | def flushdb(self, asynchronous: bool = False, **kwargs) -> bool | Awai...
    method sync (line 1577) | def sync(self: SyncClientProtocol) -> bytes: ...
    method sync (line 1580) | def sync(self: AsyncClientProtocol) -> Awaitable[bytes]: ...
    method sync (line 1582) | def sync(self) -> bytes | Awaitable[bytes]:
    method psync (line 1595) | def psync(self: SyncClientProtocol, replicationid: str, offset: int) -...
    method psync (line 1598) | def psync(
    method psync (line 1602) | def psync(self, replicationid: str, offset: int) -> bytes | Awaitable[...
    method swapdb (line 1616) | def swapdb(self: SyncClientProtocol, first: int, second: int, **kwargs...
    method swapdb (line 1619) | def swapdb(
    method swapdb (line 1623) | def swapdb(self, first: int, second: int, **kwargs) -> bool | Awaitabl...
    method select (line 1632) | def select(self: SyncClientProtocol, index: int, **kwargs) -> bool: ...
    method select (line 1635) | def select(self: AsyncClientProtocol, index: int, **kwargs) -> Awaitab...
    method select (line 1637) | def select(self, index: int, **kwargs) -> bool | Awaitable[bool]:
    method info (line 1645) | def info(
    method info (line 1653) | def info(
    method info (line 1660) | def info(
    method lastsave (line 1680) | def lastsave(self: SyncClientProtocol, **kwargs) -> datetime.datetime:...
    method lastsave (line 1683) | def lastsave(
    method lastsave (line 1687) | def lastsave(self, **kwargs) -> datetime.datetime | Awaitable[datetime...
    method latency_doctor (line 1696) | def latency_doctor(self):
    method latency_graph (line 1710) | def latency_graph(self):
    method lolwut (line 1725) | def lolwut(
    method lolwut (line 1730) | def lolwut(
    method lolwut (line 1734) | def lolwut(self, *version_numbers: Union[str, float], **kwargs) -> (
    method reset (line 1748) | def reset(self: SyncClientProtocol) -> bytes | str: ...
    method reset (line 1751) | def reset(self: AsyncClientProtocol) -> Awaitable[bytes | str]: ...
    method reset (line 1753) | def reset(self) -> (bytes | str) | Awaitable[bytes | str]:
    method migrate (line 1761) | def migrate(
    method migrate (line 1775) | def migrate(
    method migrate (line 1788) | def migrate(
    method object (line 1837) | def object(self: SyncClientProtocol, infotype: str, key: KeyT, **kwarg...
    method object (line 1840) | def object(
    method object (line 1844) | def object(self, infotype: str, key: KeyT, **kwargs) -> Any | Awaitabl...
    method memory_doctor (line 1852) | def memory_doctor(self, **kwargs) -> None:
    method memory_help (line 1861) | def memory_help(self, **kwargs) -> None:
    method memory_stats (line 1871) | def memory_stats(self: SyncClientProtocol, **kwargs) -> dict[str, Any]...
    method memory_stats (line 1874) | def memory_stats(
    method memory_stats (line 1878) | def memory_stats(self, **kwargs) -> dict[str, Any] | Awaitable[dict[st...
    method memory_malloc_stats (line 1887) | def memory_malloc_stats(self: SyncClientProtocol, **kwargs) -> bytes |...
    method memory_malloc_stats (line 1890) | def memory_malloc_stats(
    method memory_malloc_stats (line 1894) | def memory_malloc_stats(self, **kwargs) -> (bytes | str) | Awaitable[b...
    method memory_usage (line 1903) | def memory_usage(
    method memory_usage (line 1908) | def memory_usage(
    method memory_usage (line 1912) | def memory_usage(self, key: KeyT, samples: int | None = None, **kwargs...
    method memory_purge (line 1931) | def memory_purge(self: SyncClientProtocol, **kwargs) -> bool: ...
    method memory_purge (line 1934) | def memory_purge(self: AsyncClientProtocol, **kwargs) -> Awaitable[boo...
    method memory_purge (line 1936) | def memory_purge(self, **kwargs) -> bool | Awaitable[bool]:
    method latency_histogram (line 1944) | def latency_histogram(self, *args):
    method latency_history (line 1954) | def latency_history(self: SyncClientProtocol, event: str) -> list[list...
    method latency_history (line 1957) | def latency_history(
    method latency_history (line 1961) | def latency_history(
    method latency_latest (line 1972) | def latency_latest(self: SyncClientProtocol) -> list[list[bytes | str ...
    method latency_latest (line 1975) | def latency_latest(
    method latency_latest (line 1979) | def latency_latest(
    method latency_reset (line 1990) | def latency_reset(self: SyncClientProtocol, *events: str) -> int: ...
    method latency_reset (line 1993) | def latency_reset(self: AsyncClientProtocol, *events: str) -> Awaitabl...
    method latency_reset (line 1995) | def latency_reset(self, *events: str) -> int | Awaitable[int]:
    method ping (line 2004) | def ping(self: SyncClientProtocol, **kwargs) -> bool: ...
    method ping (line 2007) | def ping(self: AsyncClientProtocol, **kwargs) -> Awaitable[bool]: ...
    method ping (line 2009) | def ping(self, **kwargs) -> Union[Awaitable[bool], bool]:
    method quit (line 2025) | def quit(self: SyncClientProtocol, **kwargs) -> bool: ...
    method quit (line 2028) | def quit(self: AsyncClientProtocol, **kwargs) -> Awaitable[bool]: ...
    method quit (line 2030) | def quit(self, **kwargs) -> bool | Awaitable[bool]:
    method replicaof (line 2039) | def replicaof(self: SyncClientProtocol, *args, **kwargs) -> bytes | st...
    method replicaof (line 2042) | def replicaof(
    method replicaof (line 2046) | def replicaof(self, *args, **kwargs) -> (bytes | str) | Awaitable[byte...
    method save (line 2060) | def save(self: SyncClientProtocol, **kwargs) -> bool: ...
    method save (line 2063) | def save(self: AsyncClientProtocol, **kwargs) -> Awaitable[bool]: ...
    method save (line 2065) | def save(self, **kwargs) -> bool | Awaitable[bool]:
    method shutdown (line 2074) | def shutdown(
    method slaveof (line 2117) | def slaveof(
    method slaveof (line 2125) | def slaveof(
    method slaveof (line 2132) | def slaveof(
    method slowlog_get (line 2147) | def slowlog_get(
    method slowlog_get (line 2152) | def slowlog_get(
    method slowlog_get (line 2156) | def slowlog_get(
    method slowlog_len (line 2176) | def slowlog_len(self: SyncClientProtocol, **kwargs) -> int: ...
    method slowlog_len (line 2179) | def slowlog_len(self: AsyncClientProtocol, **kwargs) -> Awaitable[int]...
    method slowlog_len (line 2181) | def slowlog_len(self, **kwargs) -> int | Awaitable[int]:
    method slowlog_reset (line 2190) | def slowlog_reset(self: SyncClientProtocol, **kwargs) -> bool: ...
    method slowlog_reset (line 2193) | def slowlog_reset(self: AsyncClientProtocol, **kwargs) -> Awaitable[bo...
    method slowlog_reset (line 2195) | def slowlog_reset(self, **kwargs) -> bool | Awaitable[bool]:
    method time (line 2204) | def time(self: SyncClientProtocol, **kwargs) -> tuple[int, int]: ...
    method time (line 2207) | def time(self: AsyncClientProtocol, **kwargs) -> Awaitable[tuple[int, ...
    method time (line 2209) | def time(self, **kwargs) -> tuple[int, int] | Awaitable[tuple[int, int]]:
    method wait (line 2219) | def wait(
    method wait (line 2224) | def wait(
    method wait (line 2228) | def wait(self, num_replicas: int, timeout: int, **kwargs) -> int | Awa...
    method waitaof (line 2240) | def waitaof(
    method waitaof (line 2249) | def waitaof(
    method waitaof (line 2257) | def waitaof(
    method hello (line 2272) | def hello(self):
    method failover (line 2281) | def failover(self):
    method hotkeys_start (line 2291) | def hotkeys_start(
    method hotkeys_start (line 2302) | def hotkeys_start(
    method hotkeys_start (line 2312) | def hotkeys_start(
    method hotkeys_stop (line 2361) | def hotkeys_stop(self: SyncClientProtocol, **kwargs) -> bytes | str: ...
    method hotkeys_stop (line 2364) | def hotkeys_stop(self: AsyncClientProtocol, **kwargs) -> Awaitable[byt...
    method hotkeys_stop (line 2366) | def hotkeys_stop(self, **kwargs) -> (bytes | str) | Awaitable[bytes | ...
    method hotkeys_reset (line 2376) | def hotkeys_reset(self: SyncClientProtocol, **kwargs) -> bytes | str: ...
    method hotkeys_reset (line 2379) | def hotkeys_reset(
    method hotkeys_reset (line 2383) | def hotkeys_reset(self, **kwargs) -> (bytes | str) | Awaitable[bytes |...
    method hotkeys_get (line 2393) | def hotkeys_get(
    method hotkeys_get (line 2398) | def hotkeys_get(
    method hotkeys_get (line 2402) | def hotkeys_get(
  class AsyncManagementCommands (line 2421) | class AsyncManagementCommands(ManagementCommands):
    method command_info (line 2422) | async def command_info(self, **kwargs) -> None:
    method debug_segfault (line 2425) | async def debug_segfault(self, **kwargs) -> None:
    method memory_doctor (line 2428) | async def memory_doctor(self, **kwargs) -> None:
    method memory_help (line 2431) | async def memory_help(self, **kwargs) -> None:
    method shutdown (line 2434) | async def shutdown(
  class BitFieldOperation (line 2472) | class BitFieldOperation:
    method __init__ (line 2477) | def __init__(
    method reset (line 2491) | def reset(self):
    method overflow (line 2499) | def overflow(self, overflow: str):
    method incrby (line 2512) | def incrby(
    method get (line 2538) | def get(self, fmt: str, offset: BitfieldOffsetT):
    method set (line 2551) | def set(self, fmt: str, offset: BitfieldOffsetT, value: int):
    method command (line 2566) | def command(self):
    method execute (line 2572) | def execute(self) -> ResponseT:
  class DataPersistOptions (line 2584) | class DataPersistOptions(Enum):
  class BasicKeyCommands (line 2594) | class BasicKeyCommands(CommandsProtocol):
    method append (line 2600) | def append(self: SyncClientProtocol, key: KeyT, value: EncodableT) -> ...
    method append (line 2603) | def append(
    method append (line 2607) | def append(self, key: KeyT, value: EncodableT) -> int | Awaitable[int]:
    method bitcount (line 2618) | def bitcount(
    method bitcount (line 2627) | def bitcount(
    method bitcount (line 2635) | def bitcount(
    method bitfield (line 2658) | def bitfield(
    method bitfield_ro (line 2672) | def bitfield_ro(
    method bitfield_ro (line 2681) | def bitfield_ro(
    method bitfield_ro (line 2689) | def bitfield_ro(
    method bitop (line 2713) | def bitop(
    method bitop (line 2718) | def bitop(
    method bitop (line 2722) | def bitop(self, operation: str, dest: KeyT, *keys: KeyT) -> int | Awai...
    method bitpos (line 2732) | def bitpos(
    method bitpos (line 2742) | def bitpos(
    method bitpos (line 2751) | def bitpos(
    method copy (line 2783) | def copy(
    method copy (line 2792) | def copy(
    method copy (line 2800) | def copy(
    method decrby (line 2827) | def decrby(self: SyncClientProtocol, name: KeyT, amount: int = 1) -> i...
    method decrby (line 2830) | def decrby(
    method decrby (line 2834) | def decrby(self, name: KeyT, amount: int = 1) -> int | Awaitable[int]:
    method delete (line 2846) | def delete(self: SyncClientProtocol, *names: KeyT) -> int: ...
    method delete (line 2849) | def delete(self: AsyncClientProtocol, *names: KeyT) -> Awaitable[int]:...
    method delete (line 2851) | def delete(self, *names: KeyT) -> int | Awaitable[int]:
    method __delitem__ (line 2857) | def __delitem__(self, name: KeyT):
    method delex (line 2861) | def delex(
    method delex (line 2871) | def delex(
    method delex (line 2881) | def delex(
    method dump (line 2932) | def dump(self: SyncClientProtocol, name: KeyT) -> bytes | None: ...
    method dump (line 2935) | def dump(self: AsyncClientProtocol, name: KeyT) -> Awaitable[bytes | N...
    method dump (line 2937) | def dump(self, name: KeyT) -> (bytes | None) | Awaitable[bytes | None]:
    method exists (line 2951) | def exists(self: SyncClientProtocol, *names: KeyT) -> int: ...
    method exists (line 2954) | def exists(self: AsyncClientProtocol, *names: KeyT) -> Awaitable[int]:...
    method exists (line 2956) | def exists(self, *names: KeyT) -> int | Awaitable[int]:
    method expire (line 2967) | def expire(
    method expire (line 2978) | def expire(
    method expire (line 2988) | def expire(
    method expireat (line 3026) | def expireat(
    method expireat (line 3037) | def expireat(
    method expireat (line 3047) | def expireat(
    method expiretime (line 3085) | def expiretime(self: SyncClientProtocol, key: str) -> int: ...
    method expiretime (line 3088) | def expiretime(self: AsyncClientProtocol, key: str) -> Awaitable[int]:...
    method expiretime (line 3090) | def expiretime(self, key: str) -> int | Awaitable[int]:
    method digest_local (line 3100) | def digest_local(self, value: bytes | str) -> bytes | str:
    method digest (line 3138) | def digest(self: SyncClientProtocol, name: KeyT) -> str | bytes | None...
    method digest (line 3141) | def digest(
    method digest (line 3146) | def digest(self, name: KeyT) -> (str | bytes | None) | Awaitable[
    method get (line 3174) | def get(self: SyncClientProtocol, name: KeyT) -> bytes | str | None: ...
    method get (line 3177) | def get(self: AsyncClientProtocol, name: KeyT) -> Awaitable[bytes | st...
    method get (line 3179) | def get(self, name: KeyT) -> (bytes | str | None) | Awaitable[bytes | ...
    method getdel (line 3188) | def getdel(self: SyncClientProtocol, name: KeyT) -> bytes | str | None...
    method getdel (line 3191) | def getdel(
    method getdel (line 3195) | def getdel(self, name: KeyT) -> (bytes | str | None) | Awaitable[
    method getex (line 3209) | def getex(
    method getex (line 3220) | def getex(
    method getex (line 3230) | def getex(
    method __getitem__ (line 3272) | def __getitem__(self, name: KeyT):
    method getbit (line 3283) | def getbit(self: SyncClientProtocol, name: KeyT, offset: int) -> int: ...
    method getbit (line 3286) | def getbit(
    method getbit (line 3290) | def getbit(self, name: KeyT, offset: int) -> int | Awaitable[int]:
    method getrange (line 3299) | def getrange(
    method getrange (line 3304) | def getrange(
    method getrange (line 3308) | def getrange(self, key: KeyT, start: int, end: int) -> (bytes | str) |...
    method getset (line 3320) | def getset(
    method getset (line 3325) | def getset(
    method getset (line 3329) | def getset(self, name: KeyT, value: EncodableT) -> (bytes | str | None...
    method incrby (line 3344) | def incrby(self: SyncClientProtocol, name: KeyT, amount: int = 1) -> i...
    method incrby (line 3347) | def incrby(
    method incrby (line 3351) | def incrby(self, name: KeyT, amount: int = 1) -> int | Awaitable[int]:
    method incrbyfloat (line 3363) | def incrbyfloat(
    method incrbyfloat (line 3368) | def incrbyfloat(
    method incrbyfloat (line 3372) | def incrbyfloat(self, name: KeyT, amount: float = 1.0) -> float | Awai...
    method keys (line 3382) | def keys(
    method keys (line 3387) | def keys(
    method keys (line 3391) | def keys(
    method lmove (line 3402) | def lmove(
    method lmove (line 3411) | def lmove(
    method lmove (line 3419) | def lmove(
    method blmove (line 3433) | def blmove(
    method blmove (line 3443) | def blmove(
    method blmove (line 3452) | def blmove(
    method mget (line 3469) | def mget(
    method mget (line 3474) | def mget(
    method mget (line 3478) | def mget(
    method mset (line 3500) | def mset(
    method mset (line 3505) | def mset(
    method mset (line 3509) | def mset(self, mapping: Mapping[AnyKeyT, EncodableT]) -> bool | Awaita...
    method msetex (line 3527) | def msetex(
    method msetex (line 3539) | def msetex(
    method msetex (line 3550) | def msetex(
    method msetnx (line 3616) | def msetnx(
    method msetnx (line 3621) | def msetnx(
    method msetnx (line 3625) | def msetnx(self, mapping: Mapping[AnyKeyT, EncodableT]) -> bool | Awai...
    method move (line 3644) | def move(self: SyncClientProtocol, name: KeyT, db: int) -> bool: ...
    method move (line 3647) | def move(self: AsyncClientProtocol, name: KeyT, db: int) -> Awaitable[...
    method move (line 3649) | def move(self, name: KeyT, db: int) -> bool | Awaitable[bool]:
    method persist (line 3658) | def persist(self: SyncClientProtocol, name: KeyT) -> bool: ...
    method persist (line 3661) | def persist(self: AsyncClientProtocol, name: KeyT) -> Awaitable[bool]:...
    method persist (line 3663) | def persist(self, name: KeyT) -> bool | Awaitable[bool]:
    method pexpire (line 3672) | def pexpire(
    method pexpire (line 3683) | def pexpire(
    method pexpire (line 3693) | def pexpire(
    method pexpireat (line 3730) | def pexpireat(
    method pexpireat (line 3741) | def pexpireat(
    method pexpireat (line 3751) | def pexpireat(
    method pexpiretime (line 3787) | def pexpiretime(self: SyncClientProtocol, key: str) -> int: ...
    method pexpiretime (line 3790) | def pexpiretime(self: AsyncClientProtocol, key: str) -> Awaitable[int]...
    method pexpiretime (line 3792) | def pexpiretime(self, key: str) -> int | Awaitable[int]:
    method psetex (line 3802) | def psetex(
    method psetex (line 3807) | def psetex(
    method psetex (line 3811) | def psetex(
    method pttl (line 3826) | def pttl(self: SyncClientProtocol, name: KeyT) -> int: ...
    method pttl (line 3829) | def pttl(self: AsyncClientProtocol, name: KeyT) -> Awaitable[int]: ...
    method pttl (line 3831) | def pttl(self, name: KeyT) -> int | Awaitable[int]:
    method hrandfield (line 3840) | def hrandfield(
    method hrandfield (line 3848) | def hrandfield(
    method hrandfield (line 3855) | def hrandfield(
    method randomkey (line 3882) | def randomkey(self: SyncClientProtocol, **kwargs) -> bytes | str | Non...
    method randomkey (line 3885) | def randomkey(
    method randomkey (line 3889) | def randomkey(self, **kwargs) -> (bytes | str | None) | Awaitable[
    method rename (line 3900) | def rename(self: SyncClientProtocol, src: KeyT, dst: KeyT) -> bool: ...
    method rename (line 3903) | def rename(self: AsyncClientProtocol, src: KeyT, dst: KeyT) -> Awaitab...
    method rename (line 3905) | def rename(self, src: KeyT, dst: KeyT) -> bool | Awaitable[bool]:
    method renamenx (line 3914) | def renamenx(self: SyncClientProtocol, src: KeyT, dst: KeyT) -> bool: ...
    method renamenx (line 3917) | def renamenx(
    method renamenx (line 3921) | def renamenx(self, src: KeyT, dst: KeyT) -> bool | Awaitable[bool]:
    method restore (line 3930) | def restore(
    method restore (line 3942) | def restore(
    method restore (line 3953) | def restore(
    method set (line 4004) | def set(
    method set (line 4023) | def set(
    method set (line 4042) | def set(
    method __setitem__ (line 4154) | def __setitem__(self, name: KeyT, value: EncodableT):
    method setbit (line 4158) | def setbit(
    method setbit (line 4163) | def setbit(
    method setbit (line 4167) | def setbit(self, name: KeyT, offset: int, value: int) -> int | Awaitab...
    method setex (line 4178) | def setex(
    method setex (line 4183) | def setex(
    method setex (line 4187) | def setex(
    method setnx (line 4202) | def setnx(self: SyncClientProtocol, name: KeyT, value: EncodableT) -> ...
    method setnx (line 4205) | def setnx(
    method setnx (line 4209) | def setnx(self, name: KeyT, value: EncodableT) -> bool | Awaitable[bool]:
    method setrange (line 4218) | def setrange(
    method setrange (line 4223) | def setrange(
    method setrange (line 4227) | def setrange(
    method stralgo (line 4245) | def stralgo(
    method stralgo (line 4259) | def stralgo(
    method stralgo (line 4272) | def stralgo(
    method strlen (line 4337) | def strlen(self: SyncClientProtocol, name: KeyT) -> int: ...
    method strlen (line 4340) | def strlen(self: AsyncClientProtocol, name: KeyT) -> Awaitable[int]: ...
    method strlen (line 4342) | def strlen(self, name: KeyT) -> int | Awaitable[int]:
    method substr (line 4351) | def substr(
    method substr (line 4356) | def substr(
    method substr (line 4360) | def substr(self, name: KeyT, start: int, end: int = -1) -> (
    method touch (line 4370) | def touch(self: SyncClientProtocol, *args: KeyT) -> int: ...
    method touch (line 4373) | def touch(self: AsyncClientProtocol, *args: KeyT) -> Awaitable[int]: ...
    method touch (line 4375) | def touch(self, *args: KeyT) -> int | Awaitable[int]:
    method ttl (line 4385) | def ttl(self: SyncClientProtocol, name: KeyT) -> int: ...
    method ttl (line 4388) | def ttl(self: AsyncClientProtocol, name: KeyT) -> Awaitable[int]: ...
    method ttl (line 4390) | def ttl(self, name: KeyT) -> int | Awaitable[int]:
    method type (line 4399) | def type(self: SyncClientProtocol, name: KeyT) -> bytes | str: ...
    method type (line 4402) | def type(self: AsyncClientProtocol, name: KeyT) -> Awaitable[bytes | s...
    method type (line 4404) | def type(self, name: KeyT) -> (bytes | str) | Awaitable[bytes | str]:
    method watch (line 4412) | def watch(self, *names: KeyT) -> None:
    method unwatch (line 4420) | def unwatch(self) -> None:
    method unlink (line 4429) | def unlink(self: SyncClientProtocol, *names: KeyT) -> int: ...
    method unlink (line 4432) | def unlink(self: AsyncClientProtocol, *names: KeyT) -> Awaitable[int]:...
    method unlink (line 4434) | def unlink(self, *names: KeyT) -> int | Awaitable[int]:
    method lcs (line 4443) | def lcs(
    method lcs (line 4454) | def lcs(
    method lcs (line 4464) | def lcs(
  class AsyncBasicKeyCommands (line 4496) | class AsyncBasicKeyCommands(BasicKeyCommands):
    method __delitem__ (line 4497) | def __delitem__(self, name: KeyT):
    method __contains__ (line 4500) | def __contains__(self, name: KeyT):
    method __getitem__ (line 4503) | def __getitem__(self, name: KeyT):
    method __setitem__ (line 4506) | def __setitem__(self, name: KeyT, value: EncodableT):
    method watch (line 4509) | async def watch(self, *names: KeyT) -> None:
    method unwatch (line 4512) | async def unwatch(self) -> None:
  class ListCommands (line 4516) | class ListCommands(CommandsProtocol):
    method blpop (line 4523) | def blpop(
    method blpop (line 4528) | def blpop(
    method blpop (line 4532) | def blpop(
    method brpop (line 4554) | def brpop(
    method brpop (line 4559) | def brpop(
    method brpop (line 4563) | def brpop(
    method brpoplpush (line 4585) | def brpoplpush(
    method brpoplpush (line 4590) | def brpoplpush(
    method brpoplpush (line 4594) | def brpoplpush(self, src: KeyT, dst: KeyT, timeout: Number | None = 0)...
    method blmpop (line 4612) | def blmpop(
    method blmpop (line 4622) | def blmpop(
    method blmpop (line 4631) | def blmpop(
    method lmpop (line 4653) | def lmpop(
    method lmpop (line 4662) | def lmpop(
    method lmpop (line 4670) | def lmpop(
    method lindex (line 4690) | def lindex(
    method lindex (line 4695) | def lindex(
    method lindex (line 4699) | def lindex(self, name: KeyT, index: int) -> (bytes | str | None) | Awa...
    method linsert (line 4713) | def linsert(
    method linsert (line 4718) | def linsert(
    method linsert (line 4722) | def linsert(
    method llen (line 4737) | def llen(self: SyncClientProtocol, name: KeyT) -> int: ...
    method llen (line 4740) | def llen(self: AsyncClientProtocol, name: KeyT) -> Awaitable[int]: ...
    method llen (line 4742) | def llen(self, name: KeyT) -> int | Awaitable[int]:
    method lpop (line 4751) | def lpop(
    method lpop (line 4758) | def lpop(
    method lpop (line 4764) | def lpop(
    method lpush (line 4786) | def lpush(self: SyncClientProtocol, name: KeyT, *values: FieldT) -> in...
    method lpush (line 4789) | def lpush(
    method lpush (line 4793) | def lpush(self, name: KeyT, *values: FieldT) -> int | Awaitable[int]:
    method lpushx (line 4802) | def lpushx(self: SyncClientProtocol, name: KeyT, *values: FieldT) -> i...
    method lpushx (line 4805) | def lpushx(
    method lpushx (line 4809) | def lpushx(self, name: KeyT, *values: FieldT) -> int | Awaitable[int]:
    method lrange (line 4818) | def lrange(
    method lrange (line 4823) | def lrange(
    method lrange (line 4827) | def lrange(
    method lrem (line 4842) | def lrem(self: SyncClientProtocol, name: KeyT, count: int, value: str)...
    method lrem (line 4845) | def lrem(
    method lrem (line 4849) | def lrem(self, name: KeyT, count: int, value: str) -> int | Awaitable[...
    method lset (line 4864) | def lset(self: SyncClientProtocol, name: KeyT, index: int, value: str)...
    method lset (line 4867) | def lset(
    method lset (line 4871) | def lset(self, name: KeyT, index: int, value: str) -> bool | Awaitable...
    method ltrim (line 4880) | def ltrim(self: SyncClientProtocol, name: KeyT, start: int, end: int) ...
    method ltrim (line 4883) | def ltrim(
    method ltrim (line 4887) | def ltrim(self, name: KeyT, start: int, end: int) -> bool | Awaitable[...
    method rpop (line 4900) | def rpop(
    method rpop (line 4907) | def rpop(
    method rpop (line 4913) | def rpop(
    method rpoplpush (line 4935) | def rpoplpush(
    method rpoplpush (line 4940) | def rpoplpush(
    method rpoplpush (line 4944) | def rpoplpush(self, src: KeyT, dst: KeyT) -> (bytes | str | None) | Aw...
    method rpush (line 4956) | def rpush(self: SyncClientProtocol, name: KeyT, *values: FieldT) -> in...
    method rpush (line 4959) | def rpush(
    method rpush (line 4963) | def rpush(self, name: KeyT, *values: FieldT) -> int | Awaitable[int]:
    method rpushx (line 4972) | def rpushx(self: SyncClientProtocol, name: KeyT, *values: str) -> int:...
    method rpushx (line 4975) | def rpushx(
    method rpushx (line 4979) | def rpushx(self, name: KeyT, *values: str) -> int | Awaitable[int]:
    method lpos (line 4988) | def lpos(
    method lpos (line 4998) | def lpos(
    method lpos (line 5007) | def lpos(
    method sort (line 5053) | def sort(
    method sort (line 5067) | def sort(
    method sort (line 5080) | def sort(
    method sort_ro (line 5154) | def sort_ro(
    method sort_ro (line 5166) | def sort_ro(
    method sort_ro (line 5177) | def sort_ro(
  class ScanCommands (line 5214) | class ScanCommands(CommandsProtocol):
    method scan (line 5221) | def scan(
    method scan (line 5231) | def scan(
    method scan (line 5240) | def scan(
    method scan_iter (line 5273) | def scan_iter(
    method sscan (line 5302) | def sscan(
    method sscan (line 5311) | def sscan(
    method sscan (line 5319) | def sscan(
    method sscan_iter (line 5343) | def sscan_iter(
    method hscan (line 5363) | def hscan(
    method hscan (line 5373) | def hscan(
    method hscan (line 5382) | def hscan(
    method hscan_iter (line 5411) | def hscan_iter(
    method zscan (line 5439) | def zscan(
    method zscan (line 5449) | def zscan(
    method zscan (line 5458) | def zscan(
    method zscan_iter (line 5486) | def zscan_iter(
  class AsyncScanCommands (line 5515) | class AsyncScanCommands(ScanCommands):
    method scan_iter (line 5516) | async def scan_iter(
    method sscan_iter (line 5545) | async def sscan_iter(
    method hscan_iter (line 5567) | async def hscan_iter(
    method zscan_iter (line 5596) | async def zscan_iter(
  class SetCommands (line 5626) | class SetCommands(CommandsProtocol):
    method sadd (line 5633) | def sadd(self: SyncClientProtocol, name: KeyT, *values: FieldT) -> int...
    method sadd (line 5636) | def sadd(
    method sadd (line 5640) | def sadd(self, name: KeyT, *values: FieldT) -> int | Awaitable[int]:
    method scard (line 5649) | def scard(self: SyncClientProtocol, name: KeyT) -> int: ...
    method scard (line 5652) | def scard(self: AsyncClientProtocol, name: KeyT) -> Awaitable[int]: ...
    method scard (line 5654) | def scard(self, name: KeyT) -> int | Awaitable[int]:
    method sdiff (line 5663) | def sdiff(
    method sdiff (line 5668) | def sdiff(
    method sdiff (line 5672) | def sdiff(
    method sdiffstore (line 5684) | def sdiffstore(
    method sdiffstore (line 5689) | def sdiffstore(
    method sdiffstore (line 5693) | def sdiffstore(self, dest: str, keys: List, *args: List) -> int | Awai...
    method sinter (line 5704) | def sinter(
    method sinter (line 5709) | def sinter(
    method sinter (line 5713) | def sinter(
    method sintercard (line 5725) | def sintercard(
    method sintercard (line 5730) | def sintercard(
    method sintercard (line 5734) | def sintercard(
    method sinterstore (line 5750) | def sinterstore(
    method sinterstore (line 5755) | def sinterstore(
    method sinterstore (line 5759) | def sinterstore(self, dest: KeyT, keys: List, *args: List) -> int | Aw...
    method sismember (line 5770) | def sismember(
    method sismember (line 5775) | def sismember(
    method sismember (line 5779) | def sismember(self, name: KeyT, value: str) -> (
    method smembers (line 5792) | def smembers(self: SyncClientProtocol, name: KeyT) -> set[bytes | str]...
    method smembers (line 5795) | def smembers(
    method smembers (line 5799) | def smembers(self, name: KeyT) -> set[bytes | str] | Awaitable[set[byt...
    method smismember (line 5808) | def smismember(
    method smismember (line 5813) | def smismember(
    method smismember (line 5817) | def smismember(
    method smove (line 5832) | def smove(self: SyncClientProtocol, src: KeyT, dst: KeyT, value: str) ...
    method smove (line 5835) | def smove(
    method smove (line 5839) | def smove(self, src: KeyT, dst: KeyT, value: str) -> bool | Awaitable[...
    method spop (line 5848) | def spop(
    method spop (line 5853) | def spop(
    method spop (line 5857) | def spop(self, name: KeyT, count: int | None = None) -> (
    method srandmember (line 5869) | def srandmember(
    method srandmember (line 5874) | def srandmember(
    method srandmember (line 5878) | def srandmember(self, name: KeyT, number: int | None = None) -> (
    method srem (line 5894) | def srem(self: SyncClientProtocol, name: KeyT, *values: FieldT) -> int...
    method srem (line 5897) | def srem(
    method srem (line 5901) | def srem(self, name: KeyT, *values: FieldT) -> int | Awaitable[int]:
    method sunion (line 5910) | def sunion(
    method sunion (line 5915) | def sunion(
    method sunion (line 5919) | def sunion(
    method sunionstore (line 5931) | def sunionstore(
    method sunionstore (line 5936) | def sunionstore(
    method sunionstore (line 5940) | def sunionstore(self, dest: KeyT, keys: List, *args: List) -> int | Aw...
  class StreamCommands (line 5954) | class StreamCommands(CommandsProtocol):
    method xack (line 5961) | def xack(
    method xack (line 5966) | def xack(
    method xack (line 5970) | def xack(
    method xackdel (line 5986) | def xackdel(
    method xackdel (line 5995) | def xackdel(
    method xackdel (line 6003) | def xackdel(
    method xadd (line 6026) | def xadd(
    method xadd (line 6042) | def xadd(
    method xadd (line 6057) | def xadd(
    method xcfgset (line 6148) | def xcfgset(
    method xcfgset (line 6156) | def xcfgset(
    method xcfgset (line 6163) | def xcfgset(
    method xautoclaim (line 6228) | def xautoclaim(
    method xautoclaim (line 6240) | def xautoclaim(
    method xautoclaim (line 6251) | def xautoclaim(
    method xclaim (line 6303) | def xclaim(
    method xclaim (line 6318) | def xclaim(
    method xclaim (line 6332) | def xclaim(
    method xdel (line 6416) | def xdel(self: SyncClientProtocol, name: KeyT, *ids: StreamIdT) -> int...
    method xdel (line 6419) | def xdel(
    method xdel (line 6423) | def xdel(self, name: KeyT, *ids: StreamIdT) -> int | Awaitable[int]:
    method xdelex (line 6436) | def xdelex(
    method xdelex (line 6444) | def xdelex(
    method xdelex (line 6451) | def xdelex(
    method xgroup_create (line 6472) | def xgroup_create(
    method xgroup_create (line 6482) | def xgroup_create(
    method xgroup_create (line 6491) | def xgroup_create(
    method xgroup_delconsumer (line 6516) | def xgroup_delconsumer(
    method xgroup_delconsumer (line 6521) | def xgroup_delconsumer(
    method xgroup_delconsumer (line 6528) | def xgroup_delconsumer(
    method xgroup_destroy (line 6544) | def xgroup_destroy(
    method xgroup_destroy (line 6549) | def xgroup_destroy(
    method xgroup_destroy (line 6553) | def xgroup_destroy(self, name: KeyT, groupname: GroupT) -> bool | Awai...
    method xgroup_createconsumer (line 6564) | def xgroup_createconsumer(
    method xgroup_createconsumer (line 6569) | def xgroup_createconsumer(
    method xgroup_createconsumer (line 6576) | def xgroup_createconsumer(
    method xgroup_setid (line 6594) | def xgroup_setid(
    method xgroup_setid (line 6603) | def xgroup_setid(
    method xgroup_setid (line 6611) | def xgroup_setid(
    method xinfo_consumers (line 6632) | def xinfo_consumers(
    method xinfo_consumers (line 6637) | def xinfo_consumers(
    method xinfo_consumers (line 6641) | def xinfo_consumers(
    method xinfo_groups (line 6654) | def xinfo_groups(self: SyncClientProtocol, name: KeyT) -> list[dict[st...
    method xinfo_groups (line 6657) | def xinfo_groups(
    method xinfo_groups (line 6661) | def xinfo_groups(
    method xinfo_stream (line 6673) | def xinfo_stream(
    method xinfo_stream (line 6678) | def xinfo_stream(
    method xinfo_stream (line 6682) | def xinfo_stream(
    method xlen (line 6700) | def xlen(self: SyncClientProtocol, name: KeyT) -> int: ...
    method xlen (line 6703) | def xlen(self: AsyncClientProtocol, name: KeyT) -> Awaitable[int]: ...
    method xlen (line 6705) | def xlen(self, name: KeyT) -> int | Awaitable[int]:
    method xpending (line 6714) | def xpending(
    method xpending (line 6719) | def xpending(
    method xpending (line 6723) | def xpending(
    method xpending_range (line 6736) | def xpending_range(
    method xpending_range (line 6748) | def xpending_range(
    method xpending_range (line 6759) | def xpending_range(
    method xrange (line 6817) | def xrange(
    method xrange (line 6826) | def xrange(
    method xrange (line 6834) | def xrange(
    method xread (line 6867) | def xread(
    method xread (line 6875) | def xread(
    method xread (line 6882) | def xread(
    method xreadgroup (line 6935) | def xreadgroup(
    method xreadgroup (line 6947) | def xreadgroup(
    method xreadgroup (line 6958) | def xreadgroup(
    method xrevrange (line 7039) | def xrevrange(
    method xrevrange (line 7048) | def xrevrange(
    method xrevrange (line 7056) | def xrevrange(
    method xtrim (line 7089) | def xtrim(
    method xtrim (line 7100) | def xtrim(
    method xtrim (line 7110) | def xtrim(
  class SortedSetCommands (line 7167) | class SortedSetCommands(CommandsProtocol):
    method zadd (line 7173) | def zadd(
    method zcard (line 7250) | def zcard(self, name: KeyT) -> ResponseT:
    method zcount (line 7259) | def zcount(
    method zcount (line 7264) | def zcount(
    method zcount (line 7268) | def zcount(
    method zdiff (line 7280) | def zdiff(
    method zdiff (line 7285) | def zdiff(
    method zdiff (line 7289) | def zdiff(
    method zdiffstore (line 7304) | def zdiffstore(self: SyncClientProtocol, dest: KeyT, keys: KeysT) -> i...
    method zdiffstore (line 7307) | def zdiffstore(
    method zdiffstore (line 7311) | def zdiffstore(self, dest: KeyT, keys: KeysT) -> int | Awaitable[int]:
    method zincrby (line 7322) | def zincrby(
    method zincrby (line 7327) | def zincrby(
    method zincrby (line 7331) | def zincrby(self, name: KeyT, amount: float, value: EncodableT) -> (
    method zinter (line 7342) | def zinter(
    method zinter (line 7350) | def zinter(
    method zinter (line 7357) | def zinter(
    method zinterstore (line 7374) | def zinterstore(
    method zinterstore (line 7382) | def zinterstore(
    method zinterstore (line 7389) | def zinterstore(
    method zintercard (line 7409) | def zintercard(
    method zintercard (line 7414) | def zintercard(
    method zintercard (line 7418) | def zintercard(
    method zlexcount (line 7434) | def zlexcount(
    method zlexcount (line 7439) | def zlexcount(
    method zlexcount (line 7443) | def zlexcount(
    method zpopmax (line 7455) | def zpopmax(
    method zpopmax (line 7460) | def zpopmax(
    method zpopmax (line 7464) | def zpopmax(
    method zpopmin (line 7478) | def zpopmin(
    method zpopmin (line 7483) | def zpopmin(
    method zpopmin (line 7487) | def zpopmin(
    method zrandmember (line 7501) | def zrandmember(
    method zrandmember (line 7509) | def zrandmember(
    method zrandmember (line 7516) | def zrandmember(
    method bzpopmax (line 7543) | def bzpopmax(
    method bzpopmax (line 7548) | def bzpopmax(
    method bzpopmax (line 7552) | def bzpopmax(
    method bzpopmin (line 7574) | def bzpopmin(
    method bzpopmin (line 7579) | def bzpopmin(
    method bzpopmin (line 7583) | def bzpopmin(
    method zmpop (line 7605) | def zmpop(
    method zmpop (line 7615) | def zmpop(
    method zmpop (line 7624) | def zmpop(
    method bzmpop (line 7650) | def bzmpop(
    method bzmpop (line 7661) | def bzmpop(
    method bzmpop (line 7671) | def bzmpop(
    method _zrange (line 7703) | def _zrange(
    method zrange (line 7745) | def zrange(
    method zrange (line 7760) | def zrange(
    method zrange (line 7774) | def zrange(
    method zrevrange (line 7836) | def zrevrange(
    method zrevrange (line 7846) | def zrevrange(
    method zrevrange (line 7855) | def zrevrange(
    method zrangestore (line 7884) | def zrangestore(
    method zrangestore (line 7898) | def zrangestore(
    method zrangestore (line 7911) | def zrangestore(
    method zrangebylex (line 7962) | def zrangebylex(
    method zrangebylex (line 7972) | def zrangebylex(
    method zrangebylex (line 7981) | def zrangebylex(
    method zrevrangebylex (line 8006) | def zrevrangebylex(
    method zrevrangebylex (line 8016) | def zrevrangebylex(
    method zrevrangebylex (line 8025) | def zrevrangebylex(
    method zrangebyscore (line 8050) | def zrangebyscore(
    method zrangebyscore (line 8062) | def zrangebyscore(
    method zrangebyscore (line 8073) | def zrangebyscore(
    method zrevrangebyscore (line 8109) | def zrevrangebyscore(
    method zrevrangebyscore (line 8121) | def zrevrangebyscore(
    method zrevrangebyscore (line 8132) | def zrevrangebyscore(
    method zrank (line 8168) | def zrank(
    method zrank (line 8177) | def zrank(
    method zrank (line 8185) | def zrank(
    method zrem (line 8211) | def zrem(self: SyncClientProtocol, name: KeyT, *values: FieldT) -> int...
    method zrem (line 8214) | def zrem(
    method zrem (line 8218) | def zrem(self, name: KeyT, *values: FieldT) -> int | Awaitable[int]:
    method zremrangebylex (line 8227) | def zremrangebylex(
    method zremrangebylex (line 8232) | def zremrangebylex(
    method zremrangebylex (line 8236) | def zremrangebylex(
    method zremrangebyrank (line 8250) | def zremrangebyrank(
    method zremrangebyrank (line 8255) | def zremrangebyrank(
    method zremrangebyrank (line 8259) | def zremrangebyrank(self, name: KeyT, min: int, max: int) -> int | Awa...
    method zremrangebyscore (line 8271) | def zremrangebyscore(
    method zremrangebyscore (line 8276) | def zremrangebyscore(
    method zremrangebyscore (line 8280) | def zremrangebyscore(
    method zrevrank (line 8292) | def zrevrank(
    method zrevrank (line 8301) | def zrevrank(
    method zrevrank (line 8309) | def zrevrank(
    method zscore (line 8335) | def zscore(
    method zscore (line 8340) | def zscore(
    method zscore (line 8344) | def zscore(self, name: KeyT, value: EncodableT) -> (float | None) | Aw...
    method zunion (line 8355) | def zunion(
    method zunion (line 8364) | def zunion(
    method zunion (line 8372) | def zunion(
    method zunionstore (line 8399) | def zunionstore(
    method zunionstore (line 8407) | def zunionstore(
    method zunionstore (line 8414) | def zunionstore(
    method zmscore (line 8430) | def zmscore(
    method zmscore (line 8435) | def zmscore(
    method zmscore (line 8439) | def zmscore(
    method _zaggregate (line 8457) | def _zaggregate(
  class HyperlogCommands (line 8492) | class HyperlogCommands(CommandsProtocol):
    method pfadd (line 8499) | def pfadd(self: SyncClientProtocol, name: KeyT, *values: FieldT) -> in...
    method pfadd (line 8502) | def pfadd(
    method pfadd (line 8506) | def pfadd(self, name: KeyT, *values: FieldT) -> int | Awaitable[int]:
    method pfcount (line 8515) | def pfcount(self: SyncClientProtocol, *sources: KeyT) -> int: ...
    method pfcount (line 8518) | def pfcount(self: AsyncClientProtocol, *sources: KeyT) -> Awaitable[in...
    method pfcount (line 8520) | def pfcount(self, *sources: KeyT) -> int | Awaitable[int]:
    method pfmerge (line 8530) | def pfmerge(self: SyncClientProtocol, dest: KeyT, *sources: KeyT) -> b...
    method pfmerge (line 8533) | def pfmerge(
    method pfmerge (line 8537) | def pfmerge(self, dest: KeyT, *sources: KeyT) -> bool | Awaitable[bool]:
  class HashDataPersistOptions (line 8549) | class HashDataPersistOptions(Enum):
  class HashCommands (line 8559) | class HashCommands(CommandsProtocol):
    method hdel (line 8566) | def hdel(self: SyncClientProtocol, name: str, *keys: str) -> int: ...
    method hdel (line 8569) | def hdel(self: AsyncClientProtocol, name: str, *keys: str) -> Awaitabl...
    method hdel (line 8571) | def hdel(self, name: str, *keys: str) -> int | Awaitable[int]:
    method hexists (line 8580) | def hexists(self: SyncClientProtocol, name: str, key: str) -> bool: ...
    method hexists (line 8583) | def hexists(self: AsyncClientProtocol, name: str, key: str) -> Awaitab...
    method hexists (line 8585) | def hexists(self, name: str, key: str) -> bool | Awaitable[bool]:
    method hget (line 8594) | def hget(self: SyncClientProtocol, name: str, key: str) -> bytes | str...
    method hget (line 8597) | def hget(
    method hget (line 8601) | def hget(self, name: str, key: str) -> (bytes | str | None) | Awaitable[
    method hgetall (line 8612) | def hgetall(
    method hgetall (line 8617) | def hgetall(
    method hgetall (line 8621) | def hgetall(
    method hgetdel (line 8632) | def hgetdel(
    method hgetdel (line 8637) | def hgetdel(
    method hgetdel (line 8641) | def hgetdel(
    method hgetex (line 8659) | def hgetex(
    method hgetex (line 8671) | def hgetex(
    method hgetex (line 8682) | def hgetex(
    method hincrby (line 8735) | def hincrby(
    method hincrby (line 8740) | def hincrby(
    method hincrby (line 8744) | def hincrby(self, name: str, key: str, amount: int = 1) -> int | Await...
    method hincrbyfloat (line 8753) | def hincrbyfloat(
    method hincrbyfloat (line 8758) | def hincrbyfloat(
    method hincrbyfloat (line 8762) | def hincrbyfloat(
    method hkeys (line 8773) | def hkeys(self: SyncClientProtocol, name: str) -> list[bytes | str]: ...
    method hkeys (line 8776) | def hkeys(self: AsyncClientProtocol, name: str) -> Awaitable[list[byte...
    method hkeys (line 8778) | def hkeys(self, name: str) -> list[bytes | str] | Awaitable[list[bytes...
    method hlen (line 8787) | def hlen(self: SyncClientProtocol, name: str) -> int: ...
    method hlen (line 8790) | def hlen(self: AsyncClientProtocol, name: str) -> Awaitable[int]: ...
    method hlen (line 8792) | def hlen(self, name: str) -> int | Awaitable[int]:
    method hset (line 8801) | def hset(
    method hset (line 8811) | def hset(
    method hset (line 8820) | def hset(
    method hsetex (line 8854) | def hsetex(
    method hsetex (line 8870) | def hsetex(
    method hsetex (line 8885) | def hsetex(
    method hsetnx (line 8968) | def hsetnx(self: SyncClientProtocol, name: str, key: str, value: str) ...
    method hsetnx (line 8971) | def hsetnx(
    method hsetnx (line 8975) | def hsetnx(self, name: str, key: str, value: str) -> int | Awaitable[i...
    method hmset (line 8985) | def hmset(self: SyncClientProtocol, name: str, mapping: dict) -> bool:...
    method hmset (line 8988) | def hmset(
    method hmset (line 8997) | def hmset(self, name: str, mapping: dict) -> bool | Awaitable[bool]:
    method hmget (line 9012) | def hmget(
    method hmget (line 9017) | def hmget(
    method hmget (line 9021) | def hmget(
    method hvals (line 9033) | def hvals(self: SyncClientProtocol, name: str) -> list[bytes | str]: ...
    method hvals (line 9036) | def hvals(self: AsyncClientProtocol, name: str) -> Awaitable[list[byte...
    method hvals (line 9038) | def hvals(self, name: str) -> list[bytes | str] | Awaitable[list[bytes...
    method hstrlen (line 9047) | def hstrlen(self: SyncClientProtocol, name: str, key: str) -> int: ...
    method hstrlen (line 9050) | def hstrlen(self: AsyncClientProtocol, name: str, key: str) -> Awaitab...
    method hstrlen (line 9052) | def hstrlen(self, name: str, key: str) -> int | Awaitable[int]:
    method hexpire (line 9062) | def hexpire(
    method hexpire (line 9074) | def hexpire(
    method hexpire (line 9085) | def hexpire(
    method hpexpire (line 9146) | def hpexpire(
    method hpexpire (line 9158) | def hpexpire(
    method hpexpire (line 9169) | def hpexpire(
    method hexpireat (line 9230) | def hexpireat(
    method hexpireat (line 9242) | def hexpireat(
    method hexpireat (line 9253) | def hexpireat(
    method hpexpireat (line 9320) | def hpexpireat(
    method hpexpireat (line 9332) | def hpexpireat(
    method hpexpireat (line 9343) | def hpexpireat(
    method hpersist (line 9410) | def hpersist(self: SyncClientProtocol, name: KeyT, *fields: str) -> li...
    method hpersist (line 9413) | def hpersist(
    method hpersist (line 9417) | def hpersist(self, name: KeyT, *fields: str) -> list[int] | Awaitable[...
    method hexpiretime (line 9437) | def hexpiretime(self: SyncClientProtocol, key: KeyT, *fields: str) -> ...
    method hexpiretime (line 9440) | def hexpiretime(
    method hexpiretime (line 9444) | def hexpiretime(self, key: KeyT, *fields: str) -> list[int] | Awaitabl...
    method hpexpiretime (line 9467) | def hpexpiretime(
    method hpexpiretime (line 9472) | def hpexpiretime(
    method hpexpiretime (line 9476) | def hpexpiretime(self, key: KeyT, *fields: str) -> list[int] | Awaitab...
    method httl (line 9499) | def httl(self: SyncClientProtocol, key: KeyT, *fields: str) -> list[in...
    method httl (line 9502) | def httl(
    method httl (line 9506) | def httl(self, key: KeyT, *fields: str) -> list[int] | Awaitable[list[...
    method hpttl (line 9529) | def hpttl(self: SyncClientProtocol, key: KeyT, *fields: str) -> list[i...
    method hpttl (line 9532) | def hpttl(
    method hpttl (line 9536) | def hpttl(self, key: KeyT, *fields: str) -> list[int] | Awaitable[list...
  class Script (line 9562) | class Script:
    method __init__ (line 9567) | def __init__(self, registered_client: "redis.client.Redis", script: Sc...
    method __call__ (line 9579) | def __call__(
    method get_encoder (line 9606) | def get_encoder(self):
  class AsyncScript (line 9625) | class AsyncScript:
    method __init__ (line 9630) | def __init__(
    method __call__ (line 9650) | async def __call__(
  class PubSubCommands (line 9678) | class PubSubCommands(CommandsProtocol):
    method publish (line 9685) | def publish(
    method publish (line 9690) | def publish(
    method publish (line 9694) | def publish(
    method spublish (line 9711) | def spublish(
    method spublish (line 9716) | def spublish(
    method spublish (line 9720) | def spublish(
    method pubsub_channels (line 9738) | def pubsub_channels(
    method pubsub_channels (line 9743) | def pubsub_channels(
    method pubsub_channels (line 9747) | def pubsub_channels(
    method pubsub_shardchannels (line 9758) | def pubsub_shardchannels(
    method pubsub_shardchannels (line 9763) | def pubsub_shardchannels(
    method pubsub_shardchannels (line 9767) | def pubsub_shardchannels(
    method pubsub_numpat (line 9778) | def pubsub_numpat(self: SyncClientProtocol, **kwargs) -> int: ...
    method pubsub_numpat (line 9781) | def pubsub_numpat(self: AsyncClientProtocol, **kwargs) -> Awaitable[in...
    method pubsub_numpat (line 9783) | def pubsub_numpat(self, **kwargs) -> int | Awaitable[int]:
    method pubsub_numsub (line 9792) | def pubsub_numsub(
    method pubsub_numsub (line 9797) | def pubsub_numsub(
    method pubsub_numsub (line 9801) | def pubsub_numsub(
    method pubsub_shardnumsub (line 9813) | def pubsub_shardnumsub(
    method pubsub_shardnumsub (line 9818) | def pubsub_shardnumsub(
    method pubsub_shardnumsub (line 9822) | def pubsub_shardnumsub(
  class ScriptCommands (line 9837) | class ScriptCommands(CommandsProtocol):
    method _eval (line 9843) | def _eval(
    method eval (line 9853) | def eval(
    method eval (line 9861) | def eval(
    method eval (line 9868) | def eval(
    method eval_ro (line 9884) | def eval_ro(
    method eval_ro (line 9892) | def eval_ro(
    method eval_ro (line 9899) | def eval_ro(
    method _evalsha (line 9913) | def _evalsha(
    method evalsha (line 9923) | def evalsha(
    method evalsha (line 9931) | def evalsha(
    method evalsha (line 9938) | def evalsha(
    method evalsha_ro (line 9955) | def evalsha_ro(
    method evalsha_ro (line 9963) | def evalsha_ro(
    method evalsha_ro (line 9970) | def evalsha_ro(
    method script_exists (line 9986) | def script_exists(self: SyncClientProtocol, *args: str) -> list[bool]:...
    method script_exists (line 9989) | def script_exists(
    method script_exists (line 9993) | def script_exists(self, *args: str) -> list[bool] | Awaitable[list[boo...
    method script_debug (line 10003) | def script_debug(self, *args) -> None:
    method script_flush (line 10009) | def script_flush(
    method script_flush (line 10015) | def script_flush(
    method script_flush (line 10020) | def script_flush(
    method script_kill (line 10045) | def script_kill(self: SyncClientProtocol) -> bool: ...
    method script_kill (line 10048) | def script_kill(self: AsyncClientProtocol) -> Awaitable[bool]: ...
    method script_kill (line 10050) | def script_kill(self) -> bool | Awaitable[bool]:
    method script_load (line 10059) | def script_load(self: SyncClientProtocol, script: ScriptTextT) -> str:...
    method script_load (line 10062) | def script_load(
    method script_load (line 10066) | def script_load(self, script: ScriptTextT) -> str | Awaitable[str]:
    method register_script (line 10074) | def register_script(self: "redis.client.Redis", script: ScriptTextT) -...
  class AsyncScriptCommands (line 10084) | class AsyncScriptCommands(ScriptCommands):
    method script_debug (line 10085) | async def script_debug(self, *args) -> None:
    method register_script (line 10088) | def register_script(
  class GeoCommands (line 10101) | class GeoCommands(CommandsProtocol):
    method geoadd (line 10108) | def geoadd(
    method geoadd (line 10118) | def geoadd(
    method geoadd (line 10127) | def geoadd(
    method geodist (line 10170) | def geodist(
    method geodist (line 10179) | def geodist(
    method geodist (line 10187) | def geodist(
    method geohash (line 10206) | def geohash(
    method geohash (line 10211) | def geohash(
    method geohash (line 10215) | def geohash(
    method geopos (line 10227) | def geopos(
    method geopos (line 10232) | def geopos(
    method geopos (line 10236) | def geopos(
    method georadius (line 10249) | def georadius(
    method georadius (line 10267) | def georadius(
    method georadius (line 10284) | def georadius(
    method georadiusbymember (line 10348) | def georadiusbymember(
    method georadiusbymember (line 10365) | def georadiusbymember(
    method georadiusbymember (line 10381) | def georadiusbymember(
    method _georadiusgeneric (line 10420) | def _georadiusgeneric(
    method geosearch (line 10467) | def geosearch(
    method geosearch (line 10486) | def geosearch(
    method geosearch (line 10504) | def geosearch(
    method geosearchstore (line 10586) | def geosearchstore(
    method geosearchstore (line 10604) | def geosearchstore(
    method geosearchstore (line 10621) | def geosearchstore(
    method _geosearchgeneric (line 10668) | def _geosearchgeneric(
  class ModuleCommands (line 10738) | class ModuleCommands(CommandsProtocol):
    method module_load (line 10745) | def module_load(self: SyncClientProtocol, path, *args) -> bool: ...
    method module_load (line 10748) | def module_load(self: AsyncClientProtocol, path, *args) -> Awaitable[b...
    method module_load (line 10750) | def module_load(self, path, *args) -> bool | Awaitable[bool]:
    method module_loadex (line 10761) | def module_loadex(
    method module_loadex (line 10769) | def module_loadex(
    method module_loadex (line 10776) | def module_loadex(
    method module_unload (line 10798) | def module_unload(self: SyncClientProtocol, name) -> bool: ...
    method module_unload (line 10801) | def module_unload(self: AsyncClientProtocol, name) -> Awaitable[bool]:...
    method module_unload (line 10803) | def module_unload(self, name) -> bool | Awaitable[bool]:
    method module_list (line 10813) | def module_list(self: SyncClientProtocol) -> list[dict[Any, Any]]: ...
    method module_list (line 10816) | def module_list(self: AsyncClientProtocol) -> Awaitable[list[dict[Any,...
    method module_list (line 10818) | def module_list(self) -> list[dict[Any, Any]] | Awaitable[list[dict[An...
    method command_info (line 10827) | def command_info(self) -> None:
    method command_count (line 10833) | def command_count(self: SyncClientProtocol) -> int: ...
    method command_count (line 10836) | def command_count(self: AsyncClientProtocol) -> Awaitable[int]: ...
    method command_count (line 10838) | def command_count(self) -> int | Awaitable[int]:
    method command_getkeys (line 10842) | def command_getkeys(self: SyncClientProtocol, *args) -> list[bytes | s...
    method command_getkeys (line 10845) | def command_getkeys(
    method command_getkeys (line 10849) | def command_getkeys(
    method command (line 10855) | def command(self: SyncClientProtocol) -> dict[str, dict[str, Any]]: ...
    method command (line 10858) | def command(
    method command (line 10862) | def command(
  class AsyncModuleCommands (line 10868) | class AsyncModuleCommands(ModuleCommands):
    method command_info (line 10869) | async def command_info(self) -> None:
  class ClusterCommands (line 10873) | class ClusterCommands(CommandsProtocol):
    method cluster (line 10879) | def cluster(self: SyncClientProtocol, cluster_arg, *args, **kwargs) ->...
    method cluster (line 10882) | def cluster(
    method cluster (line 10886) | def cluster(self, cluster_arg, *args, **kwargs) -> Any | Awaitable[Any]:
    method readwrite (line 10890) | def readwrite(self: SyncClientProtocol, **kwargs) -> bool: ...
    method readwrite (line 10893) | def readwrite(self: AsyncClientProtocol, **kwargs) -> Awaitable[bool]:...
    method readwrite (line 10895) | def readwrite(self, **kwargs) -> bool | Awaitable[bool]:
    method readonly (line 10904) | def readonly(self: SyncClientProtocol, **kwargs) -> bool: ...
    method readonly (line 10907) | def readonly(self: AsyncClientProtocol, **kwargs) -> Awaitable[bool]: ...
    method readonly (line 10909) | def readonly(self, **kwargs) -> bool | Awaitable[bool]:
  class FunctionCommands (line 10921) | class FunctionCommands:
    method function_load (line 10927) | def function_load(
    method function_load (line 10932) | def function_load(
    method function_load (line 10936) | def function_load(self, code: str, replace: bool | None = False) -> (
    method function_delete (line 10954) | def function_delete(self: SyncClientProtocol, library: str) -> bool: ...
    method function_delete (line 10957) | def function_delete(self: AsyncClientProtocol, library: str) -> Awaita...
    method function_delete (line 10959) | def function_delete(self, library: str) -> bool | Awaitable[bool]:
    method function_flush (line 10968) | def function_flush(self: SyncClientProtocol, mode: str = "SYNC") -> bo...
    method function_flush (line 10971) | def function_flush(
    method function_flush (line 10975) | def function_flush(self, mode: str = "SYNC") -> bool | Awaitable[bool]:
    method function_list (line 10984) | def function_list(
    method function_list (line 10991) | def function_list(
    method function_list (line 10997) | def function_list(
    method _fcall (line 11014) | def _fcall(self, command: str, function, numkeys: int, *keys_and_args:...
    method fcall (line 11018) | def fcall(
    method fcall (line 11023) | def fcall(
    method fcall (line 11027) | def fcall(
    method fcall_ro (line 11038) | def fcall_ro(
    method fcall_ro (line 11043) | def fcall_ro(
    method fcall_ro (line 11047) | def fcall_ro(
    method function_dump (line 11059) | def function_dump(self: SyncClientProtocol) -> bytes: ...
    method function_dump (line 11062) | def function_dump(self: AsyncClientProtocol) -> Awaitable[bytes]: ...
    method function_dump (line 11064) | def function_dump(self) -> bytes | Awaitable[bytes]:
    method function_restore (line 11078) | def function_restore(
    method function_restore (line 11083) | def function_restore(
    method function_restore (line 11087) | def function_restore(
    method function_kill (line 11100) | def function_kill(self: SyncClientProtocol) -> bytes | str: ...
    method function_kill (line 11103) | def function_kill(self: AsyncClientProtocol) -> Awaitable[bytes | str]...
    method function_kill (line 11105) | def function_kill(self) -> (bytes | str) | Awaitable[bytes | str]:
    method function_stats (line 11114) | def function_stats(self: SyncClientProtocol) -> Any: ...
    method function_stats (line 11117) | def function_stats(self: AsyncClientProtocol) -> Awaitable[Any]: ...
    method function_stats (line 11119) | def function_stats(self) -> Any | Awaitable[Any]:
  class DataAccessCommands (line 11132) | class DataAccessCommands(
  class AsyncDataAccessCommands (line 11149) | class AsyncDataAccessCommands(
  class CoreCommands (line 11166) | class CoreCommands(
  class AsyncCoreCommands (line 11182) | class AsyncCoreCommands(

FILE: redis/commands/helpers.py
  function list_or_args (line 10) | def list_or_args(keys: KeysT, args: Tuple[KeyT, ...]) -> List[KeyT]:
  function nativestr (line 27) | def nativestr(x):
  function delist (line 35) | def delist(x):
  function parse_to_list (line 42) | def parse_to_list(response):
  function random_string (line 75) | def random_string(length=10):
  function decode_dict_keys (line 84) | def decode_dict_keys(obj):
  function get_protocol_version (line 94) | def get_protocol_version(client):
  function at_most_one_value_set (line 101) | def at_most_one_value_set(iterable: Iterable[Any]):

FILE: redis/commands/json/__init__.py
  class JSON (line 10) | class JSON(JSONCommands):
    method __init__ (line 21) | def __init__(
    method _decode (line 82) | def _decode(self, obj):
    method _encode (line 100) | def _encode(self, obj):
    method pipeline (line 104) | def pipeline(self, transaction=True, shard_hint=None):
  class ClusterPipeline (line 142) | class ClusterPipeline(JSONCommands, redis.cluster.ClusterPipeline):
  class Pipeline (line 146) | class Pipeline(JSONCommands, redis.client.Pipeline):

FILE: redis/commands/json/commands.py
  class JSONCommands (line 13) | class JSONCommands:
    method arrappend (line 16) | def arrappend(
    method arrindex (line 29) | def arrindex(
    method arrinsert (line 54) | def arrinsert(
    method arrlen (line 67) | def arrlen(
    method arrpop (line 77) | def arrpop(
    method arrtrim (line 90) | def arrtrim(
    method type (line 100) | def type(self, name: str, path: Optional[str] = Path.root_path()) -> L...
    method resp (line 107) | def resp(self, name: str, path: Optional[str] = Path.root_path()) -> L...
    method objkeys (line 114) | def objkeys(
    method objlen (line 124) | def objlen(
    method numincrby (line 134) | def numincrby(self, name: str, path: str, number: int) -> str:
    method nummultby (line 145) | def nummultby(self, name: str, path: str, number: int) -> str:
    method clear (line 155) | def clear(self, name: str, path: Optional[str] = Path.root_path()) -> ...
    method delete (line 166) | def delete(self, key: str, path: Optional[str] = Path.root_path()) -> ...
    method get (line 176) | def get(
    method mget (line 206) | def mget(self, keys: List[str], path: str) -> List[JsonType]:
    method set (line 218) | def set(
    method mset (line 257) | def mset(self, triplets: List[Tuple[str, str, JsonType]]) -> Optional[...
    method merge (line 274) | def merge(
    method set_file (line 297) | def set_file(
    method set_path (line 322) | def set_path(
    method strlen (line 360) | def strlen(self, name: str, path: Optional[str] = None) -> List[Option...
    method toggle (line 371) | def toggle(
    method strappend (line 381) | def strappend(
    method debug (line 393) | def debug(
    method jsonget (line 418) | def jsonget(self, *args, **kwargs):
    method jsonmget (line 424) | def jsonmget(self, *args, **kwargs):
    method jsonset (line 430) | def jsonset(self, *args, **kwargs):

FILE: redis/commands/json/decoders.py
  function bulk_of_jsons (line 7) | def bulk_of_jsons(d):
  function decode_dict_keys (line 21) | def decode_dict_keys(obj):
  function unstring (line 31) | def unstring(obj):
  function decode_list (line 49) | def decode_list(b):

FILE: redis/commands/json/path.py
  class Path (line 1) | class Path:
    method root_path (line 7) | def root_path():
    method __init__ (line 11) | def __init__(self, path):
    method __repr__ (line 15) | def __repr__(self):

FILE: redis/commands/policies.py
  class PolicyResolver (line 124) | class PolicyResolver(ABC):
    method resolve (line 126) | def resolve(self, command_name: str) -> Optional[CommandPolicies]:
    method with_fallback (line 139) | def with_fallback(self, fallback: "PolicyResolver") -> "PolicyResolver":
  class AsyncPolicyResolver (line 152) | class AsyncPolicyResolver(ABC):
    method resolve (line 154) | async def resolve(self, command_name: str) -> Optional[CommandPolicies]:
    method with_fallback (line 167) | def with_fallback(self, fallback: "AsyncPolicyResolver") -> "AsyncPoli...
  class BasePolicyResolver (line 180) | class BasePolicyResolver(PolicyResolver):
    method __init__ (line 185) | def __init__(
    method resolve (line 191) | def resolve(self, command_name: str) -> Optional[CommandPolicies]:
    method with_fallback (line 214) | def with_fallback(self, fallback: "PolicyResolver") -> "PolicyResolver":
  class AsyncBasePolicyResolver (line 218) | class AsyncBasePolicyResolver(AsyncPolicyResolver):
    method __init__ (line 223) | def __init__(
    method resolve (line 229) | async def resolve(self, command_name: str) -> Optional[CommandPolicies]:
    method with_fallback (line 252) | def with_fallback(self, fallback: "AsyncPolicyResolver") -> "AsyncPoli...
  class DynamicPolicyResolver (line 256) | class DynamicPolicyResolver(BasePolicyResolver):
    method __init__ (line 261) | def __init__(
    method with_fallback (line 273) | def with_fallback(self, fallback: "PolicyResolver") -> "PolicyResolver":
  class StaticPolicyResolver (line 277) | class StaticPolicyResolver(BasePolicyResolver):
    method __init__ (line 282) | def __init__(self, fallback: Optional[PolicyResolver] = None) -> None:
    method with_fallback (line 290) | def with_fallback(self, fallback: "PolicyResolver") -> "PolicyResolver":
  class AsyncDynamicPolicyResolver (line 294) | class AsyncDynamicPolicyResolver(AsyncBasePolicyResolver):
    method __init__ (line 299) | def __init__(
    method with_fallback (line 312) | def with_fallback(self, fallback: "AsyncPolicyResolver") -> "AsyncPoli...
  class AsyncStaticPolicyResolver (line 316) | class AsyncStaticPolicyResolver(AsyncBasePolicyResolver):
    method __init__ (line 321) | def __init__(self, fallback: Optional[AsyncPolicyResolver] = None) -> ...
    method with_fallback (line 329) | def with_fallback(self, fallback: "AsyncPolicyResolver") -> "AsyncPoli...

FILE: redis/commands/redismodules.py
  class RedisModuleCommands (line 14) | class RedisModuleCommands:
    method json (line 19) | def json(self, encoder=JSONEncoder(), decoder=JSONDecoder()) -> JSON:
    method ft (line 27) | def ft(self, index_name="idx") -> Search:
    method ts (line 35) | def ts(self) -> TimeSeries:
    method bf (line 45) | def bf(self) -> BFBloom:
    method cf (line 53) | def cf(self) -> CFBloom:
    method cms (line 61) | def cms(self) -> CMSBloom:
    method topk (line 69) | def topk(self) -> TOPKBloom:
    method tdigest (line 77) | def tdigest(self) -> TDigestBloom:
    method vset (line 85) | def vset(self) -> VectorSet:
  class AsyncRedisModuleCommands (line 94) | class AsyncRedisModuleCommands(RedisModuleCommands):
    method ft (line 95) | def ft(self, index_name="idx") -> AsyncSearch:
    method vset (line 103) | def vset(self) -> AsyncVectorSet:

FILE: redis/commands/search/__init__.py
  class Search (line 20) | class Search(SearchCommands):
    class BatchIndexer (line 26) | class BatchIndexer:
      method __init__ (line 32) | def __init__(self, client, chunk_size=1000):
      method __del__ (line 40) | def __del__(self):
      method add_document (line 44) | def add_document(
      method add_document_hash (line 74) | def add_document_hash(self, doc_id, score=1.0, replace=False):
      method commit (line 86) | def commit(self):
    method __init__ (line 93) | def __init__(self, client, index_name="idx"):
    method pipeline (line 116) | def pipeline(self, transaction=True, shard_hint=None):
  class AsyncSearch (line 130) | class AsyncSearch(Search, AsyncSearchCommands):
    class BatchIndexer (line 131) | class BatchIndexer(Search.BatchIndexer):
      method add_document (line 137) | async def add_document(
      method commit (line 167) | async def commit(self):
    method pipeline (line 174) | def pipeline(self, transaction=True, shard_hint=None):
  class Pipeline (line 188) | class Pipeline(SearchCommands, RedisPipeline):
  class AsyncPipeline (line 194) | class AsyncPipeline(AsyncSearchCommands, AsyncioPipeline, Pipeline):

FILE: redis/commands/search/_util.py
  function to_string (line 1) | def to_string(s, encoding: str = "utf-8"):

FILE: redis/commands/search/aggregation.py
  class Limit (line 8) | class Limit:
    method __init__ (line 9) | def __init__(self, offset: int = 0, count: int = 0) -> None:
    method build_args (line 13) | def build_args(self):
  class Reducer (line 20) | class Reducer:
    method __init__ (line 29) | def __init__(self, *args: str) -> None:
    method alias (line 34) | def alias(self, alias: str) -> "Reducer":
    method args (line 59) | def args(self) -> Tuple[str, ...]:
  class SortDirection (line 63) | class SortDirection:
    method __init__ (line 70) | def __init__(self, field: str) -> None:
  class Asc (line 74) | class Asc(SortDirection):
  class Desc (line 82) | class Desc(SortDirection):
  class AggregateRequest (line 90) | class AggregateRequest:
    method __init__ (line 95) | def __
Condensed preview — 372 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (6,354K chars).
[
  {
    "path": ".agent/sync_async_type_hints_overload_guide.md",
    "chars": 59859,
    "preview": "# Agent Overload Implementation Guide\n\n## Purpose\nThis document provides instructions for implementing `@overload` signa"
  },
  {
    "path": ".dockerignore",
    "chars": 46,
    "preview": "**/__pycache__\n**/*.pyc\n.coverage\n.coverage.*\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 29,
    "preview": "doctests/* @dmaier-redislabs\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 559,
    "preview": "Thanks for wanting to report an issue you've found in redis-py. Please delete this text and fill in the template below. "
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 674,
    "preview": "### Description of change\n\n_Please provide a description of the change here._\n\n### Pull Request check-list\n\n_Please make"
  },
  {
    "path": ".github/actions/run-tests/action.yml",
    "chars": 7000,
    "preview": "name: 'Run redis-py tests'\ndescription: 'Runs redis-py tests against different Redis versions and configurations'\ninputs"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 153,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    labels:\n      - \"maintenance\"\n    sch"
  },
  {
    "path": ".github/release-drafter-config.yml",
    "chars": 1056,
    "preview": "name-template: '$NEXT_MINOR_VERSION'\ntag-template: 'v$NEXT_MINOR_VERSION'\nfilter-by-commitish: true\ncommitish: master\nau"
  },
  {
    "path": ".github/spellcheck-settings.yml",
    "chars": 533,
    "preview": "matrix:\n- name: Markdown\n  expect_match: false\n  apsell:\n    lang: en\n    d: en_US\n    ignore-case: true\n  dictionary:\n "
  },
  {
    "path": ".github/wordlist.txt",
    "chars": 1686,
    "preview": "APM\nARGV\nBFCommands\nbalancer\nCacheImpl\ncancelling\nCAS\nCFCommands\nCMSCommands\nClusterNode\nClusterNodes\nClusterPipeline\nCl"
  },
  {
    "path": ".github/workflows/codeql-analysis.yml",
    "chars": 2289,
    "preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
  },
  {
    "path": ".github/workflows/docs.yaml",
    "chars": 1049,
    "preview": "name: Docs CI\n\non:\n  push:\n    branches:\n      - master\n      - '[0-9].[0-9]'\n  pull_request:\n    branches:\n      - mast"
  },
  {
    "path": ".github/workflows/hiredis-py-integration.yaml",
    "chars": 2108,
    "preview": "name: Hiredis-py integration tests\n\non:\n  workflow_dispatch:\n    inputs:\n      redis-py-branch:\n        description: 're"
  },
  {
    "path": ".github/workflows/install_and_test.sh",
    "chars": 1284,
    "preview": "#!/bin/bash\n\nset -e\n\nSUFFIX=$1\nif [ -z ${SUFFIX} ]; then\n    echo \"Supply valid python package extension such as whl or "
  },
  {
    "path": ".github/workflows/integration.yaml",
    "chars": 8641,
    "preview": "name: CI\n\non:\n  push:\n    paths-ignore:\n      - 'docs/**'\n      - '**/*.rst'\n      - '**/*.md'\n    branches:\n      - mas"
  },
  {
    "path": ".github/workflows/pypi-publish.yaml",
    "chars": 734,
    "preview": "name: Publish tag to Pypi\n\non:\n  release:\n    types: [published]\n  workflow_dispatch:\n\npermissions:\n  contents: read  # "
  },
  {
    "path": ".github/workflows/release-drafter.yml",
    "chars": 767,
    "preview": "name: Release Drafter\n\non:\n  push:\n    # branches to consider in the event; optional, defaults to all\n    branches:\n    "
  },
  {
    "path": ".github/workflows/spellcheck.yml",
    "chars": 341,
    "preview": "name: spellcheck\non:\n  pull_request:\njobs:\n  check-spelling:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkou"
  },
  {
    "path": ".github/workflows/stale-issues.yml",
    "chars": 3713,
    "preview": "name: \"Stale Issue Management\"\non:\n  schedule:\n    # Run daily at midnight UTC\n    - cron: \"0 0 * * *\"\n  workflow_dispat"
  },
  {
    "path": ".gitignore",
    "chars": 358,
    "preview": "*.pyc\nredis.egg-info\nbuild/\ndist/\ndump.rdb\n_build\nvagrant/.vagrant\n.python-version\n.cache\n.eggs\n.idea\n.vscode\n.coverage\n"
  },
  {
    "path": ".mypy.ini",
    "chars": 626,
    "preview": "[mypy]\n#, docs/examples, tests\nfiles = redis\ncheck_untyped_defs = True\nfollow_imports_for_stubs asyncio.= True\n#disallow"
  },
  {
    "path": ".readthedocs.yml",
    "chars": 199,
    "preview": "version: 2\n\npython:\n  install:\n    - requirements: docs/requirements.txt\n    - method: pip\n      path: .\n\nbuild:\n  os: u"
  },
  {
    "path": "CHANGES",
    "chars": 46901,
    "preview": "This file contains only the changes history before redis-py version 4.0.0\nAfter redis-py version 4.0.0 all changes can b"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 5940,
    "preview": "# Contributing\n\n## Introduction\n\nWe appreciate your interest in considering contributing to redis-py.\nCommunity contribu"
  },
  {
    "path": "LICENSE",
    "chars": 1074,
    "preview": "MIT License\n\nCopyright (c) 2022-2023, Redis, inc.\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "README.md",
    "chars": 10278,
    "preview": "# redis-py\n\nThe Python interface to the Redis key-value store.\n\n[![CI](https://github.com/redis/redis-py/workflows/CI/ba"
  },
  {
    "path": "benchmarks/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "benchmarks/base.py",
    "chars": 1355,
    "preview": "import functools\nimport itertools\nimport sys\nimport timeit\n\nimport redis\n\n\nclass Benchmark:\n    ARGUMENTS = ()\n\n    def "
  },
  {
    "path": "benchmarks/basic_operations.py",
    "chars": 4866,
    "preview": "import time\nfrom argparse import ArgumentParser\nfrom functools import wraps\n\nimport redis\n\n\ndef parse_args():\n    parser"
  },
  {
    "path": "benchmarks/cluster_async.py",
    "chars": 7234,
    "preview": "import asyncio\nimport functools\nimport time\n\nimport aioredis_cluster\nimport aredis\nimport uvloop\n\nimport redis.asyncio a"
  },
  {
    "path": "benchmarks/cluster_async_pipeline.py",
    "chars": 2597,
    "preview": "import asyncio\nimport functools\nimport time\n\nimport aioredis_cluster\nimport aredis\nimport uvloop\n\nimport redis.asyncio a"
  },
  {
    "path": "benchmarks/command_packer_benchmark.py",
    "chars": 3273,
    "preview": "from base import Benchmark\n\nfrom redis.connection import SYM_CRLF, SYM_DOLLAR, SYM_EMPTY, SYM_STAR, Connection\n\n\nclass S"
  },
  {
    "path": "benchmarks/otel_benchmark.py",
    "chars": 47157,
    "preview": "#!/usr/bin/env python3\n\"\"\"\nOpenTelemetry Benchmark for redis-py.\n\nThis module provides benchmarking infrastructure to me"
  },
  {
    "path": "benchmarks/socket_read_size.py",
    "chars": 797,
    "preview": "from base import Benchmark\n\nfrom redis.connection import PythonParser, _HiredisParser\n\n\nclass SocketReadBenchmark(Benchm"
  },
  {
    "path": "codecov.yml",
    "chars": 208,
    "preview": "ignore:\n  - \"benchmarks/**\"\n  - \"tasks.py\"\n\ncodecov:\n  require_ci_to_pass: yes\n\ncoverage:\n  precision: 2\n  round: down\n "
  },
  {
    "path": "dev_requirements.txt",
    "chars": 1057,
    "preview": "build\nbuild==1.2.2.post1 ; platform_python_implementation == \"PyPy\"\nclick==8.0.4\ninvoke==2.2.0\npackaging>=20.4\npackaging"
  },
  {
    "path": "docker-compose.yml",
    "chars": 3772,
    "preview": "---\n# image tag 8.0-RC2-pre is the one matching the 8.0 GA release\nx-client-libs-stack-image: &client-libs-stack-image\n "
  },
  {
    "path": "dockers/sentinel.conf",
    "chars": 374,
    "preview": "sentinel resolve-hostnames yes\nsentinel monitor redis-py-test redis 6379 2\n# Be much more tolerant to transient stalls ("
  },
  {
    "path": "docs/Makefile",
    "chars": 5572,
    "preview": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD "
  },
  {
    "path": "docs/_static/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/_templates/.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/advanced_features.rst",
    "chars": 22410,
    "preview": "Advanced Features\n=================\n\nA note about threading\n----------------------\n\nRedis client instances can safely be"
  },
  {
    "path": "docs/backoff.rst",
    "chars": 87,
    "preview": ".. _backoff-label:\n\nBackoff\n#############\n\n.. automodule:: redis.backoff\n    :members: "
  },
  {
    "path": "docs/clustering.rst",
    "chars": 10995,
    "preview": "Clustering\n==========\n\nredis-py now supports cluster mode and provides a client for `Redis\nCluster <https://redis.io/top"
  },
  {
    "path": "docs/commands.rst",
    "chars": 959,
    "preview": "Redis Commands\n##############\n\nCore Commands\n*************\n\nThe following functions can be used to replicate their equiv"
  },
  {
    "path": "docs/conf.py",
    "chars": 9845,
    "preview": "# redis-py documentation build configuration file, created by\n# sphinx-quickstart on Fri Feb  8 00:47:08 2013.\n#\n# This "
  },
  {
    "path": "docs/connections.rst",
    "chars": 2531,
    "preview": "Connecting to Redis\n###################\n\n\nGeneric Client\n**************\n\nThis is the client used to connect directly to "
  },
  {
    "path": "docs/examples/README.md",
    "chars": 134,
    "preview": "# Examples\n\nExamples of redis-py usage go here. They're being linked to the [generated documentation](https://redis.read"
  },
  {
    "path": "docs/examples/asyncio_examples.ipynb",
    "chars": 11397,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {\n    \"collapsed\": true,\n    \"pycharm\": {\n     \"name\": \"#%%"
  },
  {
    "path": "docs/examples/connection_examples.ipynb",
    "chars": 12916,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Connection Examples\"\n   ]\n  },\n  "
  },
  {
    "path": "docs/examples/opentelemetry/README.md",
    "chars": 1195,
    "preview": "# Example for redis-py OpenTelemetry instrumentation\n\nThis example demonstrates how to monitor Redis using [OpenTelemetr"
  },
  {
    "path": "docs/examples/opentelemetry/config/alertmanager.yml",
    "chars": 1692,
    "preview": "# See https://prometheus.io/docs/alerting/latest/configuration/ for details.\n\nglobal:\n  # The smarthost and SMTP sender "
  },
  {
    "path": "docs/examples/opentelemetry/config/otel-collector.yaml",
    "chars": 1249,
    "preview": "extensions:\n  health_check:\n  pprof:\n    endpoint: 0.0.0.0:1777\n  zpages:\n    endpoint: 0.0.0.0:55679\n\nreceivers:\n  otlp"
  },
  {
    "path": "docs/examples/opentelemetry/config/vector.toml",
    "chars": 923,
    "preview": "[sources.syslog_logs]\ntype = \"demo_logs\"\nformat = \"syslog\"\ninterval = 0.1\n\n[sources.apache_common_logs]\ntype = \"demo_log"
  },
  {
    "path": "docs/examples/opentelemetry/docker-compose.yml",
    "chars": 1752,
    "preview": "version: \"3\"\n\nservices:\n  clickhouse:\n    image: clickhouse/clickhouse-server:22.7\n    restart: on-failure\n    environme"
  },
  {
    "path": "docs/examples/opentelemetry/main.py",
    "chars": 1277,
    "preview": "#!/usr/bin/env python3\n\nimport time\n\nimport redis\nimport uptrace\nfrom opentelemetry import trace\nfrom opentelemetry.inst"
  },
  {
    "path": "docs/examples/opentelemetry/requirements.txt",
    "chars": 73,
    "preview": "redis==4.3.4\nuptrace==1.14.0\nopentelemetry-instrumentation-redis==0.35b0\n"
  },
  {
    "path": "docs/examples/opentelemetry/uptrace.yml",
    "chars": 8596,
    "preview": "##\n## Uptrace configuration file.\n## See https://uptrace.dev/get/config.html for details.\n##\n## You can use environment "
  },
  {
    "path": "docs/examples/opentelemetry_api_examples.ipynb",
    "chars": 14417,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"id\": \"7b02ea52\",\n   \"metadata\": {},\n   \"source\": [\n    \"# OpenTelemetr"
  },
  {
    "path": "docs/examples/pipeline_examples.ipynb",
    "chars": 8549,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Pipeline examples\\n\",\n    \"\\n\",\n "
  },
  {
    "path": "docs/examples/redis-stream-example.ipynb",
    "chars": 19064,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Redis Stream Examples\"\n   ]\n  },\n"
  },
  {
    "path": "docs/examples/search_json_examples.ipynb",
    "chars": 7989,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Indexing / querying JSON document"
  },
  {
    "path": "docs/examples/search_vector_similarity_examples.ipynb",
    "chars": 53727,
    "preview": "{\n \"cells\": [\n  {\n   \"attachments\": {},\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Vector Simi"
  },
  {
    "path": "docs/examples/set_and_get_examples.ipynb",
    "chars": 5495,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"id\": \"a3b456e8\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Basic ```set"
  },
  {
    "path": "docs/examples/ssl_connection_examples.ipynb",
    "chars": 7805,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# SSL Connection Examples\"\n   ]\n  }"
  },
  {
    "path": "docs/examples/timeseries_examples.ipynb",
    "chars": 14243,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Timeseries\\n\",\n    \"\\n\",\n    \"`re"
  },
  {
    "path": "docs/examples.rst",
    "chars": 402,
    "preview": "Examples\n########\n\n.. toctree::\n   :maxdepth: 3\n   :glob:\n\n   examples/connection_examples\n   examples/ssl_connection_ex"
  },
  {
    "path": "docs/exceptions.rst",
    "chars": 92,
    "preview": ".. _exceptions-label:\n\nExceptions\n##########\n\n.. automodule:: redis.exceptions\n    :members:"
  },
  {
    "path": "docs/genindex.rst",
    "chars": 25,
    "preview": "Module Index\n============"
  },
  {
    "path": "docs/geographic_failover.rst",
    "chars": 20468,
    "preview": "Client-side geographic failover (Active-Active)\n=====================================\n\nThe multi-database client allows "
  },
  {
    "path": "docs/index.rst",
    "chars": 2020,
    "preview": ".. redis-py documentation master file, created by\n   sphinx-quickstart on Thu Jul 28 13:55:57 2011.\n   You can adapt thi"
  },
  {
    "path": "docs/lock.rst",
    "chars": 57,
    "preview": "Lock\n#########\n\n.. automodule:: redis.lock\n    :members: "
  },
  {
    "path": "docs/lua_scripting.rst",
    "chars": 3950,
    "preview": "Lua Scripting\n===\n\n`Lua Scripting <#lua-scripting-in-default-connections>`__ \\|\n`Pipelines <#pipelines>`__ \\| `Cluster m"
  },
  {
    "path": "docs/opentelemetry.rst",
    "chars": 14735,
    "preview": "Integrating OpenTelemetry\n=========================\n\nWhat is OpenTelemetry?\n----------------------\n\n`OpenTelemetry <http"
  },
  {
    "path": "docs/redismodules.rst",
    "chars": 3227,
    "preview": "Redis Modules Commands\n######################\n\nAccessing redis module commands requires the installation of the supporte"
  },
  {
    "path": "docs/requirements.txt",
    "chars": 100,
    "preview": "sphinx>=5.0,<7.0\ndocutils<0.18\nnbsphinx\nsphinx_gallery\nipython\nsphinx-autodoc-typehints\nfuro\npandoc\n"
  },
  {
    "path": "docs/resp3_features.rst",
    "chars": 3992,
    "preview": "RESP 3 Features\n===============\n\nAs of version 5.0, redis-py supports the `RESP 3 standard <https://github.com/redis/red"
  },
  {
    "path": "docs/retry.rst",
    "chars": 3454,
    "preview": "Retry Helpers\n#############\n\n.. automodule:: redis.retry\n    :members:\n\n\nRetry in Redis Standalone\n*********************"
  },
  {
    "path": "doctests/README.md",
    "chars": 1230,
    "preview": "# Command examples for redis.io\n\n## How to add an example\n\nCreate regular python file in the current folder with meaning"
  },
  {
    "path": "doctests/cmds_cnxmgmt.py",
    "chars": 752,
    "preview": "# EXAMPLE: cmds_cnxmgmt\n# HIDE_START\nimport redis\n\nr = redis.Redis(decode_responses=True)\n# HIDE_END\n\n# STEP_START auth1"
  },
  {
    "path": "doctests/cmds_generic.py",
    "chars": 3265,
    "preview": "# EXAMPLE: cmds_generic\n# HIDE_START\nimport redis\n\nr = redis.Redis(decode_responses=True)\n# HIDE_END\n\n# STEP_START del\nr"
  },
  {
    "path": "doctests/cmds_hash.py",
    "chars": 1680,
    "preview": "# EXAMPLE: cmds_hash\n# HIDE_START\nimport redis\n\nr = redis.Redis(host=\"localhost\", port=6379, db=0, decode_responses=True"
  },
  {
    "path": "doctests/cmds_list.py",
    "chars": 2384,
    "preview": "# EXAMPLE: cmds_list\n# HIDE_START\nimport redis\n\nr = redis.Redis(decode_responses=True)\n# HIDE_END\n\n# STEP_START lpush\nre"
  },
  {
    "path": "doctests/cmds_servermgmt.py",
    "chars": 510,
    "preview": "# EXAMPLE: cmds_servermgmt\n# HIDE_START\nimport redis\n\nr = redis.Redis(decode_responses=True)\n# HIDE_END\n\n# STEP_START fl"
  },
  {
    "path": "doctests/cmds_set.py",
    "chars": 632,
    "preview": "# EXAMPLE: cmds_set\n# HIDE_START\nimport redis\n\nr = redis.Redis(decode_responses=True)\n# HIDE_END\n\n# STEP_START sadd\nres1"
  },
  {
    "path": "doctests/cmds_sorted_set.py",
    "chars": 1765,
    "preview": "# EXAMPLE: cmds_sorted_set\n# HIDE_START\nimport redis\n\nr = redis.Redis(host=\"localhost\", port=6379, db=0, decode_response"
  },
  {
    "path": "doctests/cmds_string.py",
    "chars": 319,
    "preview": "# EXAMPLE: cmds_string\n# HIDE_START\nimport redis\n\nr = redis.Redis(host=\"localhost\", port=6379, db=0, decode_responses=Tr"
  },
  {
    "path": "doctests/data/query_em.json",
    "chars": 7035,
    "preview": "[\n  {\n\t  \"pickup_zone\": \"POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, -74.0610 40.6678, -74.0610 40.75"
  },
  {
    "path": "doctests/data/query_vector.json",
    "chars": 9083,
    "preview": "[\n  {\n    \"model\": \"Jigger\",\n    \"brand\": \"Velorim\",\n    \"price\": 270,\n    \"type\": \"Kids bikes\",\n    \"specs\": {\n      \"m"
  },
  {
    "path": "doctests/dt_bitfield.py",
    "chars": 746,
    "preview": "# EXAMPLE: bitfield_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Bitfield doc pages:\n    https://redis.io/docs/latest/deve"
  },
  {
    "path": "doctests/dt_bitmap.py",
    "chars": 3435,
    "preview": "# EXAMPLE: bitmap_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Bitmap doc pages:\n    https://redis.io/docs/latest/develop/"
  },
  {
    "path": "doctests/dt_bloom.py",
    "chars": 881,
    "preview": "# EXAMPLE: bf_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Bloom filter doc pages:\n    https://redis.io/docs/latest/develo"
  },
  {
    "path": "doctests/dt_cms.py",
    "chars": 821,
    "preview": "# EXAMPLE: cms_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Count-min sketch doc pages:\n    https://redis.io/docs/latest/d"
  },
  {
    "path": "doctests/dt_cuckoo.py",
    "chars": 791,
    "preview": "# EXAMPLE: cuckoo_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Cuckoo filter doc pages:\n    https://redis.io/docs/latest/d"
  },
  {
    "path": "doctests/dt_geo.py",
    "chars": 967,
    "preview": "# EXAMPLE: geo_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Geospatial doc pages:\n    https://redis.io/docs/latest/develop"
  },
  {
    "path": "doctests/dt_hash.py",
    "chars": 1975,
    "preview": "# EXAMPLE: hash_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Hash doc pages:\n    https://redis.io/docs/latest/develop/data"
  },
  {
    "path": "doctests/dt_hll.py",
    "chars": 744,
    "preview": "# # EXAMPLE: hll_tutorial\n# HIDE_START\n\"\"\"\nCode samples for HyperLogLog doc pages:\n    https://redis.io/docs/latest/deve"
  },
  {
    "path": "doctests/dt_json.py",
    "chars": 11265,
    "preview": "# EXAMPLE: json_tutorial\n# HIDE_START\n\"\"\"\nCode samples for JSON doc pages:\n    https://redis.io/docs/latest/develop/data"
  },
  {
    "path": "doctests/dt_list.py",
    "chars": 6857,
    "preview": "# EXAMPLE: list_tutorial\n# HIDE_START\n\"\"\"\nCode samples for List doc pages:\n    https://redis.io/docs/latest/develop/data"
  },
  {
    "path": "doctests/dt_set.py",
    "chars": 4168,
    "preview": "# EXAMPLE: sets_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Set doc pages:\n    https://redis.io/docs/latest/develop/data-"
  },
  {
    "path": "doctests/dt_ss.py",
    "chars": 2673,
    "preview": "# EXAMPLE: ss_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Sorted set doc pages:\n    https://redis.io/docs/latest/develop/"
  },
  {
    "path": "doctests/dt_stream.py",
    "chars": 10190,
    "preview": "# EXAMPLE: stream_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Stream doc pages:\n    https://redis.io/docs/latest/develop/"
  },
  {
    "path": "doctests/dt_string.py",
    "chars": 1169,
    "preview": "# EXAMPLE: set_tutorial\n# HIDE_START\n\"\"\"\nCode samples for String doc pages:\n    https://redis.io/docs/latest/develop/dat"
  },
  {
    "path": "doctests/dt_tdigest.py",
    "chars": 1524,
    "preview": "# EXAMPLE: tdigest_tutorial\n# HIDE_START\n\"\"\"\nCode samples for t-digest pages:\n    https://redis.io/docs/latest/develop/d"
  },
  {
    "path": "doctests/dt_time_series.py",
    "chars": 12577,
    "preview": "# EXAMPLE: time_series_tutorial\n# HIDE_START\n\"\"\"\nCode samples for time series page:\n    https://redis.io/docs/latest/dev"
  },
  {
    "path": "doctests/dt_topk.py",
    "chars": 803,
    "preview": "# EXAMPLE: topk_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Top-K pages:\n    https://redis.io/docs/latest/develop/data-ty"
  },
  {
    "path": "doctests/dt_vec_set.py",
    "chars": 6200,
    "preview": "# EXAMPLE: vecset_tutorial\n# HIDE_START\n\"\"\"\nCode samples for Vector set doc pages:\n    https://redis.io/docs/latest/deve"
  },
  {
    "path": "doctests/geo_index.py",
    "chars": 3737,
    "preview": "# EXAMPLE: geoindex\nimport redis\nfrom redis.commands.json.path import Path\nfrom redis.commands.search.field import TextF"
  },
  {
    "path": "doctests/home_json.py",
    "chars": 4568,
    "preview": "# EXAMPLE: py_home_json\n# BINDER_ID python-py_home_json\n\"\"\"\nJSON examples from redis-py \"home\" page\"\n https://redis.io/d"
  },
  {
    "path": "doctests/home_prob_dts.py",
    "chars": 5583,
    "preview": "# EXAMPLE: home_prob_dts\n\"\"\"\nProbabilistic data type examples:\n https://redis.io/docs/latest/develop/connect/clients/pyt"
  },
  {
    "path": "doctests/query_agg.py",
    "chars": 3378,
    "preview": "# EXAMPLE: query_agg\n# HIDE_START\nimport json\nimport redis\nfrom redis.commands.json.path import Path\nfrom redis.commands"
  },
  {
    "path": "doctests/query_combined.py",
    "chars": 3108,
    "preview": "# EXAMPLE: query_combined\n# HIDE_START\nimport json\nimport numpy as np\nimport redis\nimport warnings\nfrom redis.commands.j"
  },
  {
    "path": "doctests/query_em.py",
    "chars": 2694,
    "preview": "# EXAMPLE: query_em\n# HIDE_START\nimport json\nimport redis\nfrom redis.commands.json.path import Path\nfrom redis.commands."
  },
  {
    "path": "doctests/query_ft.py",
    "chars": 3368,
    "preview": "# EXAMPLE: query_ft\n# HIDE_START\nimport json\nimport sys\nimport redis\nfrom redis.commands.json.path import Path\nfrom redi"
  },
  {
    "path": "doctests/query_geo.py",
    "chars": 1914,
    "preview": "# EXAMPLE: query_geo\n# HIDE_START\nimport json\nimport sys\nimport redis\nfrom redis.commands.json.path import Path\nfrom red"
  },
  {
    "path": "doctests/query_range.py",
    "chars": 1958,
    "preview": "# EXAMPLE: query_range\n# HIDE_START\nimport json\nimport sys\nimport redis\nfrom redis.commands.json.path import Path\nfrom r"
  },
  {
    "path": "doctests/requirements.txt",
    "chars": 90,
    "preview": "numpy\npandas\nrequests\nsentence_transformers\ntabulate\nredis #install latest stable version\n"
  },
  {
    "path": "doctests/run_examples.sh",
    "chars": 243,
    "preview": "#!/bin/sh\n\n\nbasepath=`readlink -f $1`\nif [ $? -ne 0 ]; then\nbasepath=`readlink -f $(dirname $0)`\nfi\necho \"No path specif"
  },
  {
    "path": "doctests/search_quickstart.py",
    "chars": 13016,
    "preview": "# EXAMPLE: search_quickstart\n# HIDE_START\n\"\"\"\nCode samples for document database quickstart pages:\n    https://redis.io/"
  },
  {
    "path": "doctests/search_vss.py",
    "chars": 9300,
    "preview": "# EXAMPLE: search_vss\n# HIDE_START\n\"\"\"\nCode samples for vector database quickstart pages:\n    https://redis.io/docs/late"
  },
  {
    "path": "doctests/string_set_get.py",
    "chars": 474,
    "preview": "# EXAMPLE: set_and_get\n# HIDE_START\n\"\"\"\nCode samples for data structure store quickstart pages:\n    https://redis.io/doc"
  },
  {
    "path": "doctests/trans_pipe.py",
    "chars": 2540,
    "preview": "# EXAMPLE: pipe_trans_tutorial\n# HIDE_START\n\"\"\"\nCode samples for vector database quickstart pages:\n    https://redis.io/"
  },
  {
    "path": "pyproject.toml",
    "chars": 4069,
    "preview": "[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"redis\"\ndynamic = [\"version\""
  },
  {
    "path": "redis/__init__.py",
    "chars": 2108,
    "preview": "from redis import asyncio  # noqa\nfrom redis.backoff import default_backoff\nfrom redis.client import Redis, StrictRedis\n"
  },
  {
    "path": "redis/_parsers/__init__.py",
    "chars": 693,
    "preview": "from .base import (\n    AsyncPushNotificationsParser,\n    BaseParser,\n    PushNotificationsParser,\n    _AsyncRESPBase,\n)"
  },
  {
    "path": "redis/_parsers/base.py",
    "chars": 20226,
    "preview": "import logging\nimport sys\nfrom abc import ABC\nfrom asyncio import IncompleteReadError, StreamReader, TimeoutError\nfrom t"
  },
  {
    "path": "redis/_parsers/commands.py",
    "chars": 28493,
    "preview": "from enum import Enum\nfrom typing import TYPE_CHECKING, Any, Awaitable, Dict, Optional, Tuple, Union\n\nfrom redis.excepti"
  },
  {
    "path": "redis/_parsers/encoders.py",
    "chars": 1734,
    "preview": "from ..exceptions import DataError\n\n\nclass Encoder:\n    \"Encode strings to bytes-like and decode bytes-like to strings\"\n"
  },
  {
    "path": "redis/_parsers/helpers.py",
    "chars": 31453,
    "preview": "import datetime\n\nfrom redis.utils import str_if_bytes\n\n\ndef timestamp_to_datetime(response):\n    \"Converts a unix timest"
  },
  {
    "path": "redis/_parsers/hiredis.py",
    "chars": 11201,
    "preview": "import asyncio\nimport socket\nimport sys\nfrom logging import getLogger\nfrom typing import Callable, List, Optional, Typed"
  },
  {
    "path": "redis/_parsers/resp2.py",
    "chars": 5031,
    "preview": "from typing import Any, Union\n\nfrom ..exceptions import ConnectionError, InvalidResponse, ResponseError\nfrom ..typing im"
  },
  {
    "path": "redis/_parsers/resp3.py",
    "chars": 10899,
    "preview": "from logging import getLogger\nfrom typing import Any, Union\n\nfrom ..exceptions import ConnectionError, InvalidResponse, "
  },
  {
    "path": "redis/_parsers/socket.py",
    "chars": 5520,
    "preview": "import errno\nimport io\nimport socket\nfrom io import SEEK_END\nfrom typing import Optional, Union\n\nfrom ..exceptions impor"
  },
  {
    "path": "redis/asyncio/__init__.py",
    "chars": 1489,
    "preview": "from redis.asyncio.client import Redis, StrictRedis\nfrom redis.asyncio.cluster import RedisCluster\nfrom redis.asyncio.co"
  },
  {
    "path": "redis/asyncio/client.py",
    "chars": 77148,
    "preview": "import asyncio\nimport copy\nimport inspect\nimport re\nimport time\nimport warnings\nfrom typing import (\n    TYPE_CHECKING,\n"
  },
  {
    "path": "redis/asyncio/cluster.py",
    "chars": 114542,
    "preview": "import asyncio\nimport collections\nimport random\nimport socket\nimport threading\nimport time\nimport warnings\nfrom abc impo"
  },
  {
    "path": "redis/asyncio/connection.py",
    "chars": 63681,
    "preview": "import asyncio\nimport copy\nimport enum\nimport inspect\nimport socket\nimport sys\nimport time\nimport warnings\nimport weakre"
  },
  {
    "path": "redis/asyncio/http/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "redis/asyncio/http/http_client.py",
    "chars": 7947,
    "preview": "import asyncio\nfrom abc import ABC, abstractmethod\nfrom concurrent.futures import ThreadPoolExecutor\nfrom typing import "
  },
  {
    "path": "redis/asyncio/lock.py",
    "chars": 13249,
    "preview": "import asyncio\nimport logging\nimport threading\nimport uuid\nfrom types import SimpleNamespace\nfrom typing import TYPE_CHE"
  },
  {
    "path": "redis/asyncio/multidb/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "redis/asyncio/multidb/client.py",
    "chars": 21532,
    "preview": "import asyncio\nimport logging\nfrom typing import Any, Awaitable, Callable, List, Literal, Optional, Union\n\nfrom redis.as"
  },
  {
    "path": "redis/asyncio/multidb/command_executor.py",
    "chars": 12390,
    "preview": "from abc import abstractmethod\nfrom asyncio import iscoroutinefunction\nfrom datetime import datetime\nfrom typing import "
  },
  {
    "path": "redis/asyncio/multidb/config.py",
    "chars": 9662,
    "preview": "from dataclasses import dataclass, field\nfrom enum import Enum\nfrom typing import List, Optional, Type, Union\n\nimport py"
  },
  {
    "path": "redis/asyncio/multidb/database.py",
    "chars": 1931,
    "preview": "from abc import abstractmethod\nfrom typing import Optional, Union\n\nfrom redis.asyncio import Redis, RedisCluster\nfrom re"
  },
  {
    "path": "redis/asyncio/multidb/event.py",
    "chars": 2788,
    "preview": "from typing import List\n\nfrom redis.asyncio import Redis\nfrom redis.asyncio.multidb.database import AsyncDatabase\nfrom r"
  },
  {
    "path": "redis/asyncio/multidb/failover.py",
    "chars": 3635,
    "preview": "import time\nfrom abc import ABC, abstractmethod\n\nfrom redis.asyncio.multidb.database import AsyncDatabase, Databases\nfro"
  },
  {
    "path": "redis/asyncio/multidb/failure_detector.py",
    "chars": 1263,
    "preview": "from abc import ABC, abstractmethod\n\nfrom redis.multidb.failure_detector import FailureDetector\n\n\nclass AsyncFailureDete"
  },
  {
    "path": "redis/asyncio/multidb/healthcheck.py",
    "chars": 17774,
    "preview": "import asyncio\nimport inspect\nimport logging\nfrom abc import ABC, abstractmethod\nfrom enum import Enum\nfrom typing impor"
  },
  {
    "path": "redis/asyncio/observability/__init__.py",
    "chars": 439,
    "preview": "\"\"\"\nAsync observability module for Redis async clients.\n\nThis module provides async-safe APIs for recording Redis metric"
  },
  {
    "path": "redis/asyncio/observability/recorder.py",
    "chars": 18234,
    "preview": "\"\"\"\nAsync-compatible API for recording observability metrics.\n\nThis module provides an async-safe interface for Redis as"
  },
  {
    "path": "redis/asyncio/retry.py",
    "chars": 2515,
    "preview": "from asyncio import sleep\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Awaitable,\n    Callable,\n    Optional,\n  "
  },
  {
    "path": "redis/asyncio/sentinel.py",
    "chars": 14868,
    "preview": "import asyncio\nimport random\nimport weakref\nfrom typing import AsyncIterator, Iterable, Mapping, Optional, Sequence, Tup"
  },
  {
    "path": "redis/asyncio/utils.py",
    "chars": 744,
    "preview": "from typing import TYPE_CHECKING, Any\n\nif TYPE_CHECKING:\n    from redis.asyncio.client import Pipeline, Redis\n\n\ndef from"
  },
  {
    "path": "redis/auth/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "redis/auth/err.py",
    "chars": 694,
    "preview": "from typing import Iterable\n\n\nclass RequestTokenErr(Exception):\n    \"\"\"\n    Represents an exception during token request"
  },
  {
    "path": "redis/auth/idp.py",
    "chars": 631,
    "preview": "from abc import ABC, abstractmethod\n\nfrom redis.auth.token import TokenInterface\n\n\"\"\"\nThis interface is the facade of an"
  },
  {
    "path": "redis/auth/token.py",
    "chars": 3317,
    "preview": "from abc import ABC, abstractmethod\nfrom datetime import datetime, timezone\n\nfrom redis.auth.err import InvalidTokenSche"
  },
  {
    "path": "redis/auth/token_manager.py",
    "chars": 12388,
    "preview": "import asyncio\nimport logging\nimport threading\nfrom datetime import datetime, timezone\nfrom time import sleep\nfrom typin"
  },
  {
    "path": "redis/background.py",
    "chars": 16982,
    "preview": "import asyncio\nimport logging\nimport threading\nfrom typing import Any, Callable, Coroutine\n\n\nclass BackgroundScheduler:\n"
  },
  {
    "path": "redis/backoff.py",
    "chars": 5285,
    "preview": "import random\nfrom abc import ABC, abstractmethod\n\n# Maximum backoff between each retry in seconds\nDEFAULT_CAP = 0.512\n#"
  },
  {
    "path": "redis/cache.py",
    "chars": 12089,
    "preview": "from abc import ABC, abstractmethod\nfrom collections import OrderedDict\nfrom dataclasses import dataclass\nfrom enum impo"
  },
  {
    "path": "redis/client.py",
    "chars": 80204,
    "preview": "import copy\nimport logging\nimport re\nimport threading\nimport time\nfrom itertools import chain\nfrom typing import (\n    T"
  },
  {
    "path": "redis/cluster.py",
    "chars": 161409,
    "preview": "import logging\nimport random\nimport socket\nimport sys\nimport threading\nimport time\nfrom abc import ABC, abstractmethod\nf"
  },
  {
    "path": "redis/commands/__init__.py",
    "chars": 576,
    "preview": "from .cluster import READ_COMMANDS, AsyncRedisClusterCommands, RedisClusterCommands\nfrom .core import AsyncCoreCommands,"
  },
  {
    "path": "redis/commands/bf/__init__.py",
    "chars": 8019,
    "preview": "from redis._parsers.helpers import bool_ok\n\nfrom ..helpers import get_protocol_version, parse_to_list\nfrom .commands imp"
  },
  {
    "path": "redis/commands/bf/commands.py",
    "chars": 21283,
    "preview": "from redis.client import NEVER_DECODE\nfrom redis.utils import deprecated_function\n\nBF_RESERVE = \"BF.RESERVE\"\nBF_ADD = \"B"
  },
  {
    "path": "redis/commands/bf/info.py",
    "chars": 3355,
    "preview": "from ..helpers import nativestr\n\n\nclass BFInfo:\n    capacity = None\n    size = None\n    filterNum = None\n    insertedNum"
  },
  {
    "path": "redis/commands/cluster.py",
    "chars": 40782,
    "preview": "import asyncio\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    AsyncIterator,\n    Awaitable,\n    Dict,\n    Iterable"
  },
  {
    "path": "redis/commands/core.py",
    "chars": 371073,
    "preview": "from __future__ import annotations\n\nimport datetime\nimport hashlib\nimport inspect\n\n# Try to import the xxhash library as"
  },
  {
    "path": "redis/commands/helpers.py",
    "chars": 3152,
    "preview": "import copy\nimport random\nimport string\nfrom typing import Any, Iterable, List, Tuple\n\nimport redis\nfrom redis.typing im"
  },
  {
    "path": "redis/commands/json/__init__.py",
    "chars": 4845,
    "preview": "from json import JSONDecodeError, JSONDecoder, JSONEncoder\n\nimport redis\n\nfrom ..helpers import get_protocol_version, na"
  },
  {
    "path": "redis/commands/json/_util.py",
    "chars": 137,
    "preview": "from typing import List, Mapping, Union\n\nJsonType = Union[\n    str, int, float, bool, None, Mapping[str, \"JsonType\"], Li"
  },
  {
    "path": "redis/commands/json/commands.py",
    "chars": 15711,
    "preview": "import os\nfrom json import JSONDecodeError, loads\nfrom typing import Dict, List, Optional, Tuple, Union\n\nfrom redis.exce"
  },
  {
    "path": "redis/commands/json/decoders.py",
    "chars": 1411,
    "preview": "import copy\nimport re\n\nfrom ..helpers import nativestr\n\n\ndef bulk_of_jsons(d):\n    \"\"\"Replace serialized JSON values wit"
  },
  {
    "path": "redis/commands/json/path.py",
    "chars": 393,
    "preview": "class Path:\n    \"\"\"This class represents a path in a JSON value.\"\"\"\n\n    strPath = \"\"\n\n    @staticmethod\n    def root_pa"
  },
  {
    "path": "redis/commands/policies.py",
    "chars": 11267,
    "preview": "from abc import ABC, abstractmethod\nfrom typing import Optional\n\nfrom redis._parsers.commands import (\n    CommandPolici"
  },
  {
    "path": "redis/commands/redismodules.py",
    "chars": 2794,
    "preview": "from __future__ import annotations\n\nfrom json import JSONDecoder, JSONEncoder\nfrom typing import TYPE_CHECKING\n\nif TYPE_"
  },
  {
    "path": "redis/commands/search/__init__.py",
    "chars": 5959,
    "preview": "from typing import Literal\n\nfrom redis.client import Pipeline as RedisPipeline\n\nfrom ...asyncio.client import Pipeline a"
  },
  {
    "path": "redis/commands/search/_util.py",
    "chars": 219,
    "preview": "def to_string(s, encoding: str = \"utf-8\"):\n    if isinstance(s, str):\n        return s\n    elif isinstance(s, bytes):\n  "
  },
  {
    "path": "redis/commands/search/aggregation.py",
    "chars": 11578,
    "preview": "from typing import List, Optional, Tuple, Union\n\nfrom redis.commands.search.dialect import DEFAULT_DIALECT\n\nFIELDNAME = "
  },
  {
    "path": "redis/commands/search/commands.py",
    "chars": 44070,
    "preview": "import itertools\nimport time\nfrom typing import Any, Dict, List, Optional, Union\n\nfrom redis._parsers.helpers import pai"
  },
  {
    "path": "redis/commands/search/dialect.py",
    "chars": 105,
    "preview": "# Value for the default dialect to be used as a part of\n# Search or Aggregate query.\nDEFAULT_DIALECT = 2\n"
  },
  {
    "path": "redis/commands/search/document.py",
    "chars": 413,
    "preview": "class Document:\n    \"\"\"\n    Represents a single document in a result set\n    \"\"\"\n\n    def __init__(self, id, payload=Non"
  },
  {
    "path": "redis/commands/search/field.py",
    "chars": 5935,
    "preview": "from typing import List\n\nfrom redis import DataError\n\n\nclass Field:\n    \"\"\"\n    A class representing a field in a docume"
  },
  {
    "path": "redis/commands/search/hybrid_query.py",
    "chars": 13270,
    "preview": "from enum import Enum\nfrom typing import Any, Dict, List, Optional, Union\n\nfrom redis.utils import experimental\n\ntry:\n  "
  },
  {
    "path": "redis/commands/search/hybrid_result.py",
    "chars": 815,
    "preview": "from dataclasses import dataclass\nfrom typing import Any, Dict, List, Union\n\n\n@dataclass\nclass HybridResult:\n    \"\"\"\n   "
  },
  {
    "path": "redis/commands/search/index_definition.py",
    "chars": 2489,
    "preview": "from enum import Enum\n\n\nclass IndexType(Enum):\n    \"\"\"Enum of the currently supported index types.\"\"\"\n\n    HASH = 1\n    "
  },
  {
    "path": "redis/commands/search/profile_information.py",
    "chars": 249,
    "preview": "from typing import Any\n\n\nclass ProfileInformation:\n    \"\"\"\n    Wrapper around FT.PROFILE response\n    \"\"\"\n\n    def __ini"
  },
  {
    "path": "redis/commands/search/query.py",
    "chars": 12361,
    "preview": "from typing import List, Optional, Tuple, Union\n\nfrom redis.commands.search.dialect import DEFAULT_DIALECT\n\n\nclass Query"
  },
  {
    "path": "redis/commands/search/querystring.py",
    "chars": 7597,
    "preview": "def tags(*t):\n    \"\"\"\n    Indicate that the values should be matched to a tag field\n\n    ### Parameters\n\n    - **t**: Ta"
  },
  {
    "path": "redis/commands/search/reducers.py",
    "chars": 4220,
    "preview": "from typing import Union\n\nfrom .aggregation import Asc, Desc, Reducer, SortDirection\n\n\nclass FieldOnlyReducer(Reducer):\n"
  },
  {
    "path": "redis/commands/search/result.py",
    "chars": 2624,
    "preview": "from typing import Optional\n\nfrom ._util import to_string\nfrom .document import Document\n\n\nclass Result:\n    \"\"\"\n    Rep"
  },
  {
    "path": "redis/commands/search/suggestion.py",
    "chars": 1612,
    "preview": "from typing import Optional\n\nfrom ._util import to_string\n\n\nclass Suggestion:\n    \"\"\"\n    Represents a single suggestion"
  },
  {
    "path": "redis/commands/sentinel.py",
    "chars": 5299,
    "preview": "import warnings\n\n\nclass SentinelCommands:\n    \"\"\"\n    A class containing the commands specific to redis sentinel. This c"
  },
  {
    "path": "redis/commands/timeseries/__init__.py",
    "chars": 3450,
    "preview": "import redis\nfrom redis._parsers.helpers import bool_ok\n\nfrom ..helpers import get_protocol_version, parse_to_list\nfrom "
  },
  {
    "path": "redis/commands/timeseries/commands.py",
    "chars": 47291,
    "preview": "from typing import Dict, List, Optional, Tuple, Union\n\nfrom redis.exceptions import DataError\nfrom redis.typing import K"
  },
  {
    "path": "redis/commands/timeseries/info.py",
    "chars": 3223,
    "preview": "from ..helpers import nativestr\nfrom .utils import list_to_dict\n\n\nclass TSInfo:\n    \"\"\"\n    Hold information and statist"
  },
  {
    "path": "redis/commands/timeseries/utils.py",
    "chars": 1319,
    "preview": "from ..helpers import nativestr\n\n\ndef list_to_dict(aList):\n    return {nativestr(aList[i][0]): nativestr(aList[i][1]) fo"
  },
  {
    "path": "redis/commands/vectorset/__init__.py",
    "chars": 1968,
    "preview": "import json\nfrom typing import Literal\n\nfrom redis._parsers.helpers import pairs_to_dict\nfrom redis.commands.vectorset.u"
  }
]

// ... and 172 more files (download for full content)

About this extraction

This page contains the full source code of the redis/redis-py GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 372 files (5.7 MB), approximately 1.5M tokens, and a symbol index with 8361 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!