Repository: prometheus/client_python
Pull request #481 (2019-10-26 01:47:45)

Add __repr__ method to metric objects, make them debug friendly.
before:
![Screenshot 2019-10-25 18 44 10](https://user-images.githubusercontent.com/1268088/67612503-fd931100-f757-11e9-8a8e-0e8d8030081d.png)

after:
![Screenshot 2019-10-25 18 43 40](https://user-images.githubusercontent.com/1268088/67612502-fd931100-f757-11e9-9e50-86621bb82199.png)


Patch:
diff --git a/prometheus_client/metrics.py b/prometheus_client/metrics.py
index 7f9d9c0..218bf70 100644
--- a/prometheus_client/metrics.py
+++ b/prometheus_client/metrics.py
@@ -80,6 +80,13 @@ class MetricWrapperBase(object):
             metric.add_sample(self._name + suffix, labels, value)
         return [metric]
 
+    def __str__(self):
+        return "{0}:{1}".format(self._type, self._name)
+
+    def __repr__(self):
+        metric_type = type(self)
+        return "{0}.{1}({2})".format(metric_type.__module__, metric_type.__name__, self._name)
+
     def __init__(self,
                  name,
                  documentation,
diff --git a/prometheus_client/parser.py b/prometheus_client/parser.py
index d3c7832..1b4373e 100644
--- a/prometheus_client/parser.py
+++ b/prometheus_client/parser.py
@@ -103,15 +103,17 @@ def _parse_labels(labels_string):
 
 
 # If we have multiple values only consider the first
-def _parse_value(s):
+def _parse_value_and_timestamp(s):
     s = s.lstrip()
     separator = " "
     if separator not in s:
         separator = "\t"
-    i = s.find(separator)
-    if i == -1:
-        return s
-    return s[:i]
+    values = [value.strip() for value in s.split(separator) if value.strip()]
+    if not values:
+        return float(s), None
+    value = float(values[0])
+    timestamp = (float(values[-1])/1000) if len(values) > 1 else None
+    return value, timestamp
 
 
 def _parse_sample(text):
@@ -123,8 +125,8 @@ def _parse_sample(text):
         # We ignore the starting curly brace
         label = text[label_start + 1:label_end]
         # The value is after the label end (ignoring curly brace and space)
-        value = float(_parse_value(text[label_end + 2:]))
-        return Sample(name, _parse_labels(label), value)
+        value, timestamp = _parse_value_and_timestamp(text[label_end + 2:])
+        return Sample(name, _parse_labels(label), value, timestamp)
 
     # We don't have labels
     except ValueError:
@@ -135,8 +137,8 @@ def _parse_sample(text):
         name_end = text.index(separator)
         name = text[:name_end]
         # The value is after the name
-        value = float(_parse_value(text[name_end:]))
-        return Sample(name, {}, value)
+        value, timestamp = _parse_value_and_timestamp(text[name_end:])
+        return Sample(name, {}, value, timestamp)
 
 
 def text_fd_to_metric_families(fd):
diff --git a/setup.py b/setup.py
index 90046a4..012fcc9 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,13 @@
 from setuptools import setup
+import sys
+from os import path
+
+if sys.version_info >= (2, 7):
+    with open(path.join(path.abspath(path.dirname(__file__)), 'README.md')) as f:
+        long_description = f.read()
+else: # Assuming we don't run setup in order to publish under python 2.6
+    long_description = "NA"
+
 
 setup(
     name="prometheus_client",
@@ -6,9 +15,8 @@ setup(
     author="Brian Brazil",
     author_email="brian.brazil@robustperception.io",
     description="Python client for the Prometheus monitoring system.",
-    long_description=(
-        "See https://github.com/prometheus/client_python/blob/master/README.md"
-        " for documentation."),
+    long_description=long_description,
+    long_description_content_type='text/markdown',
     license="Apache Software License 2.0",
     keywords="prometheus monitoring instrumentation client",
     url="https://github.com/prometheus/client_python",
diff --git a/tox.ini b/tox.ini
index 1a38674..a849cd9 100644
--- a/tox.ini
+++ b/tox.ini
@@ -58,9 +58,9 @@ commands =
 
 [testenv:flake8]
 deps =
-    flake8==3.5.0
-    flake8-docstrings==1.3.0
-    flake8-import-order==0.16
+    flake8==3.7.8
+    flake8-docstrings==1.5.0
+    flake8-import-order==0.18.1
 skip_install = true
 commands =
     flake8 prometheus_client/ tests/ setup.py


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
index 1c0baed..4d658a6 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -30,9 +30,13 @@ class TestCounter(unittest.TestCase):
         self.assertEqual(1, self.registry.get_sample_value('c_total'))
         self.counter.inc(7)
         self.assertEqual(8, self.registry.get_sample_value('c_total'))
+    
+    def test_repr(self):
+        self.assertEqual(repr(self.counter), "prometheus_client.metrics.Counter(c)")
 
     def test_negative_increment_raises(self):
         self.assertRaises(ValueError, self.counter.inc, -1)
+    
 
     def test_function_decorator(self):
         @self.counter.count_exceptions(ValueError)
@@ -83,6 +87,9 @@ class TestGauge(unittest.TestCase):
     def setUp(self):
         self.registry = CollectorRegistry()
         self.gauge = Gauge('g', 'help', registry=self.registry)
+    
+    def test_repr(self):
+        self.assertEqual(repr(self.gauge), "prometheus_client.metrics.Gauge(g)")
 
     def test_gauge(self):
         self.assertEqual(0, self.registry.get_sample_value('g'))
@@ -180,6 +187,9 @@ class TestSummary(unittest.TestCase):
         self.registry = CollectorRegistry()
         self.summary = Summary('s', 'help', registry=self.registry)
 
+    def test_repr(self):
+        self.assertEqual(repr(self.summary), "prometheus_client.metrics.Summary(s)")
+
     def test_summary(self):
         self.assertEqual(0, self.registry.get_sample_value('s_count'))
         self.assertEqual(0, self.registry.get_sample_value('s_sum'))
@@ -269,6 +279,10 @@ class TestHistogram(unittest.TestCase):
         self.histogram = Histogram('h', 'help', registry=self.registry)
         self.labels = Histogram('hl', 'help', ['l'], registry=self.registry)
 
+    def test_repr(self):
+        self.assertEqual(repr(self.histogram), "prometheus_client.metrics.Histogram(h)")
+        self.assertEqual(repr(self.labels), "prometheus_client.metrics.Histogram(hl)")
+
     def test_histogram(self):
         self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '1.0'}))
         self.assertEqual(0, self.registry.get_sample_value('h_bucket', {'le': '2.5'}))
@@ -373,6 +387,10 @@ class TestInfo(unittest.TestCase):
         self.info = Info('i', 'help', registry=self.registry)
         self.labels = Info('il', 'help', ['l'], registry=self.registry)
 
+    def test_repr(self):
+        self.assertEqual(repr(self.info), "prometheus_client.metrics.Info(i)")
+        self.assertEqual(repr(self.labels), "prometheus_client.metrics.Info(il)")
+
     def test_info(self):
         self.assertEqual(1, self.registry.get_sample_value('i_info', {}))
         self.info.info({'a': 'b', 'c': 'd'})
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 0c936ba..44a3fa4 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -273,17 +273,20 @@ a{foo="b\\\\a\\z"} 2
         metric_family.add_metric(["b\\a\\z"], 2)
         self.assertEqualMetrics([metric_family], list(families))
 
-    def test_timestamps_discarded(self):
+    def test_timestamps(self):
         families = text_string_to_metric_families("""# TYPE a counter
 # HELP a help
 a{foo="bar"} 1\t000
 # TYPE b counter
 # HELP b help
 b 2  1234567890
+b 88   1234566000   
 """)
         a = CounterMetricFamily("a", "help", labels=["foo"])
-        a.add_metric(["bar"], 1)
-        b = CounterMetricFamily("b", "help", value=2)
+        a.add_metric(["bar"], 1, timestamp=0)
+        b = CounterMetricFamily("b", "help")
+        b.add_metric([], 2, timestamp=1234567.89)
+        b.add_metric([], 88, timestamp=1234566)
         self.assertEqualMetrics([a, b], list(families))
 
     @unittest.skipIf(sys.version_info < (2, 7), "Test requires Python 2.7+.")