Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-django-health-check for
openSUSE:Factory checked in at 2026-08-21 17:00:02
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-health-check (Old)
and /work/SRC/openSUSE:Factory/.python-django-health-check.new.1258 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-health-check"
Fri Aug 21 17:00:02 2026 rev:18 rq:1372752 version:4.5.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-django-health-check/python-django-health-check.changes
2026-07-28 17:57:22.483351447 +0200
+++
/work/SRC/openSUSE:Factory/.python-django-health-check.new.1258/python-django-health-check.changes
2026-08-21 17:00:10.277579474 +0200
@@ -1,0 +2,9 @@
+Fri Aug 21 09:56:40 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 4.5.0:
+ * Fix #758 -- Support Django 6.1 MAILERS setting in Mail health
+ check
+- update to 4.4.4:
+ * Fix #743 -- Add redis label to JSON views
+
+-------------------------------------------------------------------
Old:
----
django-health-check-4.4.3.tar.gz
New:
----
django-health-check-4.5.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-django-health-check.spec ++++++
--- /var/tmp/diff_new_pack.HqRMyp/_old 2026-08-21 17:00:10.975604323 +0200
+++ /var/tmp/diff_new_pack.HqRMyp/_new 2026-08-21 17:00:10.976604359 +0200
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-django-health-check
-Version: 4.4.3
+Version: 4.5.0
Release: 0
Summary: Run checks on Django and is dependent services
License: MIT
++++++ django-health-check-4.4.3.tar.gz -> django-health-check-4.5.0.tar.gz
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-health-check-4.4.3/.github/workflows/ci.yml
new/django-health-check-4.5.0/.github/workflows/ci.yml
--- old/django-health-check-4.4.3/.github/workflows/ci.yml 2026-06-23
11:40:52.000000000 +0200
+++ new/django-health-check-4.5.0/.github/workflows/ci.yml 2026-08-07
17:38:08.000000000 +0200
@@ -35,11 +35,16 @@
django-version:
- "5.2"
- "6.0"
+ - "6.1"
exclude:
- python-version: "3.10"
django-version: "6.0"
- python-version: "3.11"
django-version: "6.0"
+ - python-version: "3.10"
+ django-version: "6.1"
+ - python-version: "3.11"
+ django-version: "6.1"
steps:
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-health-check-4.4.3/.pre-commit-config.yaml
new/django-health-check-4.5.0/.pre-commit-config.yaml
--- old/django-health-check-4.4.3/.pre-commit-config.yaml 2026-06-23
11:40:52.000000000 +0200
+++ new/django-health-check-4.5.0/.pre-commit-config.yaml 2026-08-07
17:38:08.000000000 +0200
@@ -18,11 +18,11 @@
- id: pyupgrade
args: [--py310-plus]
- repo: https://github.com/adamchainz/django-upgrade
- rev: 1.30.0
+ rev: 1.31.1
hooks:
- id: django-upgrade
- repo: https://github.com/djlint/djLint
- rev: v1.39.2
+ rev: v1.43.2
hooks:
- id: djlint-reformat-django
- repo: https://github.com/hukkin/mdformat
@@ -36,7 +36,7 @@
- mdformat-gfm-alerts
exclude: '.github/agents/'
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.15.18
+ rev: v0.16.1
hooks:
- id: ruff-check
args: [--fix, --exit-non-zero-on-fix]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-health-check-4.4.3/health_check/checks.py
new/django-health-check-4.5.0/health_check/checks.py
--- old/django-health-check-4.4.3/health_check/checks.py 2026-06-23
11:40:52.000000000 +0200
+++ new/django-health-check-4.5.0/health_check/checks.py 2026-08-07
17:38:08.000000000 +0200
@@ -7,9 +7,9 @@
import socket
import uuid
+import django
import dns.asyncresolver
from django import db
-from django.conf import settings
from django.core.cache import CacheKeyWarning, caches
from django.core.cache.backends.base import InvalidCacheBackendError
from django.core.files.base import ContentFile
@@ -17,6 +17,12 @@
from django.core.files.storage import Storage as DjangoStorage
from django.core.mail import get_connection
from django.core.mail.backends.base import BaseEmailBackend
+
+if django.VERSION >= (6, 1):
+ from django.core.mail import DEFAULT_MAILER_ALIAS, mailers
+else: # Django < 6.1
+ DEFAULT_MAILER_ALIAS = "default"
+
from django.db import connections
from django.db.models import Expression
from django.utils.connection import ConnectionDoesNotExist
@@ -197,18 +203,25 @@
Check that an email backend is able to open and close the connection.
Args:
- backend: The email backend to test against.
+ backend: The email backend to test against. Legacy, used only on
Django < 6.1.
+ alias: The mailer alias to test.
timeout: Timeout for connection to an email server in seconds.
"""
- backend: str = settings.EMAIL_BACKEND
+ backend: str | None = dataclasses.field(default=None, repr=False)
+ alias: str = DEFAULT_MAILER_ALIAS
timeout: datetime.timedelta = dataclasses.field(
default=datetime.timedelta(seconds=15), repr=False
)
+ def _get_connection(self) -> BaseEmailBackend:
+ if django.VERSION >= (6, 1):
+ return mailers[self.alias]
+ return get_connection(self.backend, fail_silently=False)
+
def run(self) -> None:
- connection: BaseEmailBackend = get_connection(self.backend,
fail_silently=False)
+ connection: BaseEmailBackend = self._get_connection()
connection.timeout = self.timeout.total_seconds()
logger.debug("Trying to open connection to mail backend.")
try:
@@ -221,9 +234,7 @@
raise ServiceUnavailable("Connection refused error") from e
finally:
connection.close()
- logger.debug(
- "Connection established. Mail backend %r is healthy.", self.backend
- )
+ logger.debug("Connection established. Mail backend %r is healthy.",
connection)
@dataclasses.dataclass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-health-check-4.4.3/health_check/contrib/redis.py
new/django-health-check-4.5.0/health_check/contrib/redis.py
--- old/django-health-check-4.4.3/health_check/contrib/redis.py 2026-06-23
11:40:52.000000000 +0200
+++ new/django-health-check-4.5.0/health_check/contrib/redis.py 2026-08-07
17:38:08.000000000 +0200
@@ -53,31 +53,40 @@
dataclasses.field(repr=False, default=None)
)
- 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
-
+ def _connection_kwargs(self) -> dict[str, object]:
+ """Return identifying Redis connection parameters for repr and
labels."""
+ client = (
+ self.client_factory() if self.client_factory is not None else
self.client
+ )
try:
- safe_connection_str = ", ".join(
- f"{key}={value!r}"
+ return {
+ key: value
for key, value in sorted(
client.connection_pool.connection_kwargs.items()
)
if key in {"host", "port", "db"}
- )
- return f"Redis({safe_connection_str})"
+ }
except AttributeError:
pass
-
try:
- hosts = [node.name for node in client.startup_nodes]
- return f"Redis(client=RedisCluster(hosts={hosts!r}))"
+ return {"hosts": [node.name for node in client.startup_nodes]}
except AttributeError:
- return super().__repr__()
+ return {}
+
+ def __repr__(self):
+ match self._connection_kwargs():
+ case {"hosts": hosts}:
+ return f"Redis(client=RedisCluster(hosts={hosts!r}))"
+ case kwargs if kwargs:
+ return f"Redis({', '.join(f'{key}={value!r}' for key, value in
kwargs.items())})"
+ case _:
+ return super().__repr__()
+
+ @property
+ def labels(self) -> dict[str, str]:
+ return super().labels | {
+ key: str(value) for key, value in self._connection_kwargs().items()
+ }
def __post_init__(self):
# Validate that exactly one of client or client_factory is provided
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-health-check-4.4.3/pyproject.toml
new/django-health-check-4.5.0/pyproject.toml
--- old/django-health-check-4.4.3/pyproject.toml 2026-06-23
11:40:52.000000000 +0200
+++ new/django-health-check-4.5.0/pyproject.toml 2026-08-07
17:38:08.000000000 +0200
@@ -16,6 +16,7 @@
"Framework :: Django",
"Framework :: Django :: 5.2",
"Framework :: Django :: 6.0",
+ "Framework :: Django :: 6.1",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-health-check-4.4.3/tests/contrib/test_redis.py
new/django-health-check-4.5.0/tests/contrib/test_redis.py
--- old/django-health-check-4.4.3/tests/contrib/test_redis.py 2026-06-23
11:40:52.000000000 +0200
+++ new/django-health-check-4.5.0/tests/contrib/test_redis.py 2026-08-07
17:38:08.000000000 +0200
@@ -271,6 +271,73 @@
"repr must never expose the cluster username"
)
+ def test_redis__labels_standard_client(self):
+ """Labels include host, port and db for distinct OpenMetrics
samples."""
+ from redis.asyncio import Redis as RedisClient
+
+ check = RedisHealthCheck(
+ client_factory=lambda: RedisClient(host="myhost", port=6379, db=2)
+ )
+ assert check.labels == {
+ "check": "Redis",
+ "host": "myhost",
+ "port": "6379",
+ "db": "2",
+ }
+
+ def test_redis__labels_distinct_across_clients(self):
+ """Multiple Redis checks with different connections produce distinct
labels."""
+ from redis.asyncio import Redis as RedisClient
+
+ channels = RedisHealthCheck(
+ client_factory=lambda: RedisClient.from_url("redis://cache:6379/0")
+ )
+ constance = RedisHealthCheck(
+ client_factory=lambda:
RedisClient.from_url("redis://broker:6379/1")
+ )
+ assert channels.labels != constance.labels
+ assert channels.labels["host"] == "cache"
+ assert constance.labels["host"] == "broker"
+
+ def test_redis__labels_cluster_client(self):
+ """Labels include cluster node hosts for RedisCluster clients."""
+ from redis.asyncio import RedisCluster
+ from redis.asyncio.cluster import ClusterNode
+
+ check = RedisHealthCheck(
+ client_factory=lambda: RedisCluster(
+ startup_nodes=[ClusterNode("node1", 7000),
ClusterNode("node2", 7001)]
+ )
+ )
+ assert check.labels["check"] == "Redis"
+ assert "node1:7000" in check.labels["hosts"]
+ assert "node2:7001" in check.labels["hosts"]
+
+ def test_redis__labels_excludes_password(self):
+ """Labels never leak passwords."""
+ from redis.asyncio import Redis as RedisClient
+
+ check = RedisHealthCheck(
+ client_factory=lambda: RedisClient(
+ host="myhost",
+ port=6379,
+ db=0,
+ password="supersecret", # noqa: S106
+ )
+ )
+ assert "supersecret" not in str(check.labels)
+
+ def test_redis__labels_sentinel_fallback(self):
+ """Labels fall back to the base check name for Sentinel clients."""
+ from redis.asyncio import Sentinel
+
+ check = RedisHealthCheck(
+ client_factory=lambda: Sentinel([("localhost", 26379)]).master_for(
+ "mymaster"
+ )
+ )
+ assert check.labels == {"check": "Redis"}
+
@pytest.mark.asyncio
async def test_redis__real_connection(self):
"""Ping real Redis server when REDIS_URL is configured."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-health-check-4.4.3/tests/test_checks.py
new/django-health-check-4.5.0/tests/test_checks.py
--- old/django-health-check-4.4.3/tests/test_checks.py 2026-06-23
11:40:52.000000000 +0200
+++ new/django-health-check-4.5.0/tests/test_checks.py 2026-08-07
17:38:08.000000000 +0200
@@ -4,9 +4,11 @@
import logging
from unittest import mock
+import django
import pytest
from django import db
from django.core.cache import CacheKeyWarning
+from django.test import override_settings
from health_check import Storage
from health_check.checks import DNS, Cache, Database, Mail
@@ -159,9 +161,89 @@
@pytest.mark.asyncio
async def test_run_check__locmem_backend(self):
"""Mail check completes with locmem backend."""
- check = Mail(backend="django.core.mail.backends.locmem.EmailBackend")
- result = await check.get_result()
- assert result.error is None
+ with override_settings(
+ EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend"
+ ):
+ check = Mail()
+ result = await check.get_result()
+ assert result.error is None
+
+ def test_defaults(self):
+ """Mail check defaults to backend=None and DEFAULT_MAILER_ALIAS."""
+ from health_check.checks import DEFAULT_MAILER_ALIAS
+
+ check = Mail()
+ assert check.backend is None
+ assert check.alias == DEFAULT_MAILER_ALIAS
+
+ @pytest.mark.skipif(
+ django.VERSION < (6, 1),
+ reason="mailers handler requires Django 6.1+",
+ )
+ def test_get_connection__mailers_configured(self):
+ """Use the mailers handler when available."""
+ mock_connection = mock.MagicMock()
+ mock_mailers = mock.MagicMock()
+ mock_mailers.__getitem__.return_value = mock_connection
+
+ with mock.patch("health_check.checks.mailers", mock_mailers):
+ check = Mail(alias="default")
+ connection = check._get_connection()
+
+ assert connection is mock_connection
+ mock_mailers.__getitem__.assert_called_once_with("default")
+
+ @pytest.mark.skipif(
+ django.VERSION < (6, 1),
+ reason="mailers handler requires Django 6.1+",
+ )
+ def test_get_connection__mailers_custom_alias(self):
+ """Use a custom mailer alias when available."""
+ mock_connection = mock.MagicMock()
+ mock_mailers = mock.MagicMock()
+ mock_mailers.__getitem__.return_value = mock_connection
+
+ with mock.patch("health_check.checks.mailers", mock_mailers):
+ check = Mail(alias="custom")
+ connection = check._get_connection()
+
+ assert connection is mock_connection
+ mock_mailers.__getitem__.assert_called_once_with("custom")
+
+ def test_get_connection__legacy_fallback(self):
+ """Fall back to get_connection on Django < 6.1."""
+ mock_connection = mock.MagicMock()
+
+ with (
+ mock.patch("health_check.checks.django.VERSION", (5, 2)),
+ mock.patch(
+ "health_check.checks.get_connection",
return_value=mock_connection
+ ) as mock_get_conn,
+ ):
+ check =
Mail(backend="django.core.mail.backends.locmem.EmailBackend")
+ connection = check._get_connection()
+
+ assert connection is mock_connection
+ mock_get_conn.assert_called_once_with(
+ "django.core.mail.backends.locmem.EmailBackend",
+ fail_silently=False,
+ )
+
+ @pytest.mark.skipif(
+ django.VERSION < (6, 1),
+ reason="MAILERS requires Django 6.1+",
+ )
+ @pytest.mark.asyncio
+ async def test_run_check__mailers_integration(self):
+ """Mail check completes end-to-end with MAILERS configured."""
+ with override_settings(
+ MAILERS={
+ "default": {"BACKEND":
"django.core.mail.backends.locmem.EmailBackend"}
+ }
+ ):
+ check = Mail()
+ result = await check.get_result()
+ assert result.error is None
class TestStorage:
@@ -380,7 +462,7 @@
@pytest.mark.asyncio
async def test_check_status__success(self, caplog):
"""Successfully open and close connection logs debug message."""
- with mock.patch("health_check.checks.get_connection") as
mock_get_connection:
+ with mock.patch.object(Mail, "_get_connection") as mock_get_connection:
mock_connection = mock.MagicMock()
mock_get_connection.return_value = mock_connection
mock_connection.open.return_value = None
@@ -401,7 +483,7 @@
"""Raise ServiceUnavailable when SMTPException is raised."""
import smtplib
- with mock.patch("health_check.checks.get_connection") as
mock_get_connection:
+ with mock.patch.object(Mail, "_get_connection") as mock_get_connection:
mock_connection = mock.MagicMock()
mock_get_connection.return_value = mock_connection
mock_connection.open.side_effect = smtplib.SMTPException("SMTP
error")
@@ -416,7 +498,7 @@
@pytest.mark.asyncio
async def test_check_status__connection_refused_error(self):
"""Raise ServiceUnavailable when ConnectionRefusedError is raised."""
- with mock.patch("health_check.checks.get_connection") as
mock_get_connection:
+ with mock.patch.object(Mail, "_get_connection") as mock_get_connection:
mock_connection = mock.MagicMock()
mock_get_connection.return_value = mock_connection
mock_connection.open.side_effect = ConnectionRefusedError(