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(

Reply via email to