From 0c4c67765a3dc3355a56008f2a0cb5cd73a55735 Mon Sep 17 00:00:00 2001 From: Om Pathak Date: Tue, 23 Jun 2026 01:54:22 -0400 Subject: [PATCH] Make clear() a no-op for label-less metrics instead of raising AttributeError Fixes #707. clear() unconditionally acquired self._lock, which is never created for metrics with no labelnames, causing an AttributeError. Per discussion in the issue (csmarchbanks, roidelapluie, sdfordham, tomprince), a label-less metric has no labelsets to clear, so clear() now treats this as a no-op rather than raising. Signed-off-by: Om Pathak --- prometheus_client/metrics.py | 2 ++ tests/test_core.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/prometheus_client/metrics.py b/prometheus_client/metrics.py index 4c53b26b..a33bac9f 100644 --- a/prometheus_client/metrics.py +++ b/prometheus_client/metrics.py @@ -254,6 +254,8 @@ def remove_by_labels(self, labels: dict[str, str]) -> None: def clear(self) -> None: """Remove all labelsets from the metric""" + if not self._labelnames: + return if 'prometheus_multiproc_dir' in os.environ or 'PROMETHEUS_MULTIPROC_DIR' in os.environ: warnings.warn( "Clearing labels has not been implemented in multi-process mode yet", diff --git a/tests/test_core.py b/tests/test_core.py index cee4bfb0..f339b6df 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -59,6 +59,12 @@ def test_reset(self): def test_repr(self): self.assertEqual(repr(self.counter), "prometheus_client.metrics.Counter(c)") + def test_clear_without_labels_is_noop(self): + self.counter.inc() + self.assertEqual(1, self.registry.get_sample_value('c_total')) + self.counter.clear() # should not raise + self.assertEqual(1, self.registry.get_sample_value('c_total')) + def test_negative_increment_raises(self): self.assertRaises(ValueError, self.counter.inc, -1)