Skip to content

Show host and db in Redis check __repr__#662

Merged
codingjoe merged 4 commits intomainfrom
issues/659/alias
Feb 23, 2026
Merged

Show host and db in Redis check __repr__#662
codingjoe merged 4 commits intomainfrom
issues/659/alias

Conversation

@codingjoe
Copy link
Copy Markdown
Owner

Fix #659

Copilot AI review requested due to automatic review settings February 23, 2026 08:50
@codingjoe codingjoe self-assigned this Feb 23, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (c141523) to head (7fefd04).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #662   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           13        13           
  Lines          749       765   +16     
=========================================
+ Hits           749       765   +16     
Flag Coverage Δ
python-3.10-django-5.2 50.58% <0.00%> (-1.09%) ⬇️
python-3.10-django-5.2-celery 55.42% <0.00%> (-1.19%) ⬇️
python-3.10-django-5.2-kafka 53.59% <0.00%> (-1.15%) ⬇️
python-3.10-django-5.2-psutil 62.09% <0.00%> (-1.33%) ⬇️
python-3.10-django-5.2-rabbitmq 53.33% <0.00%> (-1.14%) ⬇️
python-3.10-django-5.2-redis 57.38% <100.00%> (+0.91%) ⬆️
python-3.10-django-5.2-rss 69.54% <0.00%> (-1.49%) ⬇️
python-3.11-django-5.2 50.58% <0.00%> (-1.09%) ⬇️
python-3.12-django-5.2 50.58% <0.00%> (-1.09%) ⬇️
python-3.12-django-6.0 50.58% <0.00%> (-1.09%) ⬇️
python-3.13-django-5.2 50.58% <0.00%> (-1.09%) ⬇️
python-3.13-django-6.0 50.58% <0.00%> (-1.09%) ⬇️
python-3.14-django-5.2 50.85% <0.00%> (-1.10%) ⬇️
python-3.14-django-5.2-celery 55.71% <0.00%> (-1.20%) ⬇️
python-3.14-django-5.2-kafka 53.74% <0.00%> (-1.16%) ⬇️
python-3.14-django-5.2-psutil 62.41% <0.00%> (-1.35%) ⬇️
python-3.14-django-5.2-rabbitmq 53.48% <0.00%> (-1.15%) ⬇️
python-3.14-django-5.2-redis 57.68% <100.00%> (+0.90%) ⬆️
python-3.14-django-5.2-rss 70.56% <0.00%> (-1.52%) ⬇️
python-3.14-django-6.0 50.85% <0.00%> (-1.10%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #659 by implementing a custom __repr__ method for the Redis health check to display the host and database number, allowing users to distinguish between multiple Redis health check instances in the output.

Changes:

  • Added a __repr__ method to the Redis health check class that extracts host and db from the Redis client's connection pool and formats them in a readable string representation

Comment thread health_check/contrib/redis.py
Comment thread health_check/contrib/redis.py Outdated

def __repr__(self):
# include client host name and logical database number to identify them
# Create a new client for this health check request
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "Create a new client for this health check request" but this is misleading - we're in the __repr__ method which is for string representation, not for running a health check. This will confuse future maintainers about the purpose of this code.

Suggested change
# Create a new client for this health check request
# Create a client instance to derive connection details for representation

Copilot uses AI. Check for mistakes.
Comment thread health_check/contrib/redis.py
Comment thread health_check/contrib/redis.py
@codingjoe
Copy link
Copy Markdown
Owner Author

@copilot address the unresolved review comments.

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 23, 2026

@codingjoe I've opened a new pull request, #663, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 2 commits February 23, 2026 12:26
Multiple Redis health check instances are indistinguishable in health
check output. This adds `host`, `db`, and cluster node information to
`Redis.__repr__` so each instance is identifiable in JSON, text, and
feed outputs.

## Changes

- **`Redis.__repr__`**: Extracts `host` and `db` from
`connection_pool.connection_kwargs` for standard Redis clients using
EAFP; for `RedisCluster` clients, extracts startup node names
(`host:port`) from `client.startup_nodes`; falls back to default
dataclass repr for clients that don't expose these (e.g. Sentinel)
- **Tests**: Eight new non-mocked tests covering standard client,
`from_url`, deprecated `client` param, Sentinel fallback, RedisCluster
with startup node hosts, and explicit security tests ensuring passwords
and usernames are never exposed in the repr output

## Security

Credentials are never included in the repr output. The implementation
only extracts `host` and `db` keys explicitly from `connection_kwargs`,
and uses `node.name` (`host:port`) for cluster nodes. Security
regression tests enforce this for all client types:

- Standard client with `password=` kwarg
- Credentials embedded in a Redis URL (`redis://user:pass@host/db`)
- `RedisCluster` with `password=` and `username=` kwargs

## Example

```python
from redis.asyncio import Redis as RedisClient, RedisCluster
from redis.asyncio.cluster import ClusterNode
from health_check.contrib.redis import Redis

check = Redis(client_factory=lambda: RedisClient.from_url("redis://localhost:6379/1"))
repr(check)  # "Redis(client=RedisClient(host=localhost, db=1))"

# RedisCluster shows startup node host:port names
cluster_check = Redis(client_factory=lambda: RedisCluster(
    startup_nodes=[ClusterNode("node1", 7000), ClusterNode("node2", 7001)]
))
repr(cluster_check)  # "Redis(client=RedisCluster(hosts=['node1:7000', 'node2:7001']))"

# Sentinel falls back gracefully — host/db not accessible via connection_pool
sentinel_check = Redis(client_factory=lambda: Sentinel([("localhost", 26379)]).master_for("mymaster"))
repr(sentinel_check)  # "Redis()"

# Credentials are never included
secret_check = Redis(client_factory=lambda: RedisClient.from_url("redis://admin:secret@host/0"))
repr(secret_check)  # "Redis(client=RedisClient(host=host, db=0))"  — no credentials
```

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: codingjoe <1772890+codingjoe@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
@codingjoe
Copy link
Copy Markdown
Owner Author

pre-commit.ci run

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment on lines +56 to +76
def __repr__(self):
# include client host name and logical database number to identify them
if self.client_factory is not None:
client = self.client_factory()
else:
# Use the deprecated client parameter (user manages lifecycle)
client = self.client

try:
conn_kwargs = client.connection_pool.connection_kwargs
host = conn_kwargs["host"]
db = conn_kwargs["db"]
return f"Redis(client=RedisClient(host={host}, db={db}))"
except (AttributeError, KeyError):
pass

try:
hosts = [node.name for node in client.startup_nodes]
return f"Redis(client=RedisCluster(hosts={hosts!r}))"
except AttributeError:
return super().__repr__()
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repr method creates a Redis client from client_factory (line 59) but never closes it, causing a resource leak. The run() method properly closes all factory-created clients (line 127), establishing the pattern that factory-created clients must be closed.

Since repr is synchronous and clients require async cleanup (aclose()), this approach is fundamentally incompatible with proper resource management. The repr method should not create clients at all.

Consider storing connection information (host, db) during initialization or caching it on first access in an instance variable. For the deprecated client parameter, you can safely access connection details since the client lifecycle is managed externally.

Copilot uses AI. Check for mistakes.
Comment thread tests/contrib/test_redis.py
@codingjoe codingjoe merged commit 692e0dd into main Feb 23, 2026
32 checks passed
@codingjoe codingjoe deleted the issues/659/alias branch February 23, 2026 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add label to Redis check

3 participants