Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-prometheus_client for 
openSUSE:Factory checked in at 2021-04-10 15:27:06
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-prometheus_client (Old)
 and      /work/SRC/openSUSE:Factory/.python-prometheus_client.new.2401 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-prometheus_client"

Sat Apr 10 15:27:06 2021 rev:12 rq:883902 version:0.10.1

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-prometheus_client/python-prometheus_client.changes
        2020-11-23 10:45:25.374298532 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-prometheus_client.new.2401/python-prometheus_client.changes
      2021-04-10 15:28:08.426432318 +0200
@@ -1,0 +2,19 @@
+Thu Apr  8 17:16:38 UTC 2021 - Michael Str??der <mich...@stroeder.com>
+
+- Update to upstream 0.10.1 release
+  * [BUGFIX] Support lowercase prometheus_multiproc_dir environment 
+    variable in mark_process_dead.
+
+-------------------------------------------------------------------
+Mon Apr  5 23:44:20 UTC 2021 - Michael Str??der <mich...@stroeder.com>
+
+- Update to upstream 0.10.0 release
+  * [CHANGE] Python 2.6 is no longer supported. #592
+  * [CHANGE] The prometheus_multiproc_dir environment variable is 
+    deprecated in favor of PROMETHEUS_MULTIPROC_DIR. #624
+  * [FEATURE] Follow redirects when pushing to Pushgateway using
+    passthrough_redirect_handler. #622
+  * [FEATURE] Metrics support a clear() method to remove all children. #642
+  * [ENHANCEMENT] Tag support in GraphiteBridge. #618
+
+-------------------------------------------------------------------

Old:
----
  v0.9.0.tar.gz

New:
----
  v0.10.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-prometheus_client.spec ++++++
--- /var/tmp/diff_new_pack.6LgICQ/_old  2021-04-10 15:28:08.782432736 +0200
+++ /var/tmp/diff_new_pack.6LgICQ/_new  2021-04-10 15:28:08.786432741 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-prometheus_client
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %bcond_without python2
 Name:           python-prometheus_client
-Version:        0.9.0
+Version:        0.10.1
 Release:        0
 Summary:        Python client for the Prometheus monitoring system
 License:        Apache-2.0

++++++ v0.9.0.tar.gz -> v0.10.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/.circleci/config.yml 
new/client_python-0.10.1/.circleci/config.yml
--- old/client_python-0.9.0/.circleci/config.yml        1970-01-01 
01:00:00.000000000 +0100
+++ new/client_python-0.10.1/.circleci/config.yml       2021-04-08 
18:30:13.000000000 +0200
@@ -0,0 +1,89 @@
+---
+version: 2.1
+
+executors:
+  python:
+    docker:
+      - image: cimg/python:3.9
+
+jobs:
+  flake8_lint:
+    executor: python
+    steps:
+      - checkout
+      - run: pip install tox
+      - run: tox -e flake8
+  isort_lint:
+    executor: python
+    steps:
+      - checkout
+      - run: pip install tox
+      - run: tox -e isort
+  test:
+    parameters:
+      python:
+        type: string
+    docker:
+      - image: circleci/python:<< parameters.python >>
+    environment:
+      TOXENV: "py<< parameters.python >>"
+    steps:
+      - checkout
+      - run: echo 'export PATH=$HOME/.local/bin:$PATH' >> $BASH_ENV
+      - run: pip install --user tox
+      - run: tox
+  test_nooptionals:
+    parameters:
+      python:
+        type: string
+    docker:
+      - image: cimg/python:<< parameters.python >>
+    environment:
+      TOXENV: "py<< parameters.python >>-nooptionals"
+    steps:
+      - checkout
+      - run: pip install tox
+      - run: tox
+  test_pypy:
+    parameters:
+      python:
+        type: string
+    docker:
+      - image: pypy:<< parameters.python >>
+    environment:
+      TOXENV: "pypy<< parameters.python >>"
+    steps:
+      - checkout
+      - run: pip install tox
+      - run: tox
+   
+
+workflows:
+  version: 2
+  client_python:
+    jobs:
+      - flake8_lint
+      - isort_lint
+      - test:
+          matrix:
+            parameters:
+               python:
+                 - "2.7"
+                 - "3.4"
+                 - "3.5"
+                 - "3.6"
+                 - "3.7"
+                 - "3.8"
+                 - "3.9"
+      - test_nooptionals:
+          matrix:
+            parameters:
+               python:
+                 - "2.7"
+                 - "3.9"
+      - test_pypy:
+          matrix:
+            parameters:
+               python:
+                 - "2.7"
+                 - "3.7"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/.travis.yml 
new/client_python-0.10.1/.travis.yml
--- old/client_python-0.9.0/.travis.yml 2020-11-16 14:26:28.000000000 +0100
+++ new/client_python-0.10.1/.travis.yml        1970-01-01 01:00:00.000000000 
+0100
@@ -1,64 +0,0 @@
-os: linux
-dist: xenial
-cache:
-  directories:
-    - $HOME/.cache/pip
-
-language: python
-
-jobs:
-  include:
-    - stage: lint
-      name: flake8_lint
-      script: 
-        - tox -e flake8 
-    - stage: lint
-      name: isort_lint
-      script: 
-        - tox -e isort
-    - stage: test
-      python: "2.6"
-      env: TOXENV=py26
-      dist: trusty
-    - stage: test
-      python: "2.7"
-      env: TOXENV=py27
-    - stage: test
-      python: "2.7"
-      env: TOXENV=py27-nooptionals
-    - stage: test
-      python: "3.4"
-      env: TOXENV=py34
-    - stage: test
-      python: "3.5"
-      env: TOXENV=py35
-    - stage: test
-      python: "3.6"
-      env: TOXENV=py36
-    - stage: test
-      python: "3.7"
-      env: TOXENV=py37
-    - stage: test
-      python: "3.8"
-      env: TOXENV=py38
-    - stage: test
-      python: "3.9"
-      env: TOXENV=py39
-    - stage: test
-      python: "3.9"
-      env: TOXENV=py39-nooptionals
-    - stage: test
-      python: "pypy"
-      env: TOXENV=pypy
-    - stage: test
-      python: "pypy3"
-      env: TOXENV=pypy3
-
-install:
-  - pip install tox
-
-script:
-  - tox
-
-notifications:
-  email: false
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/MAINTAINERS.md 
new/client_python-0.10.1/MAINTAINERS.md
--- old/client_python-0.9.0/MAINTAINERS.md      2020-11-16 14:26:28.000000000 
+0100
+++ new/client_python-0.10.1/MAINTAINERS.md     2021-04-08 18:30:13.000000000 
+0200
@@ -1 +1 @@
-* Brian Brazil <brian.bra...@robustperception.io>
+* Chris Marchbanks <csmarchba...@gmail.com> @csmarchbanks
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/README.md 
new/client_python-0.10.1/README.md
--- old/client_python-0.9.0/README.md   2020-11-16 14:26:28.000000000 +0100
+++ new/client_python-0.10.1/README.md  2021-04-08 18:30:13.000000000 +0200
@@ -6,7 +6,7 @@
 
 **One**: Install the client:
 ```
-pip install prometheus_client
+pip install prometheus-client
 ```
 
 **Two**: Paste the following into a Python interpreter:
@@ -447,6 +447,17 @@
 gb.start(10.0)
 ```
 
+Graphite 
[tags](https://grafana.com/blog/2018/01/11/graphite-1.1-teaching-an-old-dog-new-tricks/)
 are also supported.
+
+```python
+from prometheus_client.bridge.graphite import GraphiteBridge
+
+gb = GraphiteBridge(('graphite.your.org', 2003), tags=True)
+c = Counter('my_requests_total', 'HTTP Failures', ['method', 'endpoint'])
+c.labels('get', '/').inc()
+gb.push()
+```
+
 ## Custom Collectors
 
 Sometimes it is not possible to directly instrument code, as it is not
@@ -468,7 +479,7 @@
 REGISTRY.register(CustomCollector())
 ```
 
-`SummaryMetricFamily` and `HistogramMetricFamily` work similarly.
+`SummaryMetricFamily`, `HistogramMetricFamily` and `InfoMetricFamily` work 
similarly.
 
 A collector may implement a `describe` method which returns metrics in the same
 format as `collect` (though you don't have to include the samples). This is
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/SECURITY.md 
new/client_python-0.10.1/SECURITY.md
--- old/client_python-0.9.0/SECURITY.md 1970-01-01 01:00:00.000000000 +0100
+++ new/client_python-0.10.1/SECURITY.md        2021-04-08 18:30:13.000000000 
+0200
@@ -0,0 +1,6 @@
+# Reporting a security issue
+
+The Prometheus security policy, including how to report vulnerabilities, can be
+found here:
+
+https://prometheus.io/docs/operating/security/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/client_python-0.9.0/prometheus_client/bridge/graphite.py 
new/client_python-0.10.1/prometheus_client/bridge/graphite.py
--- old/client_python-0.9.0/prometheus_client/bridge/graphite.py        
2020-11-16 14:26:28.000000000 +0100
+++ new/client_python-0.10.1/prometheus_client/bridge/graphite.py       
2021-04-08 18:30:13.000000000 +0200
@@ -46,9 +46,10 @@
 
 
 class GraphiteBridge(object):
-    def __init__(self, address, registry=REGISTRY, timeout_seconds=30, 
_timer=time.time):
+    def __init__(self, address, registry=REGISTRY, timeout_seconds=30, 
_timer=time.time, tags=False):
         self._address = address
         self._registry = registry
+        self._tags = tags
         self._timeout = timeout_seconds
         self._timer = _timer
 
@@ -63,8 +64,14 @@
         for metric in self._registry.collect():
             for s in metric.samples:
                 if s.labels:
-                    labelstr = '.' + '.'.join(
-                        ['{0}.{1}'.format(
+                    if self._tags:
+                        sep = ';'
+                        fmt = '{0}={1}'
+                    else:
+                        sep = '.'
+                        fmt = '{0}.{1}'
+                    labelstr = sep + sep.join(
+                        [fmt.format(
                             _sanitize(k), _sanitize(v))
                             for k, v in sorted(s.labels.items())])
                 else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/prometheus_client/exposition.py 
new/client_python-0.10.1/prometheus_client/exposition.py
--- old/client_python-0.9.0/prometheus_client/exposition.py     2020-11-16 
14:26:28.000000000 +0100
+++ new/client_python-0.10.1/prometheus_client/exposition.py    2021-04-08 
18:30:13.000000000 +0200
@@ -17,22 +17,86 @@
 
     from BaseHTTPServer import BaseHTTPRequestHandler
     from SocketServer import ThreadingMixIn
-    from urllib2 import build_opener, HTTPHandler, Request
+    from urllib2 import (
+        build_opener, HTTPError, HTTPHandler, HTTPRedirectHandler, Request,
+    )
     from urlparse import parse_qs, urlparse
 except ImportError:
     # Python 3
     from http.server import BaseHTTPRequestHandler
     from socketserver import ThreadingMixIn
+    from urllib.error import HTTPError
     from urllib.parse import parse_qs, quote_plus, urlparse
-    from urllib.request import build_opener, HTTPHandler, Request
+    from urllib.request import (
+        build_opener, HTTPHandler, HTTPRedirectHandler, Request,
+    )
 
 CONTENT_TYPE_LATEST = str('text/plain; version=0.0.4; charset=utf-8')
 """Content type of the latest text format"""
-
+PYTHON27_OR_OLDER = sys.version_info < (3, )
 PYTHON26_OR_OLDER = sys.version_info < (2, 7)
 PYTHON376_OR_NEWER = sys.version_info > (3, 7, 5)
 
 
+class _PrometheusRedirectHandler(HTTPRedirectHandler):
+    """
+    Allow additional methods (e.g. PUT) and data forwarding in redirects.
+
+    Use of this class constitute a user's explicit agreement to the
+    redirect responses the Prometheus client will receive when using it.
+    You should only use this class if you control or otherwise trust the
+    redirect behavior involved and are certain it is safe to full transfer
+    the original request (method and data) to the redirected URL. For
+    example, if you know there is a cosmetic URL redirect in front of a
+    local deployment of a Prometheus server, and all redirects are safe,
+    this is the class to use to handle redirects in that case.
+
+    The standard HTTPRedirectHandler does not forward request data nor
+    does it allow redirected PUT requests (which Prometheus uses for some
+    operations, for example `push_to_gateway`) because these cannot
+    generically guarantee no violations of HTTP RFC 2616 requirements for
+    the user to explicitly confirm redirects that could have unexpected
+    side effects (such as rendering a PUT request non-idempotent or
+    creating multiple resources not named in the original request).
+    """
+
+    def redirect_request(self, req, fp, code, msg, headers, newurl):
+        """
+        Apply redirect logic to a request.
+
+        See parent HTTPRedirectHandler.redirect_request for parameter info.
+
+        If the redirect is disallowed, this raises the corresponding HTTP 
error.
+        If the redirect can't be determined, return None to allow other 
handlers
+        to try. If the redirect is allowed, return the new request.
+
+        This method specialized for the case when (a) the user knows that the
+        redirect will not cause unacceptable side effects for any request 
method,
+        and (b) the user knows that any request data should be passed through 
to
+        the redirect. If either condition is not met, this should not be used.
+        """
+        # note that requests being provided by a handler will use get_method to
+        # indicate the method, by monkeypatching this, instead of setting the
+        # Request object's method attribute.
+        m = getattr(req, "method", req.get_method())
+        if not (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
+                or code in (301, 302, 303) and m in ("POST", "PUT")):
+            raise HTTPError(req.full_url, code, msg, headers, fp)
+        new_request = Request(
+            newurl.replace(' ', '%20'),  # space escaping in new url if needed.
+            headers=req.headers,
+            origin_req_host=req.origin_req_host,
+            unverifiable=True,
+            data=req.data,
+        )
+        if PYTHON27_OR_OLDER:
+            # the `method` attribute did not exist for Request in Python 2.7.
+            new_request.get_method = lambda: m
+        else:
+            new_request.method = m
+        return new_request
+
+
 def _bake_output(registry, accept_header, params):
     """Bake output for metrics output."""
     encoder, content_type = choose_encoder(accept_header)
@@ -49,8 +113,14 @@
         # Prepare parameters
         accept_header = environ.get('HTTP_ACCEPT')
         params = parse_qs(environ.get('QUERY_STRING', ''))
-        # Bake output
-        status, header, output = _bake_output(registry, accept_header, params)
+        if environ['PATH_INFO'] == '/favicon.ico':
+            # Serve empty response for browsers
+            status = '200 OK'
+            header = ('', '')
+            output = b''
+        else:
+            # Bake output
+            status, header, output = _bake_output(registry, accept_header, 
params)
         # Return output
         start_response(status, [header])
         return [output]
@@ -141,7 +211,7 @@
             raise
 
         for suffix, lines in sorted(om_samples.items()):
-            output.append('# HELP {0}{1} {2}\n'.format(metric.name, suffix, 
+            output.append('# HELP {0}{1} {2}\n'.format(metric.name, suffix,
                                                        
metric.documentation.replace('\\', r'\\').replace('\n', r'\n')))
             output.append('# TYPE {0}{1} gauge\n'.format(metric.name, suffix))
             output.extend(lines)
@@ -205,17 +275,14 @@
     os.rename(tmppath, path)
 
 
-def default_handler(url, method, timeout, headers, data):
-    """Default handler that implements HTTP/HTTPS connections.
-
-    Used by the push_to_gateway functions. Can be re-used by other handlers."""
+def _make_handler(url, method, timeout, headers, data, base_handler):
 
     def handle():
         request = Request(url, data=data)
         request.get_method = lambda: method
         for k, v in headers:
             request.add_header(k, v)
-        resp = build_opener(HTTPHandler).open(request, timeout=timeout)
+        resp = build_opener(base_handler).open(request, timeout=timeout)
         if resp.code >= 400:
             raise IOError("error talking to pushgateway: {0} {1}".format(
                 resp.code, resp.msg))
@@ -223,6 +290,28 @@
     return handle
 
 
+def default_handler(url, method, timeout, headers, data):
+    """Default handler that implements HTTP/HTTPS connections.
+
+    Used by the push_to_gateway functions. Can be re-used by other handlers."""
+
+    return _make_handler(url, method, timeout, headers, data, HTTPHandler)
+
+
+def passthrough_redirect_handler(url, method, timeout, headers, data):
+    """
+    Handler that automatically trusts redirect responses for all HTTP methods.
+
+    Augments standard HTTPRedirectHandler capability by permitting PUT 
requests,
+    preserving the method upon redirect, and passing through all headers and
+    data from the original request. Only use this handler if you control or
+    trust the source of redirect responses you encounter when making requests
+    via the Prometheus client. This handler will simply repeat the identical
+    request, including same method and data, to the new redirect URL."""
+
+    return _make_handler(url, method, timeout, headers, data, 
_PrometheusRedirectHandler)
+
+
 def basic_auth_handler(url, method, timeout, headers, data, username=None, 
password=None):
     """Handler that implements HTTP/HTTPS connections with Basic Auth.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/prometheus_client/metrics.py 
new/client_python-0.10.1/prometheus_client/metrics.py
--- old/client_python-0.9.0/prometheus_client/metrics.py        2020-11-16 
14:26:28.000000000 +0100
+++ new/client_python-0.10.1/prometheus_client/metrics.py       2021-04-08 
18:30:13.000000000 +0200
@@ -186,6 +186,11 @@
         with self._lock:
             del self._metrics[labelvalues]
 
+    def clear(self):
+        """Remove all labelsets from the metric"""
+        with self._lock:
+            self._metrics = {}
+
     def _samples(self):
         if self._is_parent():
             return self._multi_samples()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/client_python-0.9.0/prometheus_client/multiprocess.py 
new/client_python-0.10.1/prometheus_client/multiprocess.py
--- old/client_python-0.9.0/prometheus_client/multiprocess.py   2020-11-16 
14:26:28.000000000 +0100
+++ new/client_python-0.10.1/prometheus_client/multiprocess.py  2021-04-08 
18:30:13.000000000 +0200
@@ -4,6 +4,7 @@
 import glob
 import json
 import os
+import warnings
 
 from .metrics_core import Metric
 from .mmap_dict import MmapedDict
@@ -23,9 +24,13 @@
 
     def __init__(self, registry, path=None):
         if path is None:
-            path = os.environ.get('prometheus_multiproc_dir')
+            # This deprecation warning can go away in a few releases when 
removing the compatibility
+            if 'prometheus_multiproc_dir' in os.environ and 
'PROMETHEUS_MULTIPROC_DIR' not in os.environ:
+                os.environ['PROMETHEUS_MULTIPROC_DIR'] = 
os.environ['prometheus_multiproc_dir']
+                warnings.warn("prometheus_multiproc_dir variable has been 
deprecated in favor of the upper case naming PROMETHEUS_MULTIPROC_DIR", 
DeprecationWarning)
+            path = os.environ.get('PROMETHEUS_MULTIPROC_DIR')
         if not path or not os.path.isdir(path):
-            raise ValueError('env prometheus_multiproc_dir is not set or not a 
directory')
+            raise ValueError('env PROMETHEUS_MULTIPROC_DIR is not set or not a 
directory')
         self._path = path
         if registry:
             registry.register(self)
@@ -66,7 +71,7 @@
                     # the file is missing
                     continue
                 raise
-            for key, value, pos in file_values:
+            for key, value, _ in file_values:
                 metric_name, name, labels, labels_key = _parse_key(key)
 
                 metric = metrics.get(metric_name)
@@ -152,7 +157,7 @@
 def mark_process_dead(pid, path=None):
     """Do bookkeeping for when one process dies in a multi-process setup."""
     if path is None:
-        path = os.environ.get('prometheus_multiproc_dir')
+        path = os.environ.get('PROMETHEUS_MULTIPROC_DIR', 
os.environ.get('prometheus_multiproc_dir'))
     for f in glob.glob(os.path.join(path, 'gauge_livesum_{0}.db'.format(pid))):
         os.remove(f)
     for f in glob.glob(os.path.join(path, 'gauge_liveall_{0}.db'.format(pid))):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/client_python-0.9.0/prometheus_client/openmetrics/parser.py 
new/client_python-0.10.1/prometheus_client/openmetrics/parser.py
--- old/client_python-0.9.0/prometheus_client/openmetrics/parser.py     
2020-11-16 14:26:28.000000000 +0100
+++ new/client_python-0.10.1/prometheus_client/openmetrics/parser.py    
2021-04-08 18:30:13.000000000 +0200
@@ -529,10 +529,7 @@
             if parts[1] == 'HELP':
                 if documentation is not None:
                     raise ValueError("More than one HELP for metric: " + line)
-                if len(parts) == 4:
-                    documentation = _unescape_help(parts[3])
-                elif len(parts) == 3:
-                    raise ValueError("Invalid line: " + line)
+                documentation = _unescape_help(parts[3])
             elif parts[1] == 'TYPE':
                 if typ is not None:
                     raise ValueError("More than one TYPE for metric: " + line)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/prometheus_client/values.py 
new/client_python-0.10.1/prometheus_client/values.py
--- old/client_python-0.9.0/prometheus_client/values.py 2020-11-16 
14:26:28.000000000 +0100
+++ new/client_python-0.10.1/prometheus_client/values.py        2021-04-08 
18:30:13.000000000 +0200
@@ -2,6 +2,7 @@
 
 import os
 from threading import Lock
+import warnings
 
 from .mmap_dict import mmap_key, MmapedDict
 
@@ -51,6 +52,10 @@
 
         def __init__(self, typ, metric_name, name, labelnames, labelvalues, 
multiprocess_mode='', **kwargs):
             self._params = typ, metric_name, name, labelnames, labelvalues, 
multiprocess_mode
+            # This deprecation warning can go away in a few releases when 
removing the compatibility
+            if 'prometheus_multiproc_dir' in os.environ and 
'PROMETHEUS_MULTIPROC_DIR' not in os.environ:
+                os.environ['PROMETHEUS_MULTIPROC_DIR'] = 
os.environ['prometheus_multiproc_dir']
+                warnings.warn("prometheus_multiproc_dir variable has been 
deprecated in favor of the upper case naming PROMETHEUS_MULTIPROC_DIR", 
DeprecationWarning)
             with lock:
                 self.__check_for_pid_change()
                 self.__reset()
@@ -64,7 +69,7 @@
                 file_prefix = typ
             if file_prefix not in files:
                 filename = os.path.join(
-                    os.environ['prometheus_multiproc_dir'],
+                    os.environ.get('PROMETHEUS_MULTIPROC_DIR'),
                     '{0}_{1}.db'.format(file_prefix, pid['value']))
 
                 files[file_prefix] = MmapedDict(filename)
@@ -108,7 +113,7 @@
     # This needs to be chosen before the first metric is constructed,
     # and as that may be in some arbitrary library the user/admin has
     # no control over we use an environment variable.
-    if 'prometheus_multiproc_dir' in os.environ:
+    if 'prometheus_multiproc_dir' in os.environ or 'PROMETHEUS_MULTIPROC_DIR' 
in os.environ:
         return MultiProcessValue()
     else:
         return MutexValue
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/setup.py 
new/client_python-0.10.1/setup.py
--- old/client_python-0.9.0/setup.py    2020-11-16 14:26:28.000000000 +0100
+++ new/client_python-0.10.1/setup.py   2021-04-08 18:30:13.000000000 +0200
@@ -12,7 +12,7 @@
 
 setup(
     name="prometheus_client",
-    version="0.9.0",
+    version="0.10.1",
     author="Brian Brazil",
     author_email="brian.bra...@robustperception.io",
     description="Python client for the Prometheus monitoring system.",
@@ -31,6 +31,7 @@
         'twisted': ['twisted'],
     },
     test_suite="tests",
+    python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
     classifiers=[
         "Development Status :: 4 - Beta",
         "Intended Audience :: Developers",
@@ -38,7 +39,6 @@
         "Intended Audience :: System Administrators",
         "Programming Language :: Python",
         "Programming Language :: Python :: 2",
-        "Programming Language :: Python :: 2.6",
         "Programming Language :: Python :: 2.7",
         "Programming Language :: Python :: 3",
         "Programming Language :: Python :: 3.4",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/client_python-0.9.0/tests/openmetrics/test_exposition.py 
new/client_python-0.10.1/tests/openmetrics/test_exposition.py
--- old/client_python-0.9.0/tests/openmetrics/test_exposition.py        
2020-11-16 14:26:28.000000000 +0100
+++ new/client_python-0.10.1/tests/openmetrics/test_exposition.py       
2021-04-08 18:30:13.000000000 +0200
@@ -48,6 +48,12 @@
         self.assertEqual(b'# HELP cc A counter\n# TYPE cc counter\ncc_total 
1.0\ncc_created 123.456\n# EOF\n',
                          generate_latest(self.registry))
 
+    def test_counter_unit(self):
+        c = Counter('cc_seconds', 'A counter', registry=self.registry, 
unit="seconds")
+        c.inc()
+        self.assertEqual(b'# HELP cc_seconds A counter\n# TYPE cc_seconds 
counter\n# UNIT cc_seconds seconds\ncc_seconds_total 1.0\ncc_seconds_created 
123.456\n# EOF\n',
+                         generate_latest(self.registry))
+
     def test_gauge(self):
         g = Gauge('gg', 'A gauge', registry=self.registry)
         g.set(17)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/tests/openmetrics/test_parser.py 
new/client_python-0.10.1/tests/openmetrics/test_parser.py
--- old/client_python-0.9.0/tests/openmetrics/test_parser.py    2020-11-16 
14:26:28.000000000 +0100
+++ new/client_python-0.10.1/tests/openmetrics/test_parser.py   2021-04-08 
18:30:13.000000000 +0200
@@ -221,6 +221,16 @@
         cfm.add_sample("a_total", {}, 0.0, Timestamp(123, 0), Exemplar({"a": 
"b"}, 0.5))
         self.assertEqual([cfm], list(families))
 
+    def test_counter_exemplars_empty_brackets(self):
+        families = text_string_to_metric_families("""# TYPE a counter
+# HELP a help
+a_total{} 0 123 # {a="b"} 0.5
+# EOF
+""")
+        cfm = CounterMetricFamily("a", "help")
+        cfm.add_sample("a_total", {}, 0.0, Timestamp(123, 0), Exemplar({"a": 
"b"}, 0.5))
+        self.assertEqual([cfm], list(families))
+
     def test_simple_info(self):
         families = text_string_to_metric_families("""# TYPE a info
 # HELP a help
@@ -409,11 +419,15 @@
 # HELP a he\\n\\\\l\\tp
 a_total{foo="b\\"a\\nr"} 1
 a_total{foo="b\\\\a\\z"} 2
+a_total{foo="b\\"a\\nr # "} 3
+a_total{foo="b\\\\a\\z # "} 4
 # EOF
 """)
         metric_family = CounterMetricFamily("a", "he\n\\l\\tp", labels=["foo"])
         metric_family.add_metric(["b\"a\nr"], 1)
         metric_family.add_metric(["b\\a\\z"], 2)
+        metric_family.add_metric(["b\"a\nr # "], 3)
+        metric_family.add_metric(["b\\a\\z # "], 4)
         self.assertEqual([metric_family], list(families))
 
     def test_null_byte(self):
@@ -591,6 +605,10 @@
 foo_count 17.0
 foo_sum 324789.3
 foo_created 1.520430000123e+09
+# HELP bar histogram Testing with labels
+# TYPE bar histogram
+bar_bucket{a="b",le="+Inf"} 0.0
+bar_bucket{a="c",le="+Inf"} 0.0
 # EOF
 """
         families = list(text_string_to_metric_families(text))
@@ -628,9 +646,14 @@
             ('a{a="1",b="2",} 1\n# EOF\n'),
             # Invalid labels.
             ('a{1="1"} 1\n# EOF\n'),
+            ('a{1="1"}1\n# EOF\n'),
             ('a{a="1",a="1"} 1\n# EOF\n'),
+            ('a{a="1"b} 1\n# EOF\n'),
             ('a{1=" # "} 1\n# EOF\n'),
             ('a{a=" # ",a=" # "} 1\n# EOF\n'),
+            ('a{a=" # "}1\n# EOF\n'),
+            ('a{a=" # ",b=}1\n# EOF\n'),
+            ('a{a=" # "b}1\n# EOF\n'),
             # Missing value.
             ('a\n# EOF\n'),
             ('a \n# EOF\n'),
@@ -667,6 +690,8 @@
             ('# HELP a x\n# HELP a x\n# EOF\n'),
             ('# TYPE a untyped\n# TYPE a untyped\n# EOF\n'),
             ('# UNIT a_s s\n# UNIT a_s s\n# EOF\n'),
+            # Bad metadata.
+            ('# FOO a x\n# EOF\n'),
             # Bad metric names.
             ('0a 1\n# EOF\n'),
             ('a.b 1\n# EOF\n'),
@@ -706,6 +731,10 @@
              
'{a="23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"}
 1 1\n# EOF\n'),
             ('# TYPE a histogram\na_bucket{le="+Inf"} 1 # {} 0x1p-3\n# EOF\n'),
             ('# TYPE a histogram\na_bucket{le="+Inf"} 1 # {} 1 0x1p-3\n# 
EOF\n'),
+            ('# TYPE a counter\na_total 1 1 # {id="a"}  \n# EOF\n'),
+            ('# TYPE a counter\na_total 1 1 # id="a"} 1\n# EOF\n'),
+            ('# TYPE a counter\na_total 1 1 #id=" # "} 1\n# EOF\n'),
+            ('# TYPE a counter\na_total 1 1 id=" # "} 1\n# EOF\n'),
             # Exemplars on unallowed samples.
             ('# TYPE a histogram\na_sum 1 # {a="b"} 0.5\n# EOF\n'),
             ('# TYPE a gaugehistogram\na_sum 1 # {a="b"} 0.5\n# EOF\n'),
@@ -750,13 +779,18 @@
             ('# TYPE a summary\na_sum -1\n# EOF\n'),
             ('# TYPE a summary\na_count -1\n# EOF\n'),
             ('# TYPE a summary\na{quantile="0.5"} -1\n# EOF\n'),
+            # Bad info and stateset values.
+            ('# TYPE a info\na_info{foo="bar"} 2\n# EOF\n'),
+            ('# TYPE a stateset\na{a="bar"} 2\n# EOF\n'),
             # Bad histograms.
             ('# TYPE a histogram\na_sum 1\n# EOF\n'),
-            ('# TYPE a histogram\na_bucket{le="+Inf"} 0\n#a_sum 0\n# EOF\n'),
-            ('# TYPE a histogram\na_bucket{le="+Inf"} 0\n#a_count 0\n# EOF\n'),
+            ('# TYPE a histogram\na_bucket{le="+Inf"} 0\na_sum 0\n# EOF\n'),
+            ('# TYPE a histogram\na_bucket{le="+Inf"} 0\na_count 0\n# EOF\n'),
+            ('# TYPE a histogram\na_bucket{le="-1"} 0\na_bucket{le="+Inf"} 
0\na_sum 0\na_count 0\n# EOF\n'),
             ('# TYPE a gaugehistogram\na_gsum 1\n# EOF\n'),
             ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} 0\na_gsum 0\n# 
EOF\n'),
             ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} 0\na_gcount 0\n# 
EOF\n'),
+            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} 1\na_gsum 
-1\na_gcount 1\n# EOF\n'),
             ('# TYPE a histogram\na_count 1\na_bucket{le="+Inf"} 0\n# EOF\n'),
             ('# TYPE a histogram\na_bucket{le="+Inf"} 0\na_count 1\n# EOF\n'),
             ('# TYPE a histogram\na_bucket{le="+INF"} 0\n# EOF\n'),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/tests/test_core.py 
new/client_python-0.10.1/tests/test_core.py
--- old/client_python-0.9.0/tests/test_core.py  2020-11-16 14:26:28.000000000 
+0100
+++ new/client_python-0.10.1/tests/test_core.py 2021-04-08 18:30:13.000000000 
+0200
@@ -456,6 +456,15 @@
         self.assertEqual(None, self.registry.get_sample_value('c_total', {'l': 
'x'}))
         self.assertEqual(2, self.registry.get_sample_value('c_total', {'l': 
'y'}))
 
+    def test_clear(self):
+        self.counter.labels('x').inc()
+        self.counter.labels('y').inc(2)
+        self.assertEqual(1, self.registry.get_sample_value('c_total', {'l': 
'x'}))
+        self.assertEqual(2, self.registry.get_sample_value('c_total', {'l': 
'y'}))
+        self.counter.clear()
+        self.assertEqual(None, self.registry.get_sample_value('c_total', {'l': 
'x'}))
+        self.assertEqual(None, self.registry.get_sample_value('c_total', {'l': 
'y'}))
+
     def test_incorrect_label_count_raises(self):
         self.assertRaises(ValueError, self.counter.labels)
         self.assertRaises(ValueError, self.counter.labels, 'a', 'b')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/tests/test_exposition.py 
new/client_python-0.10.1/tests/test_exposition.py
--- old/client_python-0.9.0/tests/test_exposition.py    2020-11-16 
14:26:28.000000000 +0100
+++ new/client_python-0.10.1/tests/test_exposition.py   2021-04-08 
18:30:13.000000000 +0200
@@ -14,6 +14,7 @@
 from prometheus_client.core import GaugeHistogramMetricFamily, Timestamp
 from prometheus_client.exposition import (
     basic_auth_handler, default_handler, MetricsHandler,
+    passthrough_redirect_handler,
 )
 
 if sys.version_info < (2, 7):
@@ -208,6 +209,8 @@
 
 class TestPushGateway(unittest.TestCase):
     def setUp(self):
+        redirect_flag = 'testFlag'
+        self.redirect_flag = redirect_flag  # preserve a copy for downstream 
test assertions
         self.registry = CollectorRegistry()
         self.counter = Gauge('g', 'help', registry=self.registry)
         self.requests = requests = []
@@ -216,6 +219,11 @@
             def do_PUT(self):
                 if 'with_basic_auth' in self.requestline and 
self.headers['authorization'] != 'Basic Zm9vOmJhcg==':
                     self.send_response(401)
+                elif 'redirect' in self.requestline and redirect_flag not in 
self.requestline:
+                    # checks for an initial test request with 'redirect' but 
without the redirect_flag,
+                    # and simulates a redirect to a url with the redirect_flag 
(which will produce a 201)
+                    self.send_response(301)
+                    self.send_header('Location', getattr(self, 
'redirect_address', None))
                 else:
                     self.send_response(201)
                 length = int(self.headers['content-length'])
@@ -225,6 +233,22 @@
             do_POST = do_PUT
             do_DELETE = do_PUT
 
+        # set up a separate server to serve a fake redirected request.
+        # the redirected URL will have `redirect_flag` added to it,
+        # which will cause the request handler to return 201.
+        httpd_redirect = HTTPServer(('localhost', 0), TestHandler)
+        self.redirect_address = TestHandler.redirect_address = \
+            
'http://localhost:{0}/{1}'.format(httpd_redirect.server_address[1], 
redirect_flag)
+
+        class TestRedirectServer(threading.Thread):
+            def run(self):
+                httpd_redirect.handle_request()
+
+        self.redirect_server = TestRedirectServer()
+        self.redirect_server.daemon = True
+        self.redirect_server.start()
+
+        # set up the normal server to serve the example requests across test 
cases.
         httpd = HTTPServer(('localhost', 0), TestHandler)
         self.address = 'http://localhost:{0}'.format(httpd.server_address[1])
 
@@ -236,6 +260,7 @@
         self.server.daemon = True
         self.server.start()
 
+
     def test_push(self):
         push_to_gateway(self.address, "my_job", self.registry)
         self.assertEqual(self.requests[0][0].command, 'PUT')
@@ -330,6 +355,27 @@
         self.assertEqual(self.requests[0][0].headers.get('content-type'), 
CONTENT_TYPE_LATEST)
         self.assertEqual(self.requests[0][1], b'# HELP g help\n# TYPE g 
gauge\ng 0.0\n')
 
+    def test_push_with_redirect_handler(self):
+        def my_redirect_handler(url, method, timeout, headers, data):
+            return passthrough_redirect_handler(url, method, timeout, headers, 
data)
+
+        push_to_gateway(self.address, "my_job_with_redirect", self.registry, 
handler=my_redirect_handler)
+        self.assertEqual(self.requests[0][0].command, 'PUT')
+        self.assertEqual(self.requests[0][0].path, 
'/metrics/job/my_job_with_redirect')
+        self.assertEqual(self.requests[0][0].headers.get('content-type'), 
CONTENT_TYPE_LATEST)
+        self.assertEqual(self.requests[0][1], b'# HELP g help\n# TYPE g 
gauge\ng 0.0\n')
+
+        # ensure the redirect preserved request settings from the initial 
request.
+        self.assertEqual(self.requests[0][0].command, 
self.requests[1][0].command)
+        self.assertEqual(
+            self.requests[0][0].headers.get('content-type'),
+            self.requests[1][0].headers.get('content-type')
+        )
+        self.assertEqual(self.requests[0][1], self.requests[1][1])
+
+        # ensure the redirect took place at the expected redirect location.
+        self.assertEqual(self.requests[1][0].path, "/" + self.redirect_flag)
+
     @unittest.skipIf(
         sys.platform == "darwin",
         "instance_ip_grouping_key() does not work on macOS."
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/tests/test_graphite_bridge.py 
new/client_python-0.10.1/tests/test_graphite_bridge.py
--- old/client_python-0.9.0/tests/test_graphite_bridge.py       2020-11-16 
14:26:28.000000000 +0100
+++ new/client_python-0.10.1/tests/test_graphite_bridge.py      2021-04-08 
18:30:13.000000000 +0200
@@ -35,8 +35,11 @@
         self.t.start()
 
         # Explicitly use localhost as the target host, since connecting to 
0.0.0.0 fails on Windows
-        address = ('localhost', server.server_address[1])
-        self.gb = GraphiteBridge(address, self.registry, _timer=fake_timer)
+        self.address = ('localhost', server.server_address[1])
+        self.gb = GraphiteBridge(self.address, self.registry, 
_timer=fake_timer)
+
+    def _use_tags(self):
+        self.gb = GraphiteBridge(self.address, self.registry, tags=True, 
_timer=fake_timer)
 
     def test_nolabels(self):
         gauge = Gauge('g', 'help', registry=self.registry)
@@ -56,6 +59,16 @@
 
         self.assertEqual(b'labels.a.c.b.d 1.0 1434898897\n', self.data)
 
+    def test_labels_tags(self):
+        self._use_tags()
+        labels = Gauge('labels', 'help', ['a', 'b'], registry=self.registry)
+        labels.labels('c', 'd').inc()
+
+        self.gb.push()
+        self.t.join()
+
+        self.assertEqual(b'labels;a=c;b=d 1.0 1434898897\n', self.data)
+
     def test_prefix(self):
         labels = Gauge('labels', 'help', ['a', 'b'], registry=self.registry)
         labels.labels('c', 'd').inc()
@@ -65,6 +78,16 @@
 
         self.assertEqual(b'pre.fix.labels.a.c.b.d 1.0 1434898897\n', self.data)
 
+    def test_prefix_tags(self):
+        self._use_tags()
+        labels = Gauge('labels', 'help', ['a', 'b'], registry=self.registry)
+        labels.labels('c', 'd').inc()
+
+        self.gb.push(prefix='pre.fix')
+        self.t.join()
+
+        self.assertEqual(b'pre.fix.labels;a=c;b=d 1.0 1434898897\n', self.data)
+
     def test_sanitizing(self):
         labels = Gauge('labels', 'help', ['a'], registry=self.registry)
         labels.labels('c.:8').inc()
@@ -73,3 +96,13 @@
         self.t.join()
 
         self.assertEqual(b'labels.a.c__8 1.0 1434898897\n', self.data)
+
+    def test_sanitizing_tags(self):
+        self._use_tags()
+        labels = Gauge('labels', 'help', ['a'], registry=self.registry)
+        labels.labels('c.:8').inc()
+
+        self.gb.push()
+        self.t.join()
+
+        self.assertEqual(b'labels;a=c__8 1.0 1434898897\n', self.data)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/tests/test_multiprocess.py 
new/client_python-0.10.1/tests/test_multiprocess.py
--- old/client_python-0.9.0/tests/test_multiprocess.py  2020-11-16 
14:26:28.000000000 +0100
+++ new/client_python-0.10.1/tests/test_multiprocess.py 2021-04-08 
18:30:13.000000000 +0200
@@ -5,6 +5,7 @@
 import shutil
 import sys
 import tempfile
+import warnings
 
 from prometheus_client import mmap_dict, values
 from prometheus_client.core import (
@@ -13,7 +14,9 @@
 from prometheus_client.multiprocess import (
     mark_process_dead, MultiProcessCollector,
 )
-from prometheus_client.values import MultiProcessValue, MutexValue
+from prometheus_client.values import (
+    get_value_class, MultiProcessValue, MutexValue,
+)
 
 if sys.version_info < (2, 7):
     # We need the skip decorators from unittest2 on Python 2.6.
@@ -22,20 +25,50 @@
     import unittest
 
 
-class TestMultiProcess(unittest.TestCase):
+class TestMultiProcessDeprecation(unittest.TestCase):
     def setUp(self):
         self.tempdir = tempfile.mkdtemp()
+
+    def tearDown(self):
+        os.environ.pop('prometheus_multiproc_dir', None)
+        os.environ.pop('PROMETHEUS_MULTIPROC_DIR', None)
+        values.ValueClass = MutexValue
+        shutil.rmtree(self.tempdir)
+
+    def test_deprecation_warning(self):
         os.environ['prometheus_multiproc_dir'] = self.tempdir
+        with warnings.catch_warnings(record=True) as w:
+            values.ValueClass = get_value_class()
+            registry = CollectorRegistry()
+            collector = MultiProcessCollector(registry)
+            Counter('c', 'help', registry=None)
+
+            assert os.environ['PROMETHEUS_MULTIPROC_DIR'] == self.tempdir
+            assert len(w) == 1
+            assert issubclass(w[-1].category, DeprecationWarning)
+            assert "PROMETHEUS_MULTIPROC_DIR" in str(w[-1].message)
+
+    def test_mark_process_dead_respects_lowercase(self):
+        os.environ['prometheus_multiproc_dir'] = self.tempdir
+        # Just test that this does not raise with a lowercase env var. The
+        # logic is tested elsewhere.
+        mark_process_dead(123)
+
+
+class TestMultiProcess(unittest.TestCase):
+    def setUp(self):
+        self.tempdir = tempfile.mkdtemp()
+        os.environ['PROMETHEUS_MULTIPROC_DIR'] = self.tempdir
         values.ValueClass = MultiProcessValue(lambda: 123)
         self.registry = CollectorRegistry()
-        self.collector = MultiProcessCollector(self.registry, self.tempdir)
+        self.collector = MultiProcessCollector(self.registry)
 
     @property
     def _value_class(self):
         return
 
     def tearDown(self):
-        del os.environ['prometheus_multiproc_dir']
+        del os.environ['PROMETHEUS_MULTIPROC_DIR']
         shutil.rmtree(self.tempdir)
         values.ValueClass = MutexValue
 
@@ -80,7 +113,7 @@
         self.assertEqual(0, self.registry.get_sample_value('g', {'pid': 
'456'}))
         g1.set(1)
         g2.set(2)
-        mark_process_dead(123, os.environ['prometheus_multiproc_dir'])
+        mark_process_dead(123)
         self.assertEqual(1, self.registry.get_sample_value('g', {'pid': 
'123'}))
         self.assertEqual(2, self.registry.get_sample_value('g', {'pid': 
'456'}))
 
@@ -94,7 +127,7 @@
         g2.set(2)
         self.assertEqual(1, self.registry.get_sample_value('g', {'pid': 
'123'}))
         self.assertEqual(2, self.registry.get_sample_value('g', {'pid': 
'456'}))
-        mark_process_dead(123, os.environ['prometheus_multiproc_dir'])
+        mark_process_dead(123, os.environ['PROMETHEUS_MULTIPROC_DIR'])
         self.assertEqual(None, self.registry.get_sample_value('g', {'pid': 
'123'}))
         self.assertEqual(2, self.registry.get_sample_value('g', {'pid': 
'456'}))
 
@@ -124,7 +157,7 @@
         g1.set(1)
         g2.set(2)
         self.assertEqual(3, self.registry.get_sample_value('g'))
-        mark_process_dead(123, os.environ['prometheus_multiproc_dir'])
+        mark_process_dead(123, os.environ['PROMETHEUS_MULTIPROC_DIR'])
         self.assertEqual(2, self.registry.get_sample_value('g'))
 
     def test_namespace_subsystem(self):
@@ -151,7 +184,7 @@
         # can not inspect the files cache directly, as it's a closure, so we
         # check for the actual files themselves
         def files():
-            fs = os.listdir(os.environ['prometheus_multiproc_dir'])
+            fs = os.listdir(os.environ['PROMETHEUS_MULTIPROC_DIR'])
             fs.sort()
             return fs
 
@@ -240,7 +273,7 @@
         pid = 1
         h.labels(**labels).observe(5)
 
-        path = os.path.join(os.environ['prometheus_multiproc_dir'], '*.db')
+        path = os.path.join(os.environ['PROMETHEUS_MULTIPROC_DIR'], '*.db')
         files = glob.glob(path)
         metrics = dict(
             (m.name, m) for m in self.collector.merge(files, accumulate=False)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/tests/test_wsgi.py 
new/client_python-0.10.1/tests/test_wsgi.py
--- old/client_python-0.9.0/tests/test_wsgi.py  2020-11-16 14:26:28.000000000 
+0100
+++ new/client_python-0.10.1/tests/test_wsgi.py 2021-04-08 18:30:13.000000000 
+0200
@@ -1,10 +1,12 @@
 from __future__ import absolute_import, unicode_literals
 
+import sys
+import unittest
 from unittest import TestCase
 from wsgiref.util import setup_testing_defaults
 
 from prometheus_client import CollectorRegistry, Counter, make_wsgi_app
-from prometheus_client.exposition import CONTENT_TYPE_LATEST
+from prometheus_client.exposition import _bake_output, CONTENT_TYPE_LATEST
 
 
 class WSGITest(TestCase):
@@ -65,3 +67,22 @@
 
     def test_report_metrics_4(self):
         self.validate_metrics("failed_requests", "Number of failed requests", 
7)
+
+    @unittest.skipIf(sys.version_info < (3, 3), "Test requires Python 3.3+.")
+    def test_favicon_path(self):
+        from unittest.mock import patch
+
+        # Create mock to enable counting access of _bake_output
+        with patch("prometheus_client.exposition._bake_output", 
side_effect=_bake_output) as mock:
+            # Create and run WSGI app
+            app = make_wsgi_app(self.registry)
+            # Try accessing the favicon path
+            favicon_environ = dict(self.environ)
+            favicon_environ['PATH_INFO'] = '/favicon.ico'
+            outputs = app(favicon_environ, self.capture)
+            # Test empty response
+            self.assertEqual(outputs, [b''])
+            self.assertEqual(mock.call_count, 0)
+            # Try accessing normal paths
+            app(self.environ, self.capture)
+            self.assertEqual(mock.call_count, 1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/client_python-0.9.0/tox.ini 
new/client_python-0.10.1/tox.ini
--- old/client_python-0.9.0/tox.ini     2020-11-16 14:26:28.000000000 +0100
+++ new/client_python-0.10.1/tox.ini    2021-04-08 18:30:13.000000000 +0200
@@ -1,5 +1,5 @@
 [tox]
-envlist = 
coverage-clean,py26,py27,py34,py35,py36,py37,py38,py39,pypy,pypy3,{py27,py39}-nooptionals,coverage-report,flake8,isort
+envlist = 
coverage-clean,py2.7,py3.4,py3.5,py3.6,py3.7,py3.8,py3.9,pypy2.7,pypy3.7,{py2.7,py3.9}-nooptionals,coverage-report,flake8,isort
 
 
 [base]
@@ -7,21 +7,12 @@
     coverage
     pytest
 
-[testenv:py26]
-; Last pytest and py version supported on py26 .
-deps =
-  unittest2
-  py==1.4.31
-  pytest==2.9.2
-  coverage
-  futures
-
-[testenv:py27]
+[testenv:py2.7]
 deps =
     {[base]deps}
     futures
 
-[testenv:pypy]
+[testenv:pypy2.7]
 deps =
     {[base]deps}
     futures
@@ -29,18 +20,18 @@
 [testenv]
 deps =
     {[base]deps}
-    {py27,py37,pypy,pypy3}: twisted
-    {py37,pypy3}: asgiref
+    {py2.7,py3.7,pypy2.7,pypy3.7}: twisted
+    {py3.7,pypy3.7}: asgiref
 commands = coverage run --parallel -m pytest {posargs}
 
 ; Ensure test suite passes if no optional dependencies are present.
-[testenv:py27-nooptionals]
+[testenv:py2.7-nooptionals]
 deps = 
     {[base]deps}
     futures
 commands = coverage run --parallel -m pytest {posargs}
 
-[testenv:py39-nooptionals]
+[testenv:py3.9-nooptionals]
 commands = coverage run --parallel -m pytest {posargs}
 
 [testenv:coverage-clean]

Reply via email to