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"'