Skip to content

Commit d77b5f7

Browse files
committed
Import python3-prometheus-client 0.9.0 source.
1 parent a5a2354 commit d77b5f7

20 files changed

Lines changed: 192 additions & 128 deletions

.travis.yml

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,57 @@
1+
os: linux
12
dist: xenial
2-
sudo: false
33
cache:
44
directories:
55
- $HOME/.cache/pip
66

77
language: python
88

9-
matrix:
9+
jobs:
1010
include:
11-
- python: "2.6"
11+
- stage: lint
12+
name: flake8_lint
13+
script:
14+
- tox -e flake8
15+
- stage: lint
16+
name: isort_lint
17+
script:
18+
- tox -e isort
19+
- stage: test
20+
python: "2.6"
1221
env: TOXENV=py26
1322
dist: trusty
14-
- python: "2.7"
23+
- stage: test
24+
python: "2.7"
1525
env: TOXENV=py27
16-
- python: "2.7"
26+
- stage: test
27+
python: "2.7"
1728
env: TOXENV=py27-nooptionals
18-
- python: "3.4"
29+
- stage: test
30+
python: "3.4"
1931
env: TOXENV=py34
20-
- python: "3.5"
32+
- stage: test
33+
python: "3.5"
2134
env: TOXENV=py35
22-
- python: "3.6"
35+
- stage: test
36+
python: "3.6"
2337
env: TOXENV=py36
24-
- python: "3.7"
38+
- stage: test
39+
python: "3.7"
2540
env: TOXENV=py37
26-
sudo: true
27-
- python: "3.8"
41+
- stage: test
42+
python: "3.8"
2843
env: TOXENV=py38
29-
sudo: true
30-
- python: "3.8"
31-
env: TOXENV=py38-nooptionals
32-
sudo: true
33-
- python: "pypy"
44+
- stage: test
45+
python: "3.9"
46+
env: TOXENV=py39
47+
- stage: test
48+
python: "3.9"
49+
env: TOXENV=py39-nooptionals
50+
- stage: test
51+
python: "pypy"
3452
env: TOXENV=pypy
35-
- python: "pypy3"
53+
- stage: test
54+
python: "pypy3"
3655
env: TOXENV=pypy3
3756

3857
install:

CODE_OF_CONDUCT.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Prometheus Community Code of Conduct
2+
3+
Prometheus follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,17 @@ c.labels(method='get', endpoint='/').inc()
220220
c.labels(method='post', endpoint='/submit').inc()
221221
```
222222

223+
Metrics with labels are not initialized when declared, because the client can't
224+
know what values the label can have. It is recommended to initialize the label
225+
values by calling the `.labels()` method alone:
226+
227+
```python
228+
from prometheus_client import Counter
229+
c = Counter('my_requests_total', 'HTTP Failures', ['method', 'endpoint'])
230+
c.labels('get', '/')
231+
c.labels('post', '/submit')
232+
```
233+
223234
### Process Collector
224235

225236
The Python client automatically exports metrics about process CPU usage, RAM,
@@ -334,7 +345,7 @@ from prometheus_client import make_wsgi_app
334345
app = Flask(__name__)
335346

336347
# Add prometheus wsgi middleware to route /metrics requests
337-
app_dispatch = DispatcherMiddleware(app, {
348+
app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {
338349
'/metrics': make_wsgi_app()
339350
})
340351
```
@@ -344,7 +355,7 @@ Run the example web application like this
344355
```bash
345356
# Install uwsgi if you do not have it
346357
pip install uwsgi
347-
uwsgi --http 127.0.0.1:8000 --wsgi-file myapp.py --callable app_dispatch
358+
uwsgi --http 127.0.0.1:8000 --wsgi-file myapp.py --callable app
348359
```
349360

350361
Visit http://localhost:8000/metrics to see the metrics

prometheus_client/__init__.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
#!/usr/bin/python
22

3-
from . import exposition
4-
from . import gc_collector
5-
from . import metrics
6-
from . import metrics_core
7-
from . import platform_collector
8-
from . import process_collector
9-
from . import registry
3+
from . import (
4+
exposition, gc_collector, metrics, metrics_core, platform_collector,
5+
process_collector, registry,
6+
)
107

118
__all__ = ['Counter', 'Gauge', 'Summary', 'Histogram', 'Info', 'Enum']
129

prometheus_client/core.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
from .metrics import Counter, Enum, Gauge, Histogram, Info, Summary
44
from .metrics_core import (
5-
CounterMetricFamily, GaugeHistogramMetricFamily, GaugeMetricFamily, HistogramMetricFamily, InfoMetricFamily,
6-
Metric, StateSetMetricFamily, SummaryMetricFamily, UnknownMetricFamily, UntypedMetricFamily)
5+
CounterMetricFamily, GaugeHistogramMetricFamily, GaugeMetricFamily,
6+
HistogramMetricFamily, InfoMetricFamily, Metric, StateSetMetricFamily,
7+
SummaryMetricFamily, UnknownMetricFamily, UntypedMetricFamily,
8+
)
79
from .registry import CollectorRegistry, REGISTRY
810
from .samples import Exemplar, Sample, Timestamp
911

prometheus_client/exposition.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,25 @@
66
import socket
77
import sys
88
import threading
9-
from wsgiref.simple_server import make_server, WSGIServer, WSGIRequestHandler
9+
from wsgiref.simple_server import make_server, WSGIRequestHandler, WSGIServer
1010

1111
from .openmetrics import exposition as openmetrics
1212
from .registry import REGISTRY
1313
from .utils import floatToGoString
1414

1515
try:
16-
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
17-
from SocketServer import ThreadingMixIn
18-
from urllib2 import build_opener, Request, HTTPHandler
1916
from urllib import quote_plus
17+
18+
from BaseHTTPServer import BaseHTTPRequestHandler
19+
from SocketServer import ThreadingMixIn
20+
from urllib2 import build_opener, HTTPHandler, Request
2021
from urlparse import parse_qs, urlparse
2122
except ImportError:
2223
# Python 3
23-
from http.server import BaseHTTPRequestHandler, HTTPServer
24+
from http.server import BaseHTTPRequestHandler
2425
from socketserver import ThreadingMixIn
25-
from urllib.request import build_opener, Request, HTTPHandler
26-
from urllib.parse import quote_plus, parse_qs, urlparse
26+
from urllib.parse import parse_qs, quote_plus, urlparse
27+
from urllib.request import build_opener, HTTPHandler, Request
2728

2829
CONTENT_TYPE_LATEST = str('text/plain; version=0.0.4; charset=utf-8')
2930
"""Content type of the latest text format"""
@@ -362,7 +363,7 @@ def _use_gateway(method, gateway, job, registry, grouping_key, timeout, handler)
362363

363364

364365
def _escape_grouping_key(k, v):
365-
if v == "" :
366+
if v == "":
366367
# Per https://github.com/prometheus/pushgateway/pull/346.
367368
return k + "@base64", "="
368369
elif '/' in v:
@@ -381,6 +382,6 @@ def instance_ip_grouping_key():
381382

382383
try:
383384
# Python >3.5 only
384-
from .asgi import make_asgi_app
385+
from .asgi import make_asgi_app # noqa
385386
except:
386387
pass

prometheus_client/metrics_core.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ def add_sample(self, name, labels, value, timestamp=None, exemplar=None):
4242
self.samples.append(Sample(name, labels, value, timestamp, exemplar))
4343

4444
def __eq__(self, other):
45-
return (isinstance(other, Metric) and
46-
self.name == other.name and
47-
self.documentation == other.documentation and
48-
self.type == other.type and
49-
self.unit == other.unit and
50-
self.samples == other.samples)
45+
return (isinstance(other, Metric)
46+
and self.name == other.name
47+
and self.documentation == other.documentation
48+
and self.type == other.type
49+
and self.unit == other.unit
50+
and self.samples == other.samples)
5151

5252
def __repr__(self):
5353
return "Metric(%s, %s, %s, %s, %s)" % (
@@ -216,13 +216,13 @@ def add_metric(self, labels, buckets, sum_value, timestamp=None):
216216
timestamp,
217217
exemplar,
218218
))
219-
# +Inf is last and provides the count value.
220-
self.samples.append(
221-
Sample(self.name + '_count', dict(zip(self._labelnames, labels)), buckets[-1][1], timestamp))
222-
# Don't iunclude sum if there's negative buckets.
219+
# Don't include sum and thus count if there's negative buckets.
223220
if float(buckets[0][0]) >= 0 and sum_value is not None:
221+
# +Inf is last and provides the count value.
222+
self.samples.append(
223+
Sample(self.name + '_count', dict(zip(self._labelnames, labels)), buckets[-1][1], timestamp))
224224
self.samples.append(
225-
Sample(self.name + '_sum', dict(zip(self._labelnames, labels)), sum_value, timestamp))
225+
Sample(self.name + '_sum', dict(zip(self._labelnames, labels)), sum_value, timestamp))
226226

227227

228228

prometheus_client/mmap_dict.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def _read_all_values(data, used=0):
3737
if encoded_len + pos > used:
3838
raise RuntimeError('Read beyond file size detected, file is corrupted.')
3939
pos += 4
40-
encoded_key = data[pos : pos + encoded_len]
40+
encoded_key = data[pos:pos + encoded_len]
4141
padded_len = encoded_len + (8 - (encoded_len + 4) % 8)
4242
pos += padded_len
4343
value = _unpack_double(data, pos)[0]

prometheus_client/openmetrics/parser.py

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# Python 3
1616
import io as StringIO
1717

18+
1819
def text_string_to_metric_families(text):
1920
"""Parse Openmetrics text format from a unicode string.
2021
@@ -24,7 +25,7 @@ def text_string_to_metric_families(text):
2425
yield metric_family
2526

2627

27-
_CANONICAL_NUMBERS = set([i / 1000.0 for i in range(10000)] + [10.0**i for i in range(-10, 11)] + [float("inf")])
28+
_CANONICAL_NUMBERS = set([float("inf")])
2829

2930

3031
def _isUncanonicalNumber(s):
@@ -113,8 +114,8 @@ def _parse_timestamp(timestamp):
113114

114115
def _is_character_escaped(s, charpos):
115116
num_bslashes = 0
116-
while (charpos > num_bslashes and
117-
s[charpos - 1 - num_bslashes] == '\\'):
117+
while (charpos > num_bslashes
118+
and s[charpos - 1 - num_bslashes] == '\\'):
118119
num_bslashes += 1
119120
return num_bslashes % 2 == 1
120121

@@ -359,7 +360,7 @@ def _parse_remaining_text(text):
359360
exemplar = None
360361
if exemplar_labels is not None:
361362
exemplar_length = sum([len(k) + len(v) for k, v in exemplar_labels.items()])
362-
if exemplar_length > 64:
363+
if exemplar_length > 128:
363364
raise ValueError("Exmplar labels are too long: " + text)
364365
exemplar = Exemplar(
365366
exemplar_labels,
@@ -398,6 +399,12 @@ def do_checks():
398399
raise ValueError("+Inf bucket missing: " + name)
399400
if count is not None and value != count:
400401
raise ValueError("Count does not match +Inf value: " + name)
402+
if has_sum and count is None:
403+
raise ValueError("_count must be present if _sum is present: " + name)
404+
if has_gsum and count is None:
405+
raise ValueError("_gcount must be present if _gsum is present: " + name)
406+
if not (has_sum or has_gsum) and count is not None:
407+
raise ValueError("_sum/_gsum must be present if _count is present: " + name)
401408
if has_negative_buckets and has_sum:
402409
raise ValueError("Cannot have _sum with negative buckets: " + name)
403410
if not has_negative_buckets and has_negative_gsum:
@@ -413,6 +420,7 @@ def do_checks():
413420
bucket = None
414421
has_negative_buckets = False
415422
has_sum = False
423+
has_gsum = False
416424
has_negative_gsum = False
417425
value = 0
418426
group = g
@@ -432,8 +440,10 @@ def do_checks():
432440
count = s.value
433441
elif suffix in ['_sum']:
434442
has_sum = True
435-
elif suffix in ['_gsum'] and s.value < 0:
436-
has_negative_gsum = True
443+
elif suffix in ['_gsum']:
444+
has_gsum = True
445+
if s.value < 0:
446+
has_negative_gsum = True
437447

438448
if group is not None:
439449
do_checks()
@@ -452,14 +462,22 @@ def text_fd_to_metric_families(fd):
452462
allowed_names = []
453463
eof = False
454464

455-
seen_metrics = set()
465+
seen_names = set()
466+
type_suffixes = {
467+
'counter': ['_total', '_created'],
468+
'summary': ['', '_count', '_sum', '_created'],
469+
'histogram': ['_count', '_sum', '_bucket', '_created'],
470+
'gaugehistogram': ['_gcount', '_gsum', '_bucket'],
471+
'info': ['_info'],
472+
}
456473

457474
def build_metric(name, documentation, typ, unit, samples):
458-
if name in seen_metrics:
459-
raise ValueError("Duplicate metric: " + name)
460-
seen_metrics.add(name)
461475
if typ is None:
462476
typ = 'unknown'
477+
for suffix in set(type_suffixes.get(typ, []) + [""]):
478+
if name + suffix in seen_names:
479+
raise ValueError("Clashing name: " + name + suffix)
480+
seen_names.add(name + suffix)
463481
if documentation is None:
464482
documentation = ''
465483
if unit is None:
@@ -482,6 +500,9 @@ def build_metric(name, documentation, typ, unit, samples):
482500
if eof:
483501
raise ValueError("Received line after # EOF: " + line)
484502

503+
if not line:
504+
raise ValueError("Received blank line")
505+
485506
if line == '# EOF':
486507
eof = True
487508
elif line.startswith('#'):
@@ -518,14 +539,7 @@ def build_metric(name, documentation, typ, unit, samples):
518539
typ = parts[3]
519540
if typ == 'untyped':
520541
raise ValueError("Invalid TYPE for metric: " + line)
521-
allowed_names = {
522-
'counter': ['_total', '_created'],
523-
'summary': ['_count', '_sum', '', '_created'],
524-
'histogram': ['_count', '_sum', '_bucket', '_created'],
525-
'gaugehistogram': ['_gcount', '_gsum', '_bucket'],
526-
'info': ['_info'],
527-
}.get(typ, [''])
528-
allowed_names = [name + n for n in allowed_names]
542+
allowed_names = [name + n for n in type_suffixes.get(typ, [''])]
529543
elif parts[1] == 'UNIT':
530544
if unit is not None:
531545
raise ValueError("More than one UNIT for metric: " + line)
@@ -557,7 +571,7 @@ def build_metric(name, documentation, typ, unit, samples):
557571
raise ValueError("Invalid le label: " + line)
558572
if (typ == 'summary' and name == sample.name
559573
and (not (0 <= float(sample.labels.get('quantile', -1)) <= 1)
560-
or _isUncanonicalNumber(sample.labels['quantile']))):
574+
or _isUncanonicalNumber(sample.labels['quantile']))):
561575
raise ValueError("Invalid quantile label: " + line)
562576

563577
g = tuple(sorted(_group_for_sample(sample, name, typ).items()))

prometheus_client/parser.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ def _replace_escaping(s):
4646

4747
def _is_character_escaped(s, charpos):
4848
num_bslashes = 0
49-
while (charpos > num_bslashes and
50-
s[charpos - 1 - num_bslashes] == '\\'):
49+
while (charpos > num_bslashes
50+
and s[charpos - 1 - num_bslashes] == '\\'):
5151
num_bslashes += 1
5252
return num_bslashes % 2 == 1
5353

@@ -112,7 +112,7 @@ def _parse_value_and_timestamp(s):
112112
if not values:
113113
return float(s), None
114114
value = float(values[0])
115-
timestamp = (float(values[-1])/1000) if len(values) > 1 else None
115+
timestamp = (float(values[-1]) / 1000) if len(values) > 1 else None
116116
return value, timestamp
117117

118118

0 commit comments

Comments
 (0)