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-04-26 21:12:15
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-health-check (Old)
 and      /work/SRC/openSUSE:Factory/.python-django-health-check.new.11940 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-django-health-check"

Sun Apr 26 21:12:15 2026 rev:16 rq:1349366 version:4.4.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-django-health-check/python-django-health-check.changes
    2026-04-16 17:26:18.269788546 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-django-health-check.new.11940/python-django-health-check.changes
 2026-04-26 21:15:06.222709921 +0200
@@ -1,0 +2,9 @@
+Sun Apr 26 09:39:01 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 4.4.0:
+  * Resolve #724 -- Add public dataclass field as OpenMetric
+    label
+- update to 4.3.1:
+  * Clean up storage probe files when validation fails
+
+-------------------------------------------------------------------

Old:
----
  django-health-check-4.3.0.tar.gz

New:
----
  django-health-check-4.4.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-django-health-check.spec ++++++
--- /var/tmp/diff_new_pack.J51txr/_old  2026-04-26 21:15:07.142747379 +0200
+++ /var/tmp/diff_new_pack.J51txr/_new  2026-04-26 21:15:07.146747543 +0200
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-django-health-check
-Version:        4.3.0
+Version:        4.4.0
 Release:        0
 Summary:        Run checks on Django and is dependent services
 License:        MIT

++++++ django-health-check-4.3.0.tar.gz -> django-health-check-4.4.0.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-health-check-4.3.0/.github/copilot-instructions.md 
new/django-health-check-4.4.0/.github/copilot-instructions.md
--- old/django-health-check-4.3.0/.github/copilot-instructions.md       
2026-04-15 19:08:10.000000000 +0200
+++ new/django-health-check-4.4.0/.github/copilot-instructions.md       
2026-04-19 19:44:38.000000000 +0200
@@ -5,3 +5,36 @@
 ```console
 curl -sSL 
https://raw.githubusercontent.com/codingjoe/naming-things/refs/heads/main/README.md
 | cat
 ```
+
+Less code is more! Use the latest language features and libraries to achieve 
more with less code.
+
+Do not add new dependencies, but if you do, they must be widely adopted and 
well-maintained in the open-source community.
+
+You are a strong FOSS advocate with a preference for permissive licenses like 
BSD or MIT.
+
+Use generators instead of adding items to lists or arrays.
+
+Use class syntax for all object-oriented code.
+Use named functions instead of anonymous functions whenever possible.
+Avoid overly complex functions. Break them into smaller functions if necessary.
+Docstrings should be written in present tense imperative mood.
+They must start with a capital letter and end with a period.
+Docstrings must describe the external behavior of the function, class, or 
method.
+Docstrings should avoid redundant phrases like "This function" or "This 
method".
+Class docstrings must not repeat the class name or start with a verb since 
they don't do anything themselves.
+Avoid code comments unless they describe behavior of 3rd party code or complex 
algorithms.
+Avoid loops in favor of recursive functions or generator functions.
+Avoid functions or other code inside functions.
+Avoid if-statements in favor of switch/match-statements or polymorphism.
+Do not assign names to objects which are returned in the next line.
+
+## Python
+
+Follow PEP 8 guidelines for code style.
+EAFP (Easier to Ask Forgiveness than Permission) is preferred over LBYL (Look 
Before You Leap).
+Use type hints for all public functions, classes, and methods.
+Use dataclasses for simple data structures.
+Use context managers for resource management.
+Use list/set/dict comprehensions instead of loops for creating collections.
+Use generators for large data sets to save memory.
+Use the walrus operator (`:=`) for inline assignments when it improves 
readability.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-health-check-4.3.0/health_check/base.py 
new/django-health-check-4.4.0/health_check/base.py
--- old/django-health-check-4.3.0/health_check/base.py  2026-04-15 
19:08:10.000000000 +0200
+++ new/django-health-check-4.4.0/health_check/base.py  2026-04-19 
19:44:38.000000000 +0200
@@ -74,6 +74,17 @@
         """Return a human-readable status string, always 'OK' for the check 
itself."""
         return "OK"
 
+    @property
+    def labels(self) -> dict[str, str]:
+        """Return a human-readable label for the check, defaulting to the 
class name."""
+        return {
+            "check": self.__class__.__name__,
+        } | {
+            field.name: str(value)
+            for field in dataclasses.fields(self)
+            if field.repr and (value := getattr(self, field.name)) is not None
+        }
+
     async def get_result(self, executor: Executor | None = None) -> 
HealthCheckResult:
         loop = asyncio.get_running_loop()
         start = timeit.default_timer()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-health-check-4.3.0/health_check/checks.py 
new/django-health-check-4.4.0/health_check/checks.py
--- old/django-health-check-4.3.0/health_check/checks.py        2026-04-15 
19:08:10.000000000 +0200
+++ new/django-health-check-4.4.0/health_check/checks.py        2026-04-19 
19:44:38.000000000 +0200
@@ -274,5 +274,8 @@
         # write the file to the storage backend
         file_name = self.get_file_name()
         file_content = self.get_file_content()
-        file_name = self.check_save(file_name, file_content)
-        self.check_delete(file_name)
+        try:
+            file_name = self.check_save(file_name, file_content)
+            self.check_delete(file_name)
+        finally:
+            self.storage.delete(file_name)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-health-check-4.3.0/health_check/views.py 
new/django-health-check-4.4.0/health_check/views.py
--- old/django-health-check-4.3.0/health_check/views.py 2026-04-15 
19:08:10.000000000 +0200
+++ new/django-health-check-4.4.0/health_check/views.py 2026-04-19 
19:44:38.000000000 +0200
@@ -206,9 +206,10 @@
         """Return RSS 2.0 feed response with health check results."""
         return self._render_feed(Rss201rev2Feed)
 
-    def _escape_openmetrics_label_value(self, value):
+    @staticmethod
+    def abnf_escape(value):
         r"""
-        Escape label value according to OpenMetrics specification.
+        Escape ABNF OpenMetic labels according to RFC 5234 and RFC7405.
 
         Escapes backslashes, double quotes, and newlines as required by the 
spec:
         - Backslash (\) -> \\
@@ -217,6 +218,13 @@
         """
         return value.replace("\\", "\\\\").replace('"', '\\"').replace("\n", 
"\\n")
 
+    @staticmethod
+    def abnf_dumps(o: dict[str, str]):
+        """Return ABNF OpenMetic labels as a string suitable for use in a 
metric name."""
+        return ",".join(
+            f'{key}="{HealthCheckView.abnf_escape(value)}"' for key, value in 
o.items()
+        )
+
     def render_to_response_openmetrics(self):
         """Return OpenMetrics response with health check results."""
         lines = [
@@ -227,10 +235,9 @@
 
         # Add status metrics for each check
         for result in self.results:
-            safe_label = 
self._escape_openmetrics_label_value(repr(result.check))
             has_errors |= bool(result.error)
             lines.append(
-                f'django_health_check_status{{check="{safe_label}"}} {not 
result.error:d}'
+                
f"django_health_check_status{{{self.abnf_dumps(result.check.labels)}}} {not 
result.error:d}"
             )
 
         # Add response time metrics
@@ -241,9 +248,8 @@
         ]
 
         for result in self.results:
-            safe_label = 
self._escape_openmetrics_label_value(repr(result.check))
             lines.append(
-                
f'django_health_check_response_time_seconds{{check="{safe_label}"}} 
{result.time_taken:.6f}'
+                
f"django_health_check_response_time_seconds{{{self.abnf_dumps(result.check.labels)}}}
 {result.time_taken:.6f}"
             )
 
         # Add overall health status
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-health-check-4.3.0/tests/test_base.py 
new/django-health-check-4.4.0/tests/test_base.py
--- old/django-health-check-4.3.0/tests/test_base.py    2026-04-15 
19:08:10.000000000 +0200
+++ new/django-health-check-4.4.0/tests/test_base.py    2026-04-19 
19:44:38.000000000 +0200
@@ -1,4 +1,5 @@
 import asyncio
+import dataclasses
 from unittest.mock import MagicMock, patch
 
 import pytest
@@ -92,3 +93,19 @@
         check = SlowCheck()
         result = await check.get_result()
         assert result.time_taken > 0
+
+    def test_labels(self):
+        """Labels include class name and dataclass fields, excluding secret 
fields."""
+
+        @dataclasses.dataclass
+        class LabeledCheck(HealthCheck):
+            foo: str = "bar"
+            version: float = 1.0
+            secret_key: str = dataclasses.field(default="secret", repr=False)
+            missing_value: str | None = None
+
+            async def run(self):
+                pass
+
+        check = LabeledCheck()
+        assert check.labels == {"check": "LabeledCheck", "foo": "bar", 
"version": "1.0"}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-health-check-4.3.0/tests/test_checks.py 
new/django-health-check-4.4.0/tests/test_checks.py
--- old/django-health-check-4.3.0/tests/test_checks.py  2026-04-15 
19:08:10.000000000 +0200
+++ new/django-health-check-4.4.0/tests/test_checks.py  2026-04-19 
19:44:38.000000000 +0200
@@ -516,6 +516,25 @@
             assert "does not match" in str(result.error)
 
     @pytest.mark.asyncio
+    async def test_check_status__file_content_mismatch__cleanup(self):
+        """Ensure file is deleted even when content mismatch occurs."""
+        with mock.patch("health_check.checks.storages") as mock_storages:
+            mock_storage = mock.MagicMock()
+            mock_storages.__getitem__.return_value = mock_storage
+            mock_storage.save.return_value = "test-file.txt"
+            mock_storage.exists.return_value = True
+            mock_file = mock.MagicMock()
+            mock_file.read.return_value = b"wrong content"
+            mock_storage.open.return_value.__enter__.return_value = mock_file
+
+            check = Storage()
+            result = await check.get_result()
+            assert result.error is not None
+            assert isinstance(result.error, ServiceUnavailable)
+            assert "does not match" in str(result.error)
+            mock_storage.delete.assert_called_once()
+
+    @pytest.mark.asyncio
     async def test_check_status__service_unavailable_passthrough(self):
         """Re-raise ServiceUnavailable exceptions."""
         with mock.patch("health_check.checks.storages") as mock_storages:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-health-check-4.3.0/tests/test_views.py 
new/django-health-check-4.4.0/tests/test_views.py
--- old/django-health-check-4.3.0/tests/test_views.py   2026-04-15 
19:08:10.000000000 +0200
+++ new/django-health-check-4.4.0/tests/test_views.py   2026-04-19 
19:44:38.000000000 +0200
@@ -906,16 +906,15 @@
 
         @dataclasses.dataclass
         class CustomCheck(HealthCheck):
+            name: str = "Custom-Check.Backend Test"
+
             async def run(self):
                 pass
 
-            def __repr__(self):
-                return "Custom-Check.Backend Test"
-
         response = await health_check_view([CustomCheck], 
format_param="openmetrics")
         content = response.content.decode("utf-8")
         # Check that the label value is present (with proper escaping)
-        assert 'check="Custom-Check.Backend Test"' in content
+        assert 'check="CustomCheck",name="Custom-Check.Backend Test"' in 
content
 
     @pytest.mark.asyncio
     async def test_get__openmetrics_label_escaping(self, health_check_view):
@@ -923,12 +922,11 @@
 
         @dataclasses.dataclass
         class EscapingCheck(HealthCheck):
+            name: str = 'Test "quoted" value\\with\\backslashes\nand newlines'
+
             async def run(self):
                 pass
 
-            def __repr__(self):
-                return 'Test "quoted" value\\with\\backslashes\nand newlines'
-
         response = await health_check_view([EscapingCheck], 
format_param="openmetrics")
         content = response.content.decode("utf-8")
         # Check that special characters are properly escaped per OpenMetrics 
spec
@@ -1071,3 +1069,17 @@
         if hasattr(response, "render"):
             response.render()
         assert response.status_code == 200
+
+    def test_abnf_escape(self):
+        assert HealthCheckView.abnf_escape("simple") == "simple"
+        assert (
+            HealthCheckView.abnf_escape(r"backslash\backslash")
+            == "backslash\\\\backslash"
+        )
+        assert HealthCheckView.abnf_escape('quote"test') == 'quote\\"test'
+        assert HealthCheckView.abnf_escape("line\nbreak") == "line\\nbreak"
+
+    def test_abnf_dumps(self):
+        assert HealthCheckView.abnf_dumps({"a": "b"}) == 'a="b"'
+        assert HealthCheckView.abnf_dumps({"a": "b", "c": "d"}) == 
'a="b",c="d"'
+        assert HealthCheckView.abnf_dumps({"a": 'b"c'}) == 'a="b\\"c"'

Reply via email to