[Python-modules-commits] [python-statsd] 01/03: Import python-statsd_3.2.orig.tar.gz

Filippo Giunchedi filippo at moszumanska.debian.org
Fri Oct 23 11:24:39 UTC 2015


This is an automated email from the git hooks/post-receive script.

filippo pushed a commit to tag debian/3.2-1
in repository python-statsd.

commit bdc740b53dc6c471bb7244ae50355322de6de22f
Author: Filippo Giunchedi <filippo at debian.org>
Date:   Thu Oct 22 11:35:37 2015 +0100

    Import python-statsd_3.2.orig.tar.gz
---
 .travis.yml                 |   7 +-
 CHANGES                     |  14 +
 README.rst                  |  25 +-
 docs/conf.py                |   6 +-
 docs/index.rst              |  18 +-
 docs/reference.rst          | 101 ++++-
 docs/tcp.rst                |  20 +
 docs/types.rst              |   6 +-
 setup.py                    |   2 +-
 statsd/__init__.py          |   5 +-
 statsd/client.py            | 130 +++++--
 statsd/defaults/__init__.py |   1 +
 statsd/defaults/django.py   |   4 +-
 statsd/defaults/env.py      |   4 +-
 statsd/tests.py             | 883 +++++++++++++++++++++++++++++++++-----------
 15 files changed, 973 insertions(+), 253 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 9208899..6e420d7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,5 @@
 language: python
+sudo: false
 python:
  - "2.6"
  - "2.7"
@@ -6,4 +7,8 @@ python:
  - "3.3"
  - "3.4"
  - "pypy"
-script: nosetests
+install:
+ - pip install -q flake8
+script:
+ - nosetests
+ - flake8 statsd/
diff --git a/CHANGES b/CHANGES
index 771be84..831d06b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,20 @@
 Statsd Changelog
 ================
 
+Version 3.2
+-----------
+
+- Add an explicit IPv6 flag.
+- Add support for sub-millisecond timings
+
+
+Version 3.1
+-----------
+
+- Add IPv6 support.
+- Add TCPStatsClient/TCPPipeline to support connection-mode clients.
+
+
 Version 3.0.1
 -------------
 
diff --git a/README.rst b/README.rst
index 8c48244..ed83a22 100644
--- a/README.rst
+++ b/README.rst
@@ -2,12 +2,33 @@
 A Python statsd client
 ======================
 
+statsd_ is a friendly front-end to Graphite_. This is a Python client
+for the statsd daemon.
+
 .. image:: https://travis-ci.org/jsocol/pystatsd.png?branch=master
    :target: https://travis-ci.org/jsocol/pystatsd
    :alt: Travis-CI build status
 
-statsd_ is a friendly front-end to Graphite_. This is a Python client
-for the statsd daemon.
+.. image:: https://pypip.in/v/statsd/badge.png?style=flat
+   :target: https://pypi.python.org/pypi/statsd/
+   :alt: Latest release
+
+.. image:: https://pypip.in/py_versions/statsd/badge.svg?style=flat
+   :target: https://pypi.python.org/pypi/statsd/
+   :alt: Supported Python versions
+
+.. image:: https://pypip.in/wheel/statsd/badge.svg?style=flat
+   :target: https://pypi.python.org/pypi/statsd/
+   :alt: Wheel Status
+
+.. image:: https://pypip.in/d/statsd/badge.png?style=flat
+   :target: https://pypi.python.org/pypi/statsd/
+   :alt: Downloads
+
+:Code:          https://github.com/jsocol/pystatsd
+:License:       MIT; see LICENSE file
+:Issues:        https://github.com/jsocol/pystatsd/issues
+:Documentation: http://statsd.readthedocs.org/
 
 Quickly, to use::
 
diff --git a/docs/conf.py b/docs/conf.py
index 4e75a4b..419a1da 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -41,16 +41,16 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'Python StatsD'
-copyright = u'2014, James Socol'
+copyright = u'2015, James Socol'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = '3.0'
+version = '3.2'
 # The full version, including alpha/beta/rc tags.
-release = '3.0'
+release = '3.2.0'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/docs/index.rst b/docs/index.rst
index ac9f4b1..ed4cb6c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -6,12 +6,25 @@
 Welcome to Python StatsD's documentation!
 =========================================
 
+statsd_ is a friendly front-end to Graphite_. This is a Python client
+for the statsd daemon.
+
 .. image:: https://travis-ci.org/jsocol/pystatsd.png?branch=master
    :target: https://travis-ci.org/jsocol/pystatsd
    :alt: Travis-CI build status
 
-statsd_ is a friendly front-end to Graphite_. This is a Python client
-for the statsd daemon.
+.. image:: https://pypip.in/v/statsd/badge.png
+   :target: https://pypi.python.org/pypi/statsd/
+   :alt: Latest release
+
+.. image:: https://pypip.in/d/statsd/badge.png
+   :target: https://pypi.python.org/pypi/statsd/
+   :alt: Downloads
+
+:Code:          https://github.com/jsocol/pystatsd
+:License:       MIT; see LICENSE file
+:Issues:        https://github.com/jsocol/pystatsd/issues
+:Documentation: http://statsd.readthedocs.org/
 
 Quickly, to use::
 
@@ -57,6 +70,7 @@ Contents
    types.rst
    timing.rst
    pipeline.rst
+   tcp.rst
    reference.rst
    contributing.rst
 
diff --git a/docs/reference.rst b/docs/reference.rst
index 0c0202e..860b00f 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -9,9 +9,9 @@ statsd_ server supports.
 
 .. note::
 
-    Each public API method supports a ``rate`` parameter, but statsd
-    doesn't always use it the same way. See the :ref:`types-chapter` for
-    more information.
+    Each public stats API method supports a ``rate`` parameter, but
+    statsd doesn't always use it the same way. See the
+    :ref:`types-chapter` for more information.
 
 
 .. _StatsClient:
@@ -21,7 +21,7 @@ statsd_ server supports.
 
 ::
 
-    StatsClient(host='localhost', port=8125, prefix=None)
+    StatsClient(host='localhost', port=8125, prefix=None, maxudpsize=512)
 
 Create a new ``StatsClient`` instance with the appropriate connection
 and prefix information.
@@ -33,6 +33,10 @@ and prefix information.
 * ``prefix``: a prefix to distinguish and group stats from an
   application or environment.
 
+* ``maxudpsize``: the largest safe UDP packet to save. 512 is generally
+  considered safe for the public internet, but private networks may
+  support larger packet sizes.
+
 
 .. _incr:
 
@@ -275,6 +279,95 @@ stats.
    This method is not implemented on the base StatsClient class.
 
 
+.. _TCPStatsClient:
+
+``TCPStatsClient``
+==================
+
+::
+
+    TCPStatsClient(host='localhost', port=8125, prefix=None, timeout=None)
+
+Create a new ``TCPStatsClient`` instance with the appropriate connection
+and prefix information.
+
+* ``host``: the hostname or IPv4 address of the statsd_ server.
+
+* ``port``: the port of the statsd server.
+
+* ``prefix``: a prefix to distinguish and group stats from an
+  application or environment.
+
+* ``timeout``: socket timeout for any actions on the connection socket.
+
+
+``TCPStatsClient`` implements all methods of ``StatsClient``, including
+``pipeline()``, with the difference that it is not thread safe and it
+can raise exceptions on connection errors. Unlike ``StatsClient`` it
+uses a TCP connection to communicate with StatsD.
+
+In addition to the stats methods, ``TCPStatsClient`` supports the
+following TCP-specific methods.
+
+
+.. _tcp_close:
+
+``close``
+---------
+
+::
+
+    from statsd import TCPStatsClient
+
+    statsd = TCPStatsClient()
+    statsd.incr('some.event')
+    statsd.close()
+
+Closes a connection that's currently open and deletes it's socket. If
+this is called on a ``TCPStatsClient`` which currently has no open
+connection it is a non-action.
+
+
+.. _tcp_connect:
+
+``connect``
+-----------
+
+::
+
+    from statsd import TCPStatsClient
+
+    statsd = TCPStatsClient()
+    statsd.incr('some.event')  # calls connect() internally
+    statsd.close()
+    statsd.connect()  # creates new connection
+
+Creates a connection to StatsD. If there are errors like connection
+timed out or connection refused, the according exceptions will be
+raised. It is usually not necessary to call this method because sending
+data to StatsD will call ``connect`` implicitely if the current instance
+of ``TCPStatsClient`` does not already hold an open connection.
+
+
+.. _tcp_reconnect:
+
+``reconnect``
+-------------
+
+::
+
+    from statsd import TCPStatsClient
+
+    statsd = TCPStatsClient()
+    statsd.incr('some.event')
+    statsd.reconnect()  # closes open connection and creates new one
+
+Closes a currently existing connection and replaces it with a new one.
+If no connection exists already it will simply create a new one.
+Internally this does nothing else than calling ``close()`` and
+``connect()``.
+
+
 .. _statsd: https://github.com/etsy/statsd
 .. _0ed78be: https://github.com/etsy/statsd/commit/0ed78be7
 .. _1c10cfc0ac: https://github.com/etsy/statsd/commit/1c10cfc0ac
diff --git a/docs/tcp.rst b/docs/tcp.rst
new file mode 100644
index 0000000..5437ac5
--- /dev/null
+++ b/docs/tcp.rst
@@ -0,0 +1,20 @@
+.. _tcp-chapter:
+
+==============
+TCPStatsClient
+==============
+
+The ``TCPStatsClient`` class has a very similar interface to
+``StatsClient``, but internally it uses TCP connections instead of UDP.
+These are the main differencies when using ``TCPStatsClient`` compared
+to ``StatsClient``:
+
+* The constructor supports a ``timeout`` parameter to set a timeout on
+  all socket actions.
+
+* ``connect()`` and all methods that send data can potentially raise
+  socket exceptions.
+
+* **It is not thread-safe**, so it is recommended to not share it across
+  threads unless a lot of attention is paid to make sure that no two
+  threads ever use it at once.
diff --git a/docs/types.rst b/docs/types.rst
index bc13d72..9f77280 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -11,7 +11,7 @@ performs different aggregation on each of them. The three main types are
 The statsd server collects and aggregates in 30 second intervals before
 flushing to Graphite_. Graphite usually stores the most recent data in
 1-minute averaged buckets, so when you're looking at a graph, for each
-stat you are typically seing the average value over that minute.
+stat you are typically seeing the average value over that minute.
 
 
 .. _counter-type:
@@ -79,8 +79,8 @@ Graphite, that's usually per minute).
 * The *lower bound* is the lowest value statsd saw for that stat during
   that time period.
 
-* The *mean* is the average of all values statsd saw for that stat
-during that time period.
+* The *mean* is the average of all values statsd saw for that stat 
+  during that time period.
 
 * The *90th percentile* is a value *x* such that 90% of all the values
   statsd saw for that stat during that time period are below *x*, and
diff --git a/setup.py b/setup.py
index 3e67f09..2a49f38 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@ from setuptools import find_packages, setup
 
 setup(
     name='statsd',
-    version='3.0.1',
+    version='3.2',
     description='A simple statsd client.',
     long_description=open('README.rst').read(),
     author='James Socol',
diff --git a/statsd/__init__.py b/statsd/__init__.py
index a4f0372..ea37107 100644
--- a/statsd/__init__.py
+++ b/statsd/__init__.py
@@ -1,8 +1,9 @@
 from __future__ import absolute_import
 
 from .client import StatsClient
+from .client import TCPStatsClient
 
 
-VERSION = (3, 0, 1)
+VERSION = (3, 2, 0)
 __version__ = '.'.join(map(str, VERSION))
-__all__ = ['StatsClient']
+__all__ = ['StatsClient', 'TCPStatsClient']
diff --git a/statsd/client.py b/statsd/client.py
index 4ea0b57..09ebd2d 100644
--- a/statsd/client.py
+++ b/statsd/client.py
@@ -1,11 +1,13 @@
 from __future__ import with_statement
+from collections import deque
 from functools import wraps
 import random
 import socket
 import time
+import abc
 
 
-__all__ = ['StatsClient']
+__all__ = ['StatsClient', 'TCPStatsClient']
 
 
 class Timer(object):
@@ -27,7 +29,7 @@ class Timer(object):
             try:
                 return_value = f(*args, **kwargs)
             finally:
-                elapsed_time_ms = int(round(1000 * (time.time() - start_time)))
+                elapsed_time_ms = 1000.0 * (time.time() - start_time)
                 self.client.timing(self.stat, elapsed_time_ms, self.rate)
             return return_value
         return _wrapped
@@ -48,7 +50,7 @@ class Timer(object):
         if self._start_time is None:
             raise RuntimeError('Timer has not started.')
         dt = time.time() - self._start_time
-        self.ms = int(round(1000 * dt))  # Convert to milliseconds.
+        self.ms = 1000.0 * dt  # Convert to milliseconds.
         if send:
             self.send()
         return self
@@ -62,26 +64,25 @@ class Timer(object):
         self.client.timing(self.stat, self.ms, self.rate)
 
 
-class StatsClient(object):
-    """A client for statsd."""
+class StatsClientBase(object):
+    """A Base class for various statsd clients."""
 
-    def __init__(self, host='localhost', port=8125, prefix=None,
-                 maxudpsize=512):
-        """Create a new client."""
-        self._addr = (socket.gethostbyname(host), port)
-        self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-        self._prefix = prefix
-        self._maxudpsize = maxudpsize
+    __metaclass__ = abc.ABCMeta
+
+    @abc.abstractmethod
+    def _send(self):
+        pass
 
+    @abc.abstractmethod
     def pipeline(self):
-        return Pipeline(self)
+        pass
 
     def timer(self, stat, rate=1):
         return Timer(self, stat, rate)
 
     def timing(self, stat, delta, rate=1):
         """Send new timing information. `delta` is in milliseconds."""
-        self._send_stat(stat, '%d|ms' % delta, rate)
+        self._send_stat(stat, '%0.6f|ms' % delta, rate)
 
     def incr(self, stat, count=1, rate=1):
         """Increment a stat by `count`."""
@@ -126,21 +127,88 @@ class StatsClient(object):
         if data:
             self._send(data)
 
+
+class StatsClient(StatsClientBase):
+    """A client for statsd."""
+
+    def __init__(self, host='localhost', port=8125, ipv6=False, prefix=None,
+                 maxudpsize=512):
+        """Create a new client."""
+        fam = socket.AF_INET6 if ipv6 else socket.AF_INET
+        family, _, _, _, addr = socket.getaddrinfo(
+            host, port, fam, socket.SOCK_DGRAM)[0]
+        self._addr = addr
+        self._sock = socket.socket(family, socket.SOCK_DGRAM)
+        self._prefix = prefix
+        self._maxudpsize = maxudpsize
+
     def _send(self, data):
         """Send data to statsd."""
         try:
             self._sock.sendto(data.encode('ascii'), self._addr)
-        except socket.error:
+        except (socket.error, RuntimeError):
             # No time for love, Dr. Jones!
             pass
 
+    def pipeline(self):
+        return Pipeline(self)
+
+
+class TCPStatsClient(StatsClientBase):
+    """TCP version of StatsClient."""
+
+    def __init__(self, host='localhost', port=8125, ipv6=False, prefix=None,
+                 timeout=None):
+        """Create a new client."""
+        self._host = host
+        self._port = port
+        self._ipv6 = ipv6
+        self._timeout = timeout
+        self._prefix = prefix
+        self._sock = None
+
+    def _send(self, data):
+        """Send data to statsd."""
+        if not self._sock:
+            self.connect()
+        self._do_send(data)
+
+    def _do_send(self, data):
+        self._sock.sendall(data.encode('ascii') + b'\n')
+
+    def close(self):
+        if self._sock and hasattr(self._sock, 'close'):
+            self._sock.close()
+        self._sock = None
+
+    def connect(self):
+        fam = socket.AF_INET6 if self._ipv6 else socket.AF_INET
+        family, _, _, _, addr = socket.getaddrinfo(
+            self._host, self._port, fam, socket.SOCK_STREAM)[0]
+        self._sock = socket.socket(family, socket.SOCK_STREAM)
+        self._sock.settimeout(self._timeout)
+        self._sock.connect(addr)
+
+    def pipeline(self):
+        return TCPPipeline(self)
+
+    def reconnect(self, data):
+        self.close()
+        self.connect()
+
+
+class PipelineBase(StatsClientBase):
+
+    __metaclass__ = abc.ABCMeta
 
-class Pipeline(StatsClient):
     def __init__(self, client):
         self._client = client
         self._prefix = client._prefix
-        self._maxudpsize = client._maxudpsize
-        self._stats = []
+        self._stats = deque()
+
+    @abc.abstractmethod
+    def _send(self):
+        pass
 
     def _after(self, data):
         if data is not None:
@@ -153,15 +221,35 @@ class Pipeline(StatsClient):
         self.send()
 
     def send(self):
-        # Use pop(0) to preserve the order of the stats.
         if not self._stats:
             return
-        data = self._stats.pop(0)
+        self._send()
+
+    def pipeline(self):
+        return self.__class__(self)
+
+
+class Pipeline(PipelineBase):
+
+    def __init__(self, client):
+        super(Pipeline, self).__init__(client)
+        self._maxudpsize = client._maxudpsize
+
+    def _send(self):
+        data = self._stats.popleft()
         while self._stats:
-            stat = self._stats.pop(0)
+            # Use popleft to preserve the order of the stats.
+            stat = self._stats.popleft()
             if len(stat) + len(data) + 1 >= self._maxudpsize:
                 self._client._after(data)
                 data = stat
             else:
                 data += '\n' + stat
         self._client._after(data)
+
+
+class TCPPipeline(PipelineBase):
+
+    def _send(self):
+        self._client._after('\n'.join(self._stats))
+        self._stats.clear()
diff --git a/statsd/defaults/__init__.py b/statsd/defaults/__init__.py
index 2f2de3b..21851e3 100644
--- a/statsd/defaults/__init__.py
+++ b/statsd/defaults/__init__.py
@@ -1,4 +1,5 @@
 HOST = 'localhost'
 PORT = 8125
+IPV6 = False
 PREFIX = None
 MAXUDPSIZE = 512
diff --git a/statsd/defaults/django.py b/statsd/defaults/django.py
index c4c2951..74da212 100644
--- a/statsd/defaults/django.py
+++ b/statsd/defaults/django.py
@@ -12,4 +12,6 @@ if statsd is None:
     port = getattr(settings, 'STATSD_PORT', defaults.PORT)
     prefix = getattr(settings, 'STATSD_PREFIX', defaults.PREFIX)
     maxudpsize = getattr(settings, 'STATSD_MAXUDPSIZE', defaults.MAXUDPSIZE)
-    statsd = StatsClient(host, port, prefix, maxudpsize)
+    ipv6 = getattr(settings, 'STATSD_IPV6', defaults.IPV6)
+    statsd = StatsClient(host=host, port=port, prefix=prefix,
+                         maxudpsize=maxudpsize, ipv6=ipv6)
diff --git a/statsd/defaults/env.py b/statsd/defaults/env.py
index eb857f9..1ee863f 100644
--- a/statsd/defaults/env.py
+++ b/statsd/defaults/env.py
@@ -12,4 +12,6 @@ if statsd is None:
     port = int(os.getenv('STATSD_PORT', defaults.PORT))
     prefix = os.getenv('STATSD_PREFIX', defaults.PREFIX)
     maxudpsize = int(os.getenv('STATSD_MAXUDPSIZE', defaults.MAXUDPSIZE))
-    statsd = StatsClient(host, port, prefix, maxudpsize)
+    ipv6 = bool(int(os.getenv('STATSD_IPV6', defaults.IPV6)))
+    statsd = StatsClient(host=host, port=port, prefix=prefix,
+                         maxudpsize=maxudpsize, ipv6=ipv6)
diff --git a/statsd/tests.py b/statsd/tests.py
index 751815c..4971d52 100644
--- a/statsd/tests.py
+++ b/statsd/tests.py
@@ -7,22 +7,65 @@ import mock
 from nose.tools import eq_
 
 from statsd import StatsClient
+from statsd import TCPStatsClient
 
 
 ADDR = (socket.gethostbyname('localhost'), 8125)
 
 
-def _client(prefix=None):
-    sc = StatsClient(host=ADDR[0], port=ADDR[1], prefix=prefix)
+# proto specific methods to get the socket method to send data
+send_method = {
+    'udp': lambda x: x.sendto,
+    'tcp': lambda x: x.sendall,
+}
+
+
+# proto specific methods to create the expected value
+make_val = {
+    'udp': lambda x, addr: mock.call(str.encode(x), addr),
+    'tcp': lambda x, addr: mock.call(str.encode(x + '\n')),
+}
+
+
+def _udp_client(prefix=None, addr=None, port=None, ipv6=False):
+    if not addr:
+        addr = ADDR[0]
+    if not port:
+        port = ADDR[1]
+    sc = StatsClient(host=addr, port=port, prefix=prefix, ipv6=ipv6)
     sc._sock = mock.Mock()
     return sc
 
 
-def _sock_check(cl, count, val=None):
-    eq_(cl._sock.sendto.call_count, count)
+def _tcp_client(prefix=None, addr=None, port=None, timeout=None, ipv6=False):
+    if not addr:
+        addr = ADDR[0]
+    if not port:
+        port = ADDR[1]
+    sc = TCPStatsClient(host=addr, port=port, prefix=prefix, timeout=timeout,
+                        ipv6=ipv6)
+    sc._sock = mock.Mock()
+    return sc
+
+
+def _timer_check(sock, count, proto, start, end):
+    send = send_method[proto](sock)
+    eq_(send.call_count, count)
+    value = send.call_args[0][0].decode('ascii')
+    exp = re.compile('^%s:\d+|%s$' % (start, end))
+    assert exp.match(value)
+
+
+def _sock_check(sock, count, proto, val=None, addr=None):
+    send = send_method[proto](sock)
+    eq_(send.call_count, count)
+    if not addr:
+        addr = ADDR
     if val is not None:
-        val = val.encode('ascii')
-        eq_(cl._sock.sendto.call_args, ((val, ADDR), {}))
+        eq_(
+            send.call_args,
+            make_val[proto](val, addr),
+        )
 
 
 class assert_raises(object):
@@ -82,54 +125,132 @@ class assert_raises(object):
         return True
 
 
+def _test_incr(cl, proto):
+    cl.incr('foo')
+    _sock_check(cl._sock, 1, proto, val='foo:1|c')
+
+    cl.incr('foo', 10)
+    _sock_check(cl._sock, 2, proto, val='foo:10|c')
+
+    cl.incr('foo', 1.2)
+    _sock_check(cl._sock, 3, proto, val='foo:1.2|c')
+
+    cl.incr('foo', 10, rate=0.5)
+    _sock_check(cl._sock, 4, proto, val='foo:10|c|@0.5')
+
+
+ at mock.patch.object(random, 'random', lambda: -1)
+def test_incr_udp():
+    """StatsClient.incr works."""
+    cl = _udp_client()
+    _test_incr(cl, 'udp')
+
+
 @mock.patch.object(random, 'random', lambda: -1)
-def test_incr():
-    sc = _client()
+def test_incr_tcp():
+    """TCPStatsClient.incr works."""
+    cl = _tcp_client()
+    _test_incr(cl, 'tcp')
+
+
+def _test_decr(cl, proto):
+    cl.decr('foo')
+    _sock_check(cl._sock, 1, proto, 'foo:-1|c')
 
-    sc.incr('foo')
-    _sock_check(sc, 1, 'foo:1|c')
+    cl.decr('foo', 10)
+    _sock_check(cl._sock, 2, proto, 'foo:-10|c')
 
-    sc.incr('foo', 10)
-    _sock_check(sc, 2, 'foo:10|c')
+    cl.decr('foo', 1.2)
+    _sock_check(cl._sock, 3, proto, 'foo:-1.2|c')
 
-    sc.incr('foo', 1.2)
-    _sock_check(sc, 3, 'foo:1.2|c')
+    cl.decr('foo', 1, rate=0.5)
+    _sock_check(cl._sock, 4, proto, 'foo:-1|c|@0.5')
 
-    sc.incr('foo', 10, rate=0.5)
-    _sock_check(sc, 4, 'foo:10|c|@0.5')
+
+ at mock.patch.object(random, 'random', lambda: -1)
+def test_decr_udp():
+    """StatsClient.decr works."""
+    cl = _udp_client()
+    _test_decr(cl, 'udp')
 
 
 @mock.patch.object(random, 'random', lambda: -1)
-def test_decr():
-    sc = _client()
+def test_decr_tcp():
+    """TCPStatsClient.decr works."""
+    cl = _tcp_client()
+    _test_decr(cl, 'tcp')
 
-    sc.decr('foo')
-    _sock_check(sc, 1, 'foo:-1|c')
 
-    sc.decr('foo', 10)
-    _sock_check(sc, 2, 'foo:-10|c')
+def _test_gauge(cl, proto):
+    cl.gauge('foo', 30)
+    _sock_check(cl._sock, 1, proto, 'foo:30|g')
 
-    sc.decr('foo', 1.2)
-    _sock_check(sc, 3, 'foo:-1.2|c')
+    cl.gauge('foo', 1.2)
+    _sock_check(cl._sock, 2, proto, 'foo:1.2|g')
 
-    sc.decr('foo', 1, rate=0.5)
-    _sock_check(sc, 4, 'foo:-1|c|@0.5')
+    cl.gauge('foo', 70, rate=0.5)
+    _sock_check(cl._sock, 3, proto, 'foo:70|g|@0.5')
 
 
 @mock.patch.object(random, 'random', lambda: -1)
-def test_gauge():
-    sc = _client()
-    sc.gauge('foo', 30)
-    _sock_check(sc, 1, 'foo:30|g')
+def test_gauge_udp():
+    """StatsClient.gauge works."""
+    cl = _udp_client()
+    _test_gauge(cl, 'udp')
 
-    sc.gauge('foo', 1.2)
-    _sock_check(sc, 2, 'foo:1.2|g')
 
-    sc.gauge('foo', 70, rate=0.5)
-    _sock_check(sc, 3, 'foo:70|g|@0.5')
+ at mock.patch.object(random, 'random', lambda: -1)
+def test_gauge_tcp():
+    """TCPStatsClient.gauge works."""
+    cl = _tcp_client()
+    _test_gauge(cl, 'tcp')
 
 
-def test_gauge_delta():
+def _test_ipv6(cl, proto, addr):
+    cl.gauge('foo', 30)
+    _sock_check(cl._sock, 1, proto, 'foo:30|g', addr=addr)
+
+
+def test_ipv6_udp():
+    """StatsClient can use to IPv6 address."""
+    addr = ('::1', 8125, 0, 0)
+    cl = _udp_client(addr=addr[0], ipv6=True)
+    _test_ipv6(cl, 'udp', addr)
+
+
+def test_ipv6_tcp():
+    """TCPStatsClient can use to IPv6 address."""
+    addr = ('::1', 8125, 0, 0)
+    cl = _tcp_client(addr=addr[0], ipv6=True)
+    _test_ipv6(cl, 'tcp', addr)
+
+
+def _test_resolution(cl, proto, addr):
+    cl.incr('foo')
+    _sock_check(cl._sock, 1, proto, 'foo:1|c', addr=addr)
+
+
+def test_ipv6_resolution_udp():
+    cl = _udp_client(addr='localhost', ipv6=True)
+    _test_resolution(cl, 'udp', ('::1', 8125, 0, 0))
+
+
+def test_ipv6_resolution_tcp():
+    cl = _tcp_client(addr='localhost', ipv6=True)
+    _test_resolution(cl, 'tcp', ('::1', 8125, 0, 0))
+
+
+def test_ipv4_resolution_udp():
+    cl = _udp_client(addr='localhost')
+    _test_resolution(cl, 'udp', ('127.0.0.1', 8125))
+
+
+def test_ipv4_resolution_tcp():
+    cl = _tcp_client(addr='localhost')
+    _test_resolution(cl, 'tcp', ('127.0.0.1', 8125))
+
+
+def _test_gauge_delta(cl, proto):
     tests = (
         (12, '+12'),
         (-13, '-13'),
@@ -138,65 +259,126 @@ def test_gauge_delta():
     )
 
     def _check(num, result):
-        sc = _client()
-        sc.gauge('foo', num, delta=True)
-        _sock_check(sc, 1, 'foo:%s|g' % result)
+        cl._sock.reset_mock()
+        cl.gauge('foo', num, delta=True)
+        _sock_check(cl._sock, 1, proto, 'foo:%s|g' % result)
 
     for num, result in tests:
-        yield _check, num, result
+        _check(num, result)
 
 
-def test_gauge_absolute_negative():
-    sc = _client()
-    sc.gauge('foo', -5, delta=False)
-    _sock_check(sc, 1, 'foo:0|g\nfoo:-5|g')
+ at mock.patch.object(random, 'random', lambda: -1)
+def test_gauge_delta_udp():
+    """StatsClient.gauge works with delta values."""
+    cl = _udp_client()
+    _test_gauge_delta(cl, 'udp')
 
 
- at mock.patch.object(random, 'random')
-def test_gauge_absolute_negative_rate(mock_random):
-    sc = _client()
+ at mock.patch.object(random, 'random', lambda: -1)
+def test_gauge_delta_tcp():
+    """TCPStatsClient.gauge works with delta values."""
+    cl = _tcp_client()
+    _test_gauge_delta(cl, 'tcp')
+
+
+def _test_gauge_absolute_negative(cl, proto):
+    cl.gauge('foo', -5, delta=False)
+    _sock_check(cl._sock, 1, 'foo:0|g\nfoo:-5|g')
+
+
+ at mock.patch.object(random, 'random', lambda: -1)
+def test_gauge_absolute_negative_udp():
+    """StatsClient.gauge works with absolute negative value."""
+    cl = _udp_client()
+    _test_gauge_delta(cl, 'udp')
+
+
+ at mock.patch.object(random, 'random', lambda: -1)
+def test_gauge_absolute_negative_tcp():
+    """TCPStatsClient.gauge works with absolute negative value."""
+    cl = _tcp_client()
+    _test_gauge_delta(cl, 'tcp')
+
+
+def _test_gauge_absolute_negative_rate(cl, proto, mock_random):
     mock_random.return_value = -1
-    sc.gauge('foo', -1, rate=0.5, delta=False)
-    _sock_check(sc, 1, 'foo:0|g\nfoo:-1|g')
+    cl.gauge('foo', -1, rate=0.5, delta=False)
+    _sock_check(cl._sock, 1, proto, 'foo:0|g\nfoo:-1|g')
 
     mock_random.return_value = 2
-    sc.gauge('foo', -2, rate=0.5, delta=False)
-    _sock_check(sc, 1, 'foo:0|g\nfoo:-1|g')  # Should not have changed.
+    cl.gauge('foo', -2, rate=0.5, delta=False)
+    # Should not have changed.
+    _sock_check(cl._sock, 1, proto, 'foo:0|g\nfoo:-1|g')
 
 
- at mock.patch.object(random, 'random', lambda: -1)
-def test_set():
-    sc = _client()
-    sc.set('foo', 10)
-    _sock_check(sc, 1, 'foo:10|s')
+ at mock.patch.object(random, 'random')
+def test_gauge_absolute_negative_rate_udp(mock_random):
+    """StatsClient.gauge works with absolute negative value and rate."""
+    cl = _udp_client()
+    _test_gauge_absolute_negative_rate(cl, 'udp', mock_random)
+
+
+ at mock.patch.object(random, 'random')
+def test_gauge_absolute_negative_rate_tcp(mock_random):
+    """TCPStatsClient.gauge works with absolute negative value and rate."""
+    cl = _tcp_client()
+    _test_gauge_absolute_negative_rate(cl, 'tcp', mock_random)
+
+
+def _test_set(cl, proto):
+    cl.set('foo', 10)
+    _sock_check(cl._sock, 1, proto, 'foo:10|s')
 
-    sc.set('foo', 2.3)
-    _sock_check(sc, 2, 'foo:2.3|s')
+    cl.set('foo', 2.3)
+    _sock_check(cl._sock, 2, proto, 'foo:2.3|s')
 
-    sc.set('foo', 'bar')
-    _sock_check(sc, 3, 'foo:bar|s')
+    cl.set('foo', 'bar')
+    _sock_check(cl._sock, 3, proto, 'foo:bar|s')
 
-    sc.set('foo', 2.3, 0.5)
-    _sock_check(sc, 4, 'foo:2.3|s|@0.5')
+    cl.set('foo', 2.3, 0.5)
+    _sock_check(cl._sock, 4, proto, 'foo:2.3|s|@0.5')
 
 
 @mock.patch.object(random, 'random', lambda: -1)
-def test_timing():
-    sc = _client()
+def test_set_udp():
+    """StatsClient.set works."""
+    cl = _udp_client()
+    _test_set(cl, 'udp')
 
-    sc.timing('foo', 100)
-    _sock_check(sc, 1, 'foo:100|ms')
 
-    sc.timing('foo', 350)
-    _sock_check(sc, 2, 'foo:350|ms')
+ at mock.patch.object(random, 'random', lambda: -1)
+def test_set_tcp():
+    """TCPStatsClient.set works."""
+    cl = _tcp_client()
+    _test_set(cl, 'tcp')
+
+
+def _test_timing(cl, proto):
+    cl.timing('foo', 100)
+    _sock_check(cl._sock, 1, proto, 'foo:100.000000|ms')
+
+    cl.timing('foo', 350)
+    _sock_check(cl._sock, 2, proto, 'foo:350.000000|ms')
+
+    cl.timing('foo', 100, rate=0.5)
... 739 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-statsd.git



More information about the Python-modules-commits mailing list