Skip to content

Commit a074e4f

Browse files
committed
Unify decorators and context managers.
Consistent quotes.
1 parent 3b58b5e commit a074e4f

File tree

2 files changed

+108
-92
lines changed

2 files changed

+108
-92
lines changed

prometheus_client/__init__.py

Lines changed: 87 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def unregister(self, metric):
3838
self._collectors.remove(collector)
3939

4040
def collect(self):
41-
"""Yields metrics from the collectors in the registry."""
41+
'''Yields metrics from the collectors in the registry.'''
4242
collectors = None
4343
with self._lock:
4444
collectors = copy.copy(self._collectors)
@@ -47,10 +47,10 @@ def collect(self):
4747
yield metric
4848

4949
def get_sample_value(self, name, labels=None):
50-
"""Returns the sample value, or None if not found.
50+
'''Returns the sample value, or None if not found.
5151
5252
This is inefficient, and intended only for use in unittests.
53-
"""
53+
'''
5454
if labels is None:
5555
labels = {}
5656
for metric in self.collect():
@@ -71,7 +71,7 @@ def __init__(self, name, documentation, typ):
7171
self._name = name
7272
self._documentation = documentation
7373
if typ not in _METRIC_TYPES:
74-
raise ValueError("Invalid metric type: " + typ)
74+
raise ValueError('Invalid metric type: ' + typ)
7575
self._type = typ
7676
self._samples = []
7777

@@ -94,19 +94,19 @@ def __init__(self, wrappedClass, labelnames):
9494
raise InvalidLabelName(l)
9595

9696
def labels(self, *labelvalues):
97-
"""Return the child for the given labelset."""
97+
'''Return the child for the given labelset.'''
9898
if len(labelvalues) != len(self._labelnames):
99-
raise ValueError("Incorrect label count")
99+
raise ValueError('Incorrect label count')
100100
labelvalues = tuple(labelvalues)
101101
with self._lock:
102102
if labelvalues not in self._metrics:
103103
self._metrics[labelvalues] = self._wrappedClass()
104104
return self._metrics[labelvalues]
105105

106106
def remove(self, *labelvalues):
107-
"""Remove the given labelset from the metric."""
107+
'''Remove the given labelset from the metric.'''
108108
if len(labelvalues) != len(self._labelnames):
109-
raise ValueError("Incorrect label count")
109+
raise ValueError('Incorrect label count')
110110
labelvalues = tuple(labelvalues)
111111
with self._lock:
112112
del self._metrics[labelvalues]
@@ -125,9 +125,9 @@ def init(name, documentation, labelnames=(), namespace='', subsystem='', registr
125125
if labelnames:
126126
for l in labelnames:
127127
if not _METRIC_LABEL_NAME_RE.match(l):
128-
raise ValueError("Invalid label metric name: " + l)
128+
raise ValueError('Invalid label metric name: ' + l)
129129
if _RESERVED_METRIC_LABEL_NAME_RE.match(l):
130-
raise ValueError("Reserved label metric name: " + l)
130+
raise ValueError('Reserved label metric name: ' + l)
131131
collector = _LabelWrapper(cls, labelnames)
132132
else:
133133
collector = cls()
@@ -140,7 +140,7 @@ def init(name, documentation, labelnames=(), namespace='', subsystem='', registr
140140
full_name += name
141141

142142
if not _METRIC_NAME_RE.match(full_name):
143-
raise ValueError("Invalid metric name: " + full_name)
143+
raise ValueError('Invalid metric name: ' + full_name)
144144

145145
def collect():
146146
metric = Metric(full_name, documentation, cls._type)
@@ -162,28 +162,33 @@ def __init__(self):
162162
self._lock = Lock()
163163

164164
def inc(self, amount=1):
165-
"""Increment counter by the given amount."""
165+
'''Increment counter by the given amount.'''
166166
if amount < 0:
167-
raise ValueError("Counters can only be incremented by non-negative amounts.")
167+
raise ValueError('Counters can only be incremented by non-negative amounts.')
168168
with self._lock:
169169
self._value += amount
170170

171-
@contextmanager
172-
def countBlockExceptions(self):
173-
"""Decorator to increment if a block raises an exception."""
174-
try:
175-
yield
176-
except Exception, e:
177-
self.inc()
178-
raise e
179-
180-
def countFunctionExceptions(self, f):
181-
"""Decorator to increment if a function raises an exception."""
182-
@wraps(f)
183-
def wrapper(*args, **kwargs):
184-
with self.countBlockExceptions():
185-
return f(*args, **kwargs)
186-
return wrapper
171+
def countExceptions(self, exception=Exception):
172+
'''Count exceptions in a block of code or function.
173+
174+
Can be used as a function decorator or context manager.
175+
Increments the counter when an exception of the given
176+
type is raised up out of the code.
177+
'''
178+
class ExceptionCounter(object):
179+
def __init__(self, counter):
180+
self._counter = counter
181+
def __enter__(self): pass
182+
def __exit__(self, typ, value, traceback):
183+
if isinstance(value, exception):
184+
self._counter.inc()
185+
def __call__(self, f):
186+
@wraps(f)
187+
def wrapped(*args, **kwargs):
188+
with self:
189+
return f(*args, **kwargs)
190+
return wrapped
191+
return ExceptionCounter(self)
187192

188193
def _samples(self):
189194
with self._lock:
@@ -197,40 +202,45 @@ def __init__(self):
197202
self._lock = Lock()
198203

199204
def inc(self, amount=1):
200-
"""Increment gauge by the given amount."""
205+
'''Increment gauge by the given amount.'''
201206
with self._lock:
202207
self._value += amount
203208

204209
def dec(self, amount=1):
205-
"""Decrement gauge by the given amount."""
210+
'''Decrement gauge by the given amount.'''
206211
with self._lock:
207212
self._value -= amount
208213

209214
def set(self, value):
210-
"""Set gauge to the given value."""
215+
'''Set gauge to the given value.'''
211216
with self._lock:
212217
self._value = float(value)
213218

214219
def setToCurrentTime(self, value):
215-
"""Set gauge to the current unixtime."""
220+
'''Set gauge to the current unixtime.'''
216221
self.set(time.time())
217222

218-
@contextmanager
219-
def trackBlockInprogress(self):
220-
"""Decorator to track how many of a block are in progress."""
221-
self.inc()
222-
try:
223-
yield
224-
finally:
225-
self.dec()
226-
227-
def trackFunctionInprogress(self, f):
228-
"""Decorator to track how many of a function are in progress."""
229-
@wraps(f)
230-
def wrapper(*args, **kwargs):
231-
with self.trackBlockInprogress():
232-
return f(*args, **kwargs)
233-
return wrapper
223+
def trackInprogress(self):
224+
'''Track inprogress blocks of code or functions.
225+
226+
Can be used as a function decorator or context manager.
227+
Increments the gauge when the code is entered,
228+
and decrements when it is exited.
229+
'''
230+
class InprogressTracker(object):
231+
def __init__(self, gauge):
232+
self._gauge = gauge
233+
def __enter__(self):
234+
self._gauge.inc()
235+
def __exit__(self, typ, value, traceback):
236+
self._gauge.dec()
237+
def __call__(self, f):
238+
@wraps(f)
239+
def wrapped(*args, **kwargs):
240+
with self:
241+
return f(*args, **kwargs)
242+
return wrapped
243+
return InprogressTracker(self)
234244

235245
def _samples(self):
236246
with self._lock:
@@ -245,28 +255,31 @@ def __init__(self):
245255
self._lock = Lock()
246256

247257
def observe(self, amount):
248-
"""Observe the given amount."""
258+
'''Observe the given amount.'''
249259
with self._lock:
250260
self._count += 1
251261
self._sum += amount
252262

253-
@contextmanager
254-
def timeBlock(self):
255-
"""Context manager to time how long a block of code takes in seconds."""
256-
start = time.time()
257-
try:
258-
yield
259-
finally:
260-
# Time can go backwards.
261-
self.observe(max(time.time() - start, 0))
262-
263-
def timeFunction(self, f):
264-
"""Decorator to time long a function takes in seconds."""
265-
@wraps(f)
266-
def wrapper(*args, **kwargs):
267-
with self.timeBlock():
268-
return f(*args, **kwargs)
269-
return wrapper
263+
def time(self):
264+
'''Time a block of code or function, and observe the duration in seconds.
265+
266+
Can be used as a function decorator or context manager.
267+
'''
268+
class Timer(object):
269+
def __init__(self, summary):
270+
self._summary = summary
271+
def __enter__(self):
272+
self._start = time.time()
273+
def __exit__(self, typ, value, traceback):
274+
# Time can go backwards.
275+
self._summary.observe(max(time.time() - self._start, 0))
276+
def __call__(self, f):
277+
@wraps(f)
278+
def wrapped(*args, **kwargs):
279+
with self:
280+
return f(*args, **kwargs)
281+
return wrapped
282+
return Timer(self)
270283

271284
def _samples(self):
272285
with self._lock:
@@ -276,11 +289,11 @@ def _samples(self):
276289

277290

278291

279-
CONTENT_TYPE_004 = 'text-plain; version=0.0.4; charset=utf-8'
280-
'''Content type of the text format v0.0.4'''
292+
CONTENT_TYPE_LATEST = 'text-plain; version=0.0.4; charset=utf-8'
293+
'''Content type of the latest text format'''
281294

282-
def generate004(registry=REGISTRY):
283-
'''Returns the metrics from the registry in text format v0.0.4 as a string.'''
295+
def generate_latest(registry=REGISTRY):
296+
'''Returns the metrics from the registry in latest text format as a string.'''
284297
output = []
285298
for metric in registry.collect():
286299
output.append(u'# HELP %s %s' % (
@@ -290,7 +303,7 @@ def generate004(registry=REGISTRY):
290303
if labels:
291304
labelstr = u'{%s}' % ','.join(
292305
[u'%s="%s"' % (
293-
k, v.replace('\\', r'\\').replace('\n', r'\n').replace('"', r'\"'))
306+
k, v.replace('\\', r'\\').replace('\n', r'\n').replace('\'', r'\''))
294307
for k, v in labels.items()])
295308
else:
296309
labelstr = u''
@@ -301,9 +314,9 @@ def generate004(registry=REGISTRY):
301314
class MetricsHandler(BaseHTTPRequestHandler):
302315
def do_GET(self):
303316
self.send_response(200)
304-
self.send_header('Content-Type', CONTENT_TYPE_004)
317+
self.send_header('Content-Type', CONTENT_TYPE_LATEST)
305318
self.end_headers()
306-
self.wfile.write(generate004(REGISTRY))
319+
self.wfile.write(generate_latest(REGISTRY))
307320

308321

309322
if __name__ == '__main__':

tests/test_client.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import unittest
22

33
from prometheus_client import Gauge, Counter, Summary
4-
from prometheus_client import CollectorRegistry, generate004
4+
from prometheus_client import CollectorRegistry, generate_latest
55

66
class TestCounter(unittest.TestCase):
77
def setUp(self):
@@ -19,28 +19,31 @@ def test_negative_increment_raises(self):
1919
self.assertRaises(ValueError, self.counter.inc, -1)
2020

2121
def test_function_decorator(self):
22-
@self.counter.countFunctionExceptions
22+
@self.counter.countExceptions(ValueError)
2323
def f(r):
2424
if r:
25-
raise Exception
26-
f(False)
25+
raise ValueError
26+
else:
27+
raise TypeError
28+
try:
29+
f(False)
30+
except TypeError:
31+
pass
2732
self.assertEquals(0, self.registry.get_sample_value('c'))
28-
raised = False
2933
try:
3034
f(True)
31-
except:
35+
except ValueError:
3236
raised = True
33-
self.assertTrue(raised)
3437
self.assertEquals(1, self.registry.get_sample_value('c'))
3538

3639
def test_block_decorator(self):
37-
with self.counter.countBlockExceptions():
40+
with self.counter.countExceptions():
3841
pass
3942
self.assertEquals(0, self.registry.get_sample_value('c'))
4043
raised = False
4144
try:
42-
with self.counter.countBlockExceptions():
43-
raise Exception
45+
with self.counter.countExceptions():
46+
raise ValueError
4447
except:
4548
raised = True
4649
self.assertTrue(raised)
@@ -62,15 +65,15 @@ def test_gauge(self):
6265

6366
def test_function_decorator(self):
6467
self.assertEquals(0, self.registry.get_sample_value('g'))
65-
@self.gauge.trackFunctionInprogress
68+
@self.gauge.trackInprogress()
6669
def f():
6770
self.assertEquals(1, self.registry.get_sample_value('g'))
6871
f()
6972
self.assertEquals(0, self.registry.get_sample_value('g'))
7073

7174
def test_block_decorator(self):
7275
self.assertEquals(0, self.registry.get_sample_value('g'))
73-
with self.gauge.trackBlockInprogress():
76+
with self.gauge.trackInprogress():
7477
self.assertEquals(1, self.registry.get_sample_value('g'))
7578
self.assertEquals(0, self.registry.get_sample_value('g'))
7679

@@ -88,15 +91,15 @@ def test_summary(self):
8891

8992
def test_function_decorator(self):
9093
self.assertEquals(0, self.registry.get_sample_value('s_count'))
91-
@self.summary.timeFunction
94+
@self.summary.time()
9295
def f():
9396
pass
9497
f()
9598
self.assertEquals(1, self.registry.get_sample_value('s_count'))
9699

97100
def test_block_decorator(self):
98101
self.assertEquals(0, self.registry.get_sample_value('s_count'))
99-
with self.summary.timeBlock():
102+
with self.summary.time():
100103
pass
101104
self.assertEquals(1, self.registry.get_sample_value('s_count'))
102105

@@ -147,22 +150,22 @@ def setUp(self):
147150
def test_counter(self):
148151
c = Counter('cc', 'A counter', registry=self.registry)
149152
c.inc()
150-
self.assertEquals('# HELP cc A counter\n# TYPE cc counter\ncc 1.0\n', generate004(self.registry))
153+
self.assertEquals('# HELP cc A counter\n# TYPE cc counter\ncc 1.0\n', generate_latest(self.registry))
151154

152155
def test_gauge(self):
153156
g = Gauge('gg', 'A gauge', registry=self.registry)
154157
g.set(17)
155-
self.assertEquals('# HELP gg A gauge\n# TYPE gg gauge\ngg 17.0\n', generate004(self.registry))
158+
self.assertEquals('# HELP gg A gauge\n# TYPE gg gauge\ngg 17.0\n', generate_latest(self.registry))
156159

157160
def test_summary(self):
158161
s = Summary('ss', 'A summary', ['a', 'b'], registry=self.registry)
159162
s.labels('c', 'd').observe(17)
160-
self.assertEquals('# HELP ss A summary\n# TYPE ss summary\nss_count{a="c",b="d"} 1.0\nss_sum{a="c",b="d"} 17.0\n', generate004(self.registry))
163+
self.assertEquals('# HELP ss A summary\n# TYPE ss summary\nss_count{a="c",b="d"} 1.0\nss_sum{a="c",b="d"} 17.0\n', generate_latest(self.registry))
161164

162165
def test_escaping(self):
163166
c = Counter('cc', 'A\ncount\\er', ['a'], registry=self.registry)
164167
c.labels('\\x\n').inc(1)
165-
self.assertEquals('# HELP cc A\\ncount\\\\er\n# TYPE cc counter\ncc{a="\\\\x\\n"} 1.0\n', generate004(self.registry))
168+
self.assertEquals('# HELP cc A\\ncount\\\\er\n# TYPE cc counter\ncc{a="\\\\x\\n"} 1.0\n', generate_latest(self.registry))
166169

167170

168171
if __name__ == '__main__':

0 commit comments

Comments
 (0)