From 8be14b8365e5f17246ffb79e421073aec8b6aee7 Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 1 Nov 2021 15:49:25 +0100 Subject: [PATCH 1/8] annotate params with defaults --- statsd/client/base.py | 12 ++++++------ statsd/client/stream.py | 4 ++-- statsd/client/timer.py | 4 ++-- statsd/client/udp.py | 4 ++-- statsd/tests.py | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/statsd/client/base.py b/statsd/client/base.py index e273b417f..b17a140b8 100644 --- a/statsd/client/base.py +++ b/statsd/client/base.py @@ -20,10 +20,10 @@ def _send(self): def pipeline(self): raise NotImplementedError() - def timer(self, stat, rate=1): + def timer(self, stat, rate: int=1): return Timer(self, stat, rate) - def timing(self, stat, delta, rate=1): + def timing(self, stat, delta, rate: int=1): """ Send new timing information. @@ -34,15 +34,15 @@ def timing(self, stat, delta, rate=1): delta = delta.total_seconds() * 1000. self._send_stat(stat, '%0.6f|ms' % delta, rate) - def incr(self, stat, count=1, rate=1): + def incr(self, stat, count: int=1, rate: int=1): """Increment a stat by `count`.""" self._send_stat(stat, '%s|c' % count, rate) - def decr(self, stat, count=1, rate=1): + def decr(self, stat, count: int=1, rate: int=1): """Decrement a stat by `count`.""" self.incr(stat, -count, rate) - def gauge(self, stat, value, rate=1, delta=False): + def gauge(self, stat, value, rate: int=1, delta: bool=False): """Set a gauge value.""" if value < 0 and not delta: if rate < 1: @@ -55,7 +55,7 @@ def gauge(self, stat, value, rate=1, delta=False): prefix = '+' if delta and value >= 0 else '' self._send_stat(stat, '%s%s|g' % (prefix, value), rate) - def set(self, stat, value, rate=1): + def set(self, stat, value, rate: int=1): """Set a set value.""" self._send_stat(stat, '%s|s' % value, rate) diff --git a/statsd/client/stream.py b/statsd/client/stream.py index 76c216f56..19a363009 100644 --- a/statsd/client/stream.py +++ b/statsd/client/stream.py @@ -40,8 +40,8 @@ def _do_send(self, data): class TCPStatsClient(StreamClientBase): """TCP version of StatsClient.""" - def __init__(self, host='localhost', port=8125, prefix=None, - timeout=None, ipv6=False): + def __init__(self, host: str='localhost', port: int=8125, prefix=None, + timeout=None, ipv6: bool=False): """Create a new client.""" self._host = host self._port = port diff --git a/statsd/client/timer.py b/statsd/client/timer.py index fefc9d042..c07819572 100644 --- a/statsd/client/timer.py +++ b/statsd/client/timer.py @@ -21,7 +21,7 @@ def safe_wraps(wrapper, *args, **kwargs): class Timer(object): """A context manager/decorator for statsd.timing().""" - def __init__(self, client, stat, rate=1): + def __init__(self, client, stat, rate: int=1): self.client = client self.stat = stat self.rate = rate @@ -53,7 +53,7 @@ def start(self): self._start_time = time_now() return self - def stop(self, send=True): + def stop(self, send: bool=True): if self._start_time is None: raise RuntimeError('Timer has not started.') dt = time_now() - self._start_time diff --git a/statsd/client/udp.py b/statsd/client/udp.py index ddb9d222d..c33c8efad 100644 --- a/statsd/client/udp.py +++ b/statsd/client/udp.py @@ -27,8 +27,8 @@ def _send(self): class StatsClient(StatsClientBase): """A client for statsd.""" - def __init__(self, host='localhost', port=8125, prefix=None, - maxudpsize=512, ipv6=False): + def __init__(self, host: str='localhost', port: int=8125, prefix=None, + maxudpsize: int=512, ipv6: bool=False): """Create a new client.""" fam = socket.AF_INET6 if ipv6 else socket.AF_INET family, _, _, _, addr = socket.getaddrinfo( diff --git a/statsd/tests.py b/statsd/tests.py index 12fc410cd..1d4ce327f 100644 --- a/statsd/tests.py +++ b/statsd/tests.py @@ -34,7 +34,7 @@ } -def _udp_client(prefix=None, addr=None, port=None, ipv6=False): +def _udp_client(prefix=None, addr=None, port=None, ipv6: bool=False): if not addr: addr = ADDR[0] if not port: @@ -44,7 +44,7 @@ def _udp_client(prefix=None, addr=None, port=None, ipv6=False): return sc -def _tcp_client(prefix=None, addr=None, port=None, timeout=None, ipv6=False): +def _tcp_client(prefix=None, addr=None, port=None, timeout=None, ipv6: bool=False): if not addr: addr = ADDR[0] if not port: @@ -598,7 +598,7 @@ def foo(a, b): return [b, a] @cl.timer('bar', rate=0.2) - def bar(a, b=2, c=3): + def bar(a, b: int=2, c: int=3): return [c, b, a] eq_([2, 4], foo(4, 2)) From 9e3e4a58f9d68d9160e91b0852b29fc841868d76 Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 1 Nov 2021 15:51:22 +0100 Subject: [PATCH 2/8] annotate return types --- statsd/client/base.py | 22 ++-- statsd/client/stream.py | 18 +-- statsd/client/timer.py | 4 +- statsd/client/udp.py | 10 +- statsd/tests.py | 250 ++++++++++++++++++++-------------------- 5 files changed, 152 insertions(+), 152 deletions(-) diff --git a/statsd/client/base.py b/statsd/client/base.py index b17a140b8..f6a417620 100644 --- a/statsd/client/base.py +++ b/statsd/client/base.py @@ -23,7 +23,7 @@ def pipeline(self): def timer(self, stat, rate: int=1): return Timer(self, stat, rate) - def timing(self, stat, delta, rate: int=1): + def timing(self, stat, delta, rate: int=1) -> None: """ Send new timing information. @@ -34,15 +34,15 @@ def timing(self, stat, delta, rate: int=1): delta = delta.total_seconds() * 1000. self._send_stat(stat, '%0.6f|ms' % delta, rate) - def incr(self, stat, count: int=1, rate: int=1): + def incr(self, stat, count: int=1, rate: int=1) -> None: """Increment a stat by `count`.""" self._send_stat(stat, '%s|c' % count, rate) - def decr(self, stat, count: int=1, rate: int=1): + def decr(self, stat, count: int=1, rate: int=1) -> None: """Decrement a stat by `count`.""" self.incr(stat, -count, rate) - def gauge(self, stat, value, rate: int=1, delta: bool=False): + def gauge(self, stat, value, rate: int=1, delta: bool=False) -> None: """Set a gauge value.""" if value < 0 and not delta: if rate < 1: @@ -55,11 +55,11 @@ def gauge(self, stat, value, rate: int=1, delta: bool=False): prefix = '+' if delta and value >= 0 else '' self._send_stat(stat, '%s%s|g' % (prefix, value), rate) - def set(self, stat, value, rate: int=1): + def set(self, stat, value, rate: int=1) -> None: """Set a set value.""" self._send_stat(stat, '%s|s' % value, rate) - def _send_stat(self, stat, value, rate): + def _send_stat(self, stat, value, rate) -> None: self._after(self._prepare(stat, value, rate)) def _prepare(self, stat, value, rate): @@ -73,14 +73,14 @@ def _prepare(self, stat, value, rate): return '%s:%s' % (stat, value) - def _after(self, data): + def _after(self, data) -> None: if data: self._send(data) class PipelineBase(StatsClientBase): - def __init__(self, client): + def __init__(self, client) -> None: self._client = client self._prefix = client._prefix self._stats = deque() @@ -88,17 +88,17 @@ def __init__(self, client): def _send(self): raise NotImplementedError() - def _after(self, data): + def _after(self, data) -> None: if data is not None: self._stats.append(data) def __enter__(self): return self - def __exit__(self, typ, value, tb): + def __exit__(self, typ, value, tb) -> None: self.send() - def send(self): + def send(self) -> None: if not self._stats: return self._send() diff --git a/statsd/client/stream.py b/statsd/client/stream.py index 19a363009..5fc022c0b 100644 --- a/statsd/client/stream.py +++ b/statsd/client/stream.py @@ -6,7 +6,7 @@ class StreamPipeline(PipelineBase): - def _send(self): + def _send(self) -> None: self._client._after('\n'.join(self._stats)) self._stats.clear() @@ -15,25 +15,25 @@ class StreamClientBase(StatsClientBase): def connect(self): raise NotImplementedError() - def close(self): + def close(self) -> None: if self._sock and hasattr(self._sock, 'close'): self._sock.close() self._sock = None - def reconnect(self): + def reconnect(self) -> None: self.close() self.connect() def pipeline(self): return StreamPipeline(self) - def _send(self, data): + def _send(self, data) -> None: """Send data to statsd.""" if not self._sock: self.connect() self._do_send(data) - def _do_send(self, data): + def _do_send(self, data) -> None: self._sock.sendall(data.encode('ascii') + b'\n') @@ -41,7 +41,7 @@ class TCPStatsClient(StreamClientBase): """TCP version of StatsClient.""" def __init__(self, host: str='localhost', port: int=8125, prefix=None, - timeout=None, ipv6: bool=False): + timeout=None, ipv6: bool=False) -> None: """Create a new client.""" self._host = host self._port = port @@ -50,7 +50,7 @@ def __init__(self, host: str='localhost', port: int=8125, prefix=None, self._prefix = prefix self._sock = None - def connect(self): + def connect(self) -> None: fam = socket.AF_INET6 if self._ipv6 else socket.AF_INET family, _, _, _, addr = socket.getaddrinfo( self._host, self._port, fam, socket.SOCK_STREAM)[0] @@ -62,14 +62,14 @@ def connect(self): class UnixSocketStatsClient(StreamClientBase): """Unix domain socket version of StatsClient.""" - def __init__(self, socket_path, prefix=None, timeout=None): + def __init__(self, socket_path, prefix=None, timeout=None) -> None: """Create a new client.""" self._socket_path = socket_path self._timeout = timeout self._prefix = prefix self._sock = None - def connect(self): + def connect(self) -> None: self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self._sock.settimeout(self._timeout) self._sock.connect(self._socket_path) diff --git a/statsd/client/timer.py b/statsd/client/timer.py index c07819572..173f47618 100644 --- a/statsd/client/timer.py +++ b/statsd/client/timer.py @@ -21,7 +21,7 @@ def safe_wraps(wrapper, *args, **kwargs): class Timer(object): """A context manager/decorator for statsd.timing().""" - def __init__(self, client, stat, rate: int=1): + def __init__(self, client, stat, rate: int=1) -> None: self.client = client self.stat = stat self.rate = rate @@ -44,7 +44,7 @@ def _wrapped(*args, **kwargs): def __enter__(self): return self.start() - def __exit__(self, typ, value, tb): + def __exit__(self, typ, value, tb) -> None: self.stop() def start(self): diff --git a/statsd/client/udp.py b/statsd/client/udp.py index c33c8efad..4b3fcdec8 100644 --- a/statsd/client/udp.py +++ b/statsd/client/udp.py @@ -7,11 +7,11 @@ class Pipeline(PipelineBase): - def __init__(self, client): + def __init__(self, client) -> None: super(Pipeline, self).__init__(client) self._maxudpsize = client._maxudpsize - def _send(self): + def _send(self) -> None: data = self._stats.popleft() while self._stats: # Use popleft to preserve the order of the stats. @@ -28,7 +28,7 @@ class StatsClient(StatsClientBase): """A client for statsd.""" def __init__(self, host: str='localhost', port: int=8125, prefix=None, - maxudpsize: int=512, ipv6: bool=False): + maxudpsize: int=512, ipv6: bool=False) -> None: """Create a new client.""" fam = socket.AF_INET6 if ipv6 else socket.AF_INET family, _, _, _, addr = socket.getaddrinfo( @@ -38,7 +38,7 @@ def __init__(self, host: str='localhost', port: int=8125, prefix=None, self._prefix = prefix self._maxudpsize = maxudpsize - def _send(self, data): + def _send(self, data) -> None: """Send data to statsd.""" try: self._sock.sendto(data.encode('ascii'), self._addr) @@ -46,7 +46,7 @@ def _send(self, data): # No time for love, Dr. Jones! pass - def close(self): + def close(self) -> None: if self._sock and hasattr(self._sock, 'close'): self._sock.close() self._sock = None diff --git a/statsd/tests.py b/statsd/tests.py index 1d4ce327f..7ef1824dc 100644 --- a/statsd/tests.py +++ b/statsd/tests.py @@ -64,7 +64,7 @@ def _unix_socket_client(prefix=None, socket_path=None): return sc -def _timer_check(sock, count, proto, start, end): +def _timer_check(sock, count, proto, start, end) -> None: send = send_method[proto](sock) eq_(send.call_count, count) value = send.call_args[0][0].decode('ascii') @@ -72,7 +72,7 @@ def _timer_check(sock, count, proto, start, end): assert exp.match(value) -def _sock_check(sock, count, proto, val=None, addr=None): +def _sock_check(sock, count, proto, val=None, addr=None) -> None: send = send_method[proto](sock) eq_(send.call_count, count) if not addr: @@ -122,14 +122,14 @@ class assert_raises(object): """ - def __init__(self, *exc_cls): + def __init__(self, *exc_cls) -> None: self.exc_cls = exc_cls def __enter__(self): # For access to the exception later. return self - def __exit__(self, typ, value, tb): + def __exit__(self, typ, value, tb) -> bool: assert typ, 'No exception raised.' assert typ in self.exc_cls, '%s not in %s' % ( typ.__name__, [e.__name__ for e in self.exc_cls]) @@ -141,7 +141,7 @@ def __exit__(self, typ, value, tb): return True -def _test_incr(cl, proto): +def _test_incr(cl, proto) -> None: cl.incr('foo') _sock_check(cl._sock, 1, proto, val='foo:1|c') @@ -156,27 +156,27 @@ def _test_incr(cl, proto): @mock.patch.object(random, 'random', lambda: -1) -def test_incr_udp(): +def test_incr_udp() -> None: """StatsClient.incr works.""" cl = _udp_client() _test_incr(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_incr_tcp(): +def test_incr_tcp() -> None: """TCPStatsClient.incr works.""" cl = _tcp_client() _test_incr(cl, 'tcp') @mock.patch.object(random, 'random', lambda: -1) -def test_incr_unix_socket(): +def test_incr_unix_socket() -> None: """TCPStatsClient.incr works.""" cl = _unix_socket_client() _test_incr(cl, 'unix') -def _test_decr(cl, proto): +def _test_decr(cl, proto) -> None: cl.decr('foo') _sock_check(cl._sock, 1, proto, 'foo:-1|c') @@ -191,27 +191,27 @@ def _test_decr(cl, proto): @mock.patch.object(random, 'random', lambda: -1) -def test_decr_udp(): +def test_decr_udp() -> None: """StatsClient.decr works.""" cl = _udp_client() _test_decr(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_decr_tcp(): +def test_decr_tcp() -> None: """TCPStatsClient.decr works.""" cl = _tcp_client() _test_decr(cl, 'tcp') @mock.patch.object(random, 'random', lambda: -1) -def test_decr_unix_socket(): +def test_decr_unix_socket() -> None: """TCPStatsClient.decr works.""" cl = _unix_socket_client() _test_decr(cl, 'unix') -def _test_gauge(cl, proto): +def _test_gauge(cl, proto) -> None: cl.gauge('foo', 30) _sock_check(cl._sock, 1, proto, 'foo:30|g') @@ -223,46 +223,46 @@ def _test_gauge(cl, proto): @mock.patch.object(random, 'random', lambda: -1) -def test_gauge_udp(): +def test_gauge_udp() -> None: """StatsClient.gauge works.""" cl = _udp_client() _test_gauge(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_gauge_tcp(): +def test_gauge_tcp() -> None: """TCPStatsClient.gauge works.""" cl = _tcp_client() _test_gauge(cl, 'tcp') @mock.patch.object(random, 'random', lambda: -1) -def test_gauge_unix_socket(): +def test_gauge_unix_socket() -> None: """TCPStatsClient.decr works.""" cl = _unix_socket_client() _test_gauge(cl, 'unix') -def _test_ipv6(cl, proto, addr): +def _test_ipv6(cl, proto, addr) -> None: cl.gauge('foo', 30) _sock_check(cl._sock, 1, proto, 'foo:30|g', addr=addr) -def test_ipv6_udp(): +def test_ipv6_udp() -> None: """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(): +def test_ipv6_tcp() -> None: """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): +def _test_resolution(cl, proto, addr) -> None: cl.incr('foo') _sock_check(cl._sock, 1, proto, 'foo:1|c', addr=addr) @@ -273,22 +273,22 @@ def test_ipv6_resolution_udp(): _test_resolution(cl, 'udp', ('::1', 8125, 0, 0)) -def test_ipv6_resolution_tcp(): +def test_ipv6_resolution_tcp() -> None: cl = _tcp_client(addr='localhost', ipv6=True) _test_resolution(cl, 'tcp', ('::1', 8125, 0, 0)) -def test_ipv4_resolution_udp(): +def test_ipv4_resolution_udp() -> None: cl = _udp_client(addr='localhost') _test_resolution(cl, 'udp', ('127.0.0.1', 8125)) -def test_ipv4_resolution_tcp(): +def test_ipv4_resolution_tcp() -> None: cl = _tcp_client(addr='localhost') _test_resolution(cl, 'tcp', ('127.0.0.1', 8125)) -def _test_gauge_delta(cl, proto): +def _test_gauge_delta(cl, proto) -> None: tests = ( (12, '+12'), (-13, '-13'), @@ -296,7 +296,7 @@ def _test_gauge_delta(cl, proto): (-1.3, '-1.3'), ) - def _check(num, result): + def _check(num, result) -> None: cl._sock.reset_mock() cl.gauge('foo', num, delta=True) _sock_check(cl._sock, 1, proto, 'foo:%s|g' % result) @@ -306,39 +306,39 @@ def _check(num, result): @mock.patch.object(random, 'random', lambda: -1) -def test_gauge_delta_udp(): +def test_gauge_delta_udp() -> None: """StatsClient.gauge works with delta values.""" cl = _udp_client() _test_gauge_delta(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_gauge_delta_tcp(): +def test_gauge_delta_tcp() -> None: """TCPStatsClient.gauge works with delta values.""" cl = _tcp_client() _test_gauge_delta(cl, 'tcp') -def _test_gauge_absolute_negative(cl, proto): +def _test_gauge_absolute_negative(cl, proto) -> None: cl.gauge('foo', -5, delta=False) _sock_check(cl._sock, 1, 'foo:0|g\nfoo:-5|g') @mock.patch.object(random, 'random', lambda: -1) -def test_gauge_absolute_negative_udp(): +def test_gauge_absolute_negative_udp() -> None: """StatsClient.gauge works with absolute negative value.""" cl = _udp_client() _test_gauge_delta(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_gauge_absolute_negative_tcp(): +def test_gauge_absolute_negative_tcp() -> None: """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): +def _test_gauge_absolute_negative_rate(cl, proto, mock_random) -> None: mock_random.return_value = -1 cl.gauge('foo', -1, rate=0.5, delta=False) _sock_check(cl._sock, 1, proto, 'foo:0|g\nfoo:-1|g') @@ -350,20 +350,20 @@ def _test_gauge_absolute_negative_rate(cl, proto, mock_random): @mock.patch.object(random, 'random') -def test_gauge_absolute_negative_rate_udp(mock_random): +def test_gauge_absolute_negative_rate_udp(mock_random) -> None: """StatsClient.gauge works with absolute negative value and rate.""" cl = _udp_client() _test_gauge_absolute_negative_rate(cl, 'udp', mock_random) @mock.patch.object(random, 'random') -def test_gauge_absolute_negative_rate_tcp(mock_random): +def test_gauge_absolute_negative_rate_tcp(mock_random) -> None: """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): +def _test_set(cl, proto) -> None: cl.set('foo', 10) _sock_check(cl._sock, 1, proto, 'foo:10|s') @@ -378,20 +378,20 @@ def _test_set(cl, proto): @mock.patch.object(random, 'random', lambda: -1) -def test_set_udp(): +def test_set_udp() -> None: """StatsClient.set works.""" cl = _udp_client() _test_set(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_set_tcp(): +def test_set_tcp() -> None: """TCPStatsClient.set works.""" cl = _tcp_client() _test_set(cl, 'tcp') -def _test_timing(cl, proto): +def _test_timing(cl, proto) -> None: cl.timing('foo', 100) _sock_check(cl._sock, 1, proto, 'foo:100.000000|ms') @@ -403,20 +403,20 @@ def _test_timing(cl, proto): @mock.patch.object(random, 'random', lambda: -1) -def test_timing_udp(): +def test_timing_udp() -> None: """StatsClient.timing works.""" cl = _udp_client() _test_timing(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_timing_tcp(): +def test_timing_tcp() -> None: """TCPStatsClient.timing works.""" cl = _tcp_client() _test_timing(cl, 'tcp') -def test_timing_supports_timedelta(): +def test_timing_supports_timedelta() -> None: cl = _udp_client() proto = 'udp' @@ -428,20 +428,20 @@ def test_timing_supports_timedelta(): @mock.patch.object(random, 'random', lambda: -1) -def test_timing_unix_socket(): +def test_timing_unix_socket() -> None: """UnixSocketStatsClient.timing works.""" cl = _unix_socket_client() _test_timing(cl, 'unix') -def _test_prepare(cl, proto): +def _test_prepare(cl, proto) -> None: tests = ( ('foo:1|c', ('foo', '1|c', 1)), ('bar:50|ms|@0.5', ('bar', '50|ms', 0.5)), ('baz:23|g', ('baz', '23|g', 1)), ) - def _check(o, s, v, r): + def _check(o, s, v, r) -> None: with mock.patch.object(random, 'random', lambda: -1): eq_(o, cl._prepare(s, v, r)) @@ -450,65 +450,65 @@ def _check(o, s, v, r): @mock.patch.object(random, 'random', lambda: -1) -def test_prepare_udp(): +def test_prepare_udp() -> None: """Test StatsClient._prepare method.""" cl = _udp_client() _test_prepare(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_prepare_tcp(): +def test_prepare_tcp() -> None: """Test TCPStatsClient._prepare method.""" cl = _tcp_client() _test_prepare(cl, 'tcp') -def _test_prefix(cl, proto): +def _test_prefix(cl, proto) -> None: cl.incr('bar') _sock_check(cl._sock, 1, proto, 'foo.bar:1|c') @mock.patch.object(random, 'random', lambda: -1) -def test_prefix_udp(): +def test_prefix_udp() -> None: """StatsClient.incr works.""" cl = _udp_client(prefix='foo') _test_prefix(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_prefix_tcp(): +def test_prefix_tcp() -> None: """TCPStatsClient.incr works.""" cl = _tcp_client(prefix='foo') _test_prefix(cl, 'tcp') @mock.patch.object(random, 'random', lambda: -1) -def test_prefix_unix_socket(): +def test_prefix_unix_socket() -> None: """UnixSocketStatsClient.incr works.""" cl = _unix_socket_client(prefix='foo') _test_prefix(cl, 'unix') -def _test_timer_manager(cl, proto): +def _test_timer_manager(cl, proto) -> None: with cl.timer('foo'): pass _timer_check(cl._sock, 1, proto, 'foo', 'ms') -def test_timer_manager_udp(): +def test_timer_manager_udp() -> None: """StatsClient.timer can be used as manager.""" cl = _udp_client() _test_timer_manager(cl, 'udp') -def test_timer_manager_tcp(): +def test_timer_manager_tcp() -> None: """TCPStatsClient.timer can be used as manager.""" cl = _tcp_client() _test_timer_manager(cl, 'tcp') -def _test_timer_decorator(cl, proto): +def _test_timer_decorator(cl, proto) -> None: @cl.timer('foo') def foo(a, b): return [a, b] @@ -529,37 +529,37 @@ def bar(a, b): _timer_check(cl._sock, 3, proto, 'bar', 'ms') -def test_timer_decorator_udp(): +def test_timer_decorator_udp() -> None: """StatsClient.timer is a thread-safe decorator (UDP).""" cl = _udp_client() _test_timer_decorator(cl, 'udp') -def test_timer_decorator_tcp(): +def test_timer_decorator_tcp() -> None: """StatsClient.timer is a thread-safe decorator (TCP).""" cl = _tcp_client() _test_timer_decorator(cl, 'tcp') -def _test_timer_capture(cl, proto): +def _test_timer_capture(cl, proto) -> None: with cl.timer('woo') as result: eq_(result.ms, None) assert isinstance(result.ms, float) -def test_timer_capture_udp(): +def test_timer_capture_udp() -> None: """You can capture the output of StatsClient.timer (UDP).""" cl = _udp_client() _test_timer_capture(cl, 'udp') -def test_timer_capture_tcp(): +def test_timer_capture_tcp() -> None: """You can capture the output of StatsClient.timer (TCP).""" cl = _tcp_client() _test_timer_capture(cl, 'tcp') -def _test_timer_context_rate(cl, proto): +def _test_timer_context_rate(cl, proto) -> None: with cl.timer('foo', rate=0.5): pass @@ -567,20 +567,20 @@ def _test_timer_context_rate(cl, proto): @mock.patch.object(random, 'random', lambda: -1) -def test_timer_context_rate_udp(): +def test_timer_context_rate_udp() -> None: """StatsClient.timer can be used as manager with rate.""" cl = _udp_client() _test_timer_context_rate(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_timer_context_rate_tcp(): +def test_timer_context_rate_tcp() -> None: """TCPStatsClient.timer can be used as manager with rate.""" cl = _tcp_client() _test_timer_context_rate(cl, 'tcp') -def test_timer_decorator_partial_function(): +def test_timer_decorator_partial_function() -> None: """TCPStatsClient.timer can be used as decorator on a partial function.""" cl = _tcp_client() @@ -592,7 +592,7 @@ def test_timer_decorator_partial_function(): _timer_check(cl._sock, 1, 'tcp', 'foo', 'ms|@0.1') -def _test_timer_decorator_rate(cl, proto): +def _test_timer_decorator_rate(cl, proto) -> None: @cl.timer('foo', rate=0.1) def foo(a, b): return [b, a] @@ -609,14 +609,14 @@ def bar(a, b: int=2, c: int=3): @mock.patch.object(random, 'random', lambda: -1) -def test_timer_decorator_rate_udp(): +def test_timer_decorator_rate_udp() -> None: """StatsClient.timer can be used as decorator with rate.""" cl = _udp_client() _test_timer_decorator_rate(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_timer_decorator_rate_tcp(): +def test_timer_decorator_rate_tcp() -> None: """TCPStatsClient.timer can be used as decorator with rate.""" cl = _tcp_client() _test_timer_decorator_rate(cl, 'tcp') @@ -630,17 +630,17 @@ def _test_timer_context_exceptions(cl, proto): _timer_check(cl._sock, 1, proto, 'foo', 'ms') -def test_timer_context_exceptions_udp(): +def test_timer_context_exceptions_udp() -> None: cl = _udp_client() _test_timer_context_exceptions(cl, 'udp') -def test_timer_context_exceptions_tcp(): +def test_timer_context_exceptions_tcp() -> None: cl = _tcp_client() _test_timer_context_exceptions(cl, 'tcp') -def _test_timer_decorator_exceptions(cl, proto): +def _test_timer_decorator_exceptions(cl, proto) -> None: @cl.timer('foo') def foo(): raise ValueError() @@ -651,36 +651,36 @@ def foo(): _timer_check(cl._sock, 1, proto, 'foo', 'ms') -def test_timer_decorator_exceptions_udp(): +def test_timer_decorator_exceptions_udp() -> None: cl = _udp_client() _test_timer_decorator_exceptions(cl, 'udp') -def test_timer_decorator_exceptions_tcp(): +def test_timer_decorator_exceptions_tcp() -> None: cl = _tcp_client() _test_timer_decorator_exceptions(cl, 'tcp') -def _test_timer_object(cl, proto): +def _test_timer_object(cl, proto) -> None: t = cl.timer('foo').start() t.stop() _timer_check(cl._sock, 1, proto, 'foo', 'ms') -def test_timer_object_udp(): +def test_timer_object_udp() -> None: """StatsClient.timer works.""" cl = _udp_client() _test_timer_object(cl, 'udp') -def test_timer_object_tcp(): +def test_timer_object_tcp() -> None: """TCPStatsClient.timer works.""" cl = _tcp_client() _test_timer_object(cl, 'tcp') -def _test_timer_object_no_send(cl, proto): +def _test_timer_object_no_send(cl, proto) -> None: t = cl.timer('foo').start() t.stop(send=False) _sock_check(cl._sock, 0, proto) @@ -689,19 +689,19 @@ def _test_timer_object_no_send(cl, proto): _timer_check(cl._sock, 1, proto, 'foo', 'ms') -def test_timer_object_no_send_udp(): +def test_timer_object_no_send_udp() -> None: """Stop StatsClient.timer without sending.""" cl = _udp_client() _test_timer_object_no_send(cl, 'udp') -def test_timer_object_no_send_tcp(): +def test_timer_object_no_send_tcp() -> None: """Stop TCPStatsClient.timer without sending.""" cl = _tcp_client() _test_timer_object_no_send(cl, 'tcp') -def _test_timer_object_rate(cl, proto): +def _test_timer_object_rate(cl, proto) -> None: t = cl.timer('foo', rate=0.5) t.start() t.stop() @@ -710,20 +710,20 @@ def _test_timer_object_rate(cl, proto): @mock.patch.object(random, 'random', lambda: -1) -def test_timer_object_rate_udp(): +def test_timer_object_rate_udp() -> None: """StatsClient.timer works with rate.""" cl = _udp_client() _test_timer_object_rate(cl, 'udp') @mock.patch.object(random, 'random', lambda: -1) -def test_timer_object_rate_tcp(): +def test_timer_object_rate_tcp() -> None: """TCPStatsClient.timer works with rate.""" cl = _tcp_client() _test_timer_object_rate(cl, 'tcp') -def _test_timer_object_no_send_twice(cl): +def _test_timer_object_no_send_twice(cl) -> None: t = cl.timer('foo').start() t.stop() @@ -731,19 +731,19 @@ def _test_timer_object_no_send_twice(cl): t.send() -def test_timer_object_no_send_twice_udp(): +def test_timer_object_no_send_twice_udp() -> None: """StatsClient.timer raises RuntimeError if send is called twice.""" cl = _udp_client() _test_timer_object_no_send_twice(cl) -def test_timer_object_no_send_twice_tcp(): +def test_timer_object_no_send_twice_tcp() -> None: """TCPStatsClient.timer raises RuntimeError if send is called twice.""" cl = _tcp_client() _test_timer_object_no_send_twice(cl) -def _test_timer_send_without_stop(cl): +def _test_timer_send_without_stop(cl) -> None: with cl.timer('foo') as t: assert t.ms is None with assert_raises(RuntimeError): @@ -755,36 +755,36 @@ def _test_timer_send_without_stop(cl): t.send() -def test_timer_send_without_stop_udp(): +def test_timer_send_without_stop_udp() -> None: """StatsClient.timer raises error if send is called before stop.""" cl = _udp_client() _test_timer_send_without_stop(cl) -def test_timer_send_without_stop_tcp(): +def test_timer_send_without_stop_tcp() -> None: """TCPStatsClient.timer raises error if send is called before stop.""" cl = _tcp_client() _test_timer_send_without_stop(cl) -def _test_timer_object_stop_without_start(cl): +def _test_timer_object_stop_without_start(cl) -> None: with assert_raises(RuntimeError): cl.timer('foo').stop() -def test_timer_object_stop_without_start_udp(): +def test_timer_object_stop_without_start_udp() -> None: """StatsClient.timer raises error if stop is called before start.""" cl = _udp_client() _test_timer_object_stop_without_start(cl) -def test_timer_object_stop_without_start_tcp(): +def test_timer_object_stop_without_start_tcp() -> None: """TCPStatsClient.timer raises error if stop is called before start.""" cl = _tcp_client() _test_timer_object_stop_without_start(cl) -def _test_pipeline(cl, proto): +def _test_pipeline(cl, proto) -> None: pipe = cl.pipeline() pipe.incr('foo') pipe.decr('bar') @@ -793,37 +793,37 @@ def _test_pipeline(cl, proto): _sock_check(cl._sock, 1, proto, 'foo:1|c\nbar:-1|c\nbaz:320.000000|ms') -def test_pipeline_udp(): +def test_pipeline_udp() -> None: """StatsClient.pipeline works.""" cl = _udp_client() _test_pipeline(cl, 'udp') -def test_pipeline_tcp(): +def test_pipeline_tcp() -> None: """TCPStatsClient.pipeline works.""" cl = _tcp_client() _test_pipeline(cl, 'tcp') -def _test_pipeline_null(cl, proto): +def _test_pipeline_null(cl, proto) -> None: pipe = cl.pipeline() pipe.send() _sock_check(cl._sock, 0, proto) -def test_pipeline_null_udp(): +def test_pipeline_null_udp() -> None: """Ensure we don't error on an empty pipeline (UDP).""" cl = _udp_client() _test_pipeline_null(cl, 'udp') -def test_pipeline_null_tcp(): +def test_pipeline_null_tcp() -> None: """Ensure we don't error on an empty pipeline (TCP).""" cl = _tcp_client() _test_pipeline_null(cl, 'tcp') -def _test_pipeline_manager(cl, proto): +def _test_pipeline_manager(cl, proto) -> None: with cl.pipeline() as pipe: pipe.incr('foo') pipe.decr('bar') @@ -831,59 +831,59 @@ def _test_pipeline_manager(cl, proto): _sock_check(cl._sock, 1, proto, 'foo:1|c\nbar:-1|c\nbaz:15|g') -def test_pipeline_manager_udp(): +def test_pipeline_manager_udp() -> None: """StatsClient.pipeline can be used as manager.""" cl = _udp_client() _test_pipeline_manager(cl, 'udp') -def test_pipeline_manager_tcp(): +def test_pipeline_manager_tcp() -> None: """TCPStatsClient.pipeline can be used as manager.""" cl = _tcp_client() _test_pipeline_manager(cl, 'tcp') -def _test_pipeline_timer_manager(cl, proto): +def _test_pipeline_timer_manager(cl, proto) -> None: with cl.pipeline() as pipe: with pipe.timer('foo'): pass _timer_check(cl._sock, 1, proto, 'foo', 'ms') -def test_pipeline_timer_manager_udp(): +def test_pipeline_timer_manager_udp() -> None: """Timer manager can be retrieve from UDP Pipeline manager.""" cl = _udp_client() _test_pipeline_timer_manager(cl, 'udp') -def test_pipeline_timer_manager_tcp(): +def test_pipeline_timer_manager_tcp() -> None: """Timer manager can be retrieve from TCP Pipeline manager.""" cl = _tcp_client() _test_pipeline_timer_manager(cl, 'tcp') -def _test_pipeline_timer_decorator(cl, proto): +def _test_pipeline_timer_decorator(cl, proto) -> None: with cl.pipeline() as pipe: @pipe.timer('foo') - def foo(): + def foo() -> None: pass foo() _timer_check(cl._sock, 1, proto, 'foo', 'ms') -def test_pipeline_timer_decorator_udp(): +def test_pipeline_timer_decorator_udp() -> None: """UDP Pipeline manager can be used as decorator.""" cl = _udp_client() _test_pipeline_timer_decorator(cl, 'udp') -def test_pipeline_timer_decorator_tcp(): +def test_pipeline_timer_decorator_tcp() -> None: """TCP Pipeline manager can be used as decorator.""" cl = _tcp_client() _test_pipeline_timer_decorator(cl, 'tcp') -def _test_pipeline_timer_object(cl, proto): +def _test_pipeline_timer_object(cl, proto) -> None: with cl.pipeline() as pipe: t = pipe.timer('foo').start() t.stop() @@ -891,57 +891,57 @@ def _test_pipeline_timer_object(cl, proto): _timer_check(cl._sock, 1, proto, 'foo', 'ms') -def test_pipeline_timer_object_udp(): +def test_pipeline_timer_object_udp() -> None: """Timer from UDP Pipeline manager works.""" cl = _udp_client() _test_pipeline_timer_object(cl, 'udp') -def test_pipeline_timer_object_tcp(): +def test_pipeline_timer_object_tcp() -> None: """Timer from TCP Pipeline manager works.""" cl = _tcp_client() _test_pipeline_timer_object(cl, 'tcp') -def _test_pipeline_empty(cl): +def _test_pipeline_empty(cl) -> None: with cl.pipeline() as pipe: pipe.incr('foo') eq_(1, len(pipe._stats)) eq_(0, len(pipe._stats)) -def test_pipeline_empty_udp(): +def test_pipeline_empty_udp() -> None: """Pipelines should be empty after a send() call (UDP).""" cl = _udp_client() _test_pipeline_empty(cl) -def test_pipeline_empty_tcp(): +def test_pipeline_empty_tcp() -> None: """Pipelines should be empty after a send() call (TCP).""" cl = _tcp_client() _test_pipeline_empty(cl) -def _test_pipeline_negative_absolute_gauge(cl, proto): +def _test_pipeline_negative_absolute_gauge(cl, proto) -> None: with cl.pipeline() as pipe: pipe.gauge('foo', -10, delta=False) pipe.incr('bar') _sock_check(cl._sock, 1, proto, 'foo:0|g\nfoo:-10|g\nbar:1|c') -def test_pipeline_negative_absolute_gauge_udp(): +def test_pipeline_negative_absolute_gauge_udp() -> None: """Negative absolute gauges use an internal pipeline (UDP).""" cl = _udp_client() _test_pipeline_negative_absolute_gauge(cl, 'udp') -def test_pipeline_negative_absolute_gauge_tcp(): +def test_pipeline_negative_absolute_gauge_tcp() -> None: """Negative absolute gauges use an internal pipeline (TCP).""" cl = _tcp_client() _test_pipeline_negative_absolute_gauge(cl, 'tcp') -def _test_big_numbers(cl, proto): +def _test_big_numbers(cl, proto) -> None: num = 1234568901234 tests = ( # Explicitly create strings so we avoid the bug we're trying to test. @@ -950,7 +950,7 @@ def _test_big_numbers(cl, proto): ('timing', 'foo:1234568901234.000000|ms'), ) - def _check(method, result): + def _check(method, result) -> None: cl._sock.reset_mock() getattr(cl, method)('foo', num) _sock_check(cl._sock, 1, proto, result) @@ -959,38 +959,38 @@ def _check(method, result): _check(method, result) -def test_big_numbers_udp(): +def test_big_numbers_udp() -> None: """Test big numbers with UDP client.""" cl = _udp_client() _test_big_numbers(cl, 'udp') -def test_big_numbers_tcp(): +def test_big_numbers_tcp() -> None: """Test big numbers with TCP client.""" cl = _tcp_client() _test_big_numbers(cl, 'tcp') -def _test_rate_no_send(cl, proto): +def _test_rate_no_send(cl, proto) -> None: cl.incr('foo', rate=0.5) _sock_check(cl._sock, 0, proto) @mock.patch.object(random, 'random', lambda: 2) -def test_rate_no_send_udp(): +def test_rate_no_send_udp() -> None: """Rate below random value prevents sending with StatsClient.incr.""" cl = _udp_client() _test_rate_no_send(cl, 'udp') @mock.patch.object(random, 'random', lambda: 2) -def test_rate_no_send_tcp(): +def test_rate_no_send_tcp() -> None: """Rate below random value prevents sending with TCPStatsClient.incr.""" cl = _tcp_client() _test_rate_no_send(cl, 'tcp') -def test_socket_error(): +def test_socket_error() -> None: """Socket error on StatsClient should be ignored.""" cl = _udp_client() cl._sock.sendto.side_effect = socket.timeout() @@ -998,7 +998,7 @@ def test_socket_error(): _sock_check(cl._sock, 1, 'udp', 'foo:1|c') -def test_pipeline_packet_size(): +def test_pipeline_packet_size() -> None: """Pipelines shouldn't send packets larger than 512 bytes (UDP only).""" sc = _udp_client() pipe = sc.pipeline() @@ -1012,7 +1012,7 @@ def test_pipeline_packet_size(): @mock.patch.object(socket, 'socket') -def test_tcp_raises_exception_to_user(mock_socket): +def test_tcp_raises_exception_to_user(mock_socket) -> None: """Socket errors in TCPStatsClient should be raised to user.""" addr = ('127.0.0.1', 1234) cl = _tcp_client(addr=addr[0], port=addr[1]) @@ -1024,7 +1024,7 @@ def test_tcp_raises_exception_to_user(mock_socket): @mock.patch.object(socket, 'socket') -def test_tcp_timeout(mock_socket): +def test_tcp_timeout(mock_socket) -> None: """Timeout on TCPStatsClient should be set on socket.""" test_timeout = 321 cl = TCPStatsClient(timeout=test_timeout) @@ -1033,7 +1033,7 @@ def test_tcp_timeout(mock_socket): @mock.patch.object(socket, 'socket') -def test_unix_socket_timeout(mock_socket): +def test_unix_socket_timeout(mock_socket) -> None: """Timeout on UnixSocketStatsClient should be set on socket.""" test_timeout = 321 cl = UnixSocketStatsClient(UNIX_SOCKET, timeout=test_timeout) From c7232dcc94eff4576cb2ba828aa3e8793991095a Mon Sep 17 00:00:00 2001 From: gram Date: Mon, 1 Nov 2021 15:53:01 +0100 Subject: [PATCH 3/8] autopep8 --- statsd/client/base.py | 12 ++++++------ statsd/client/stream.py | 4 ++-- statsd/client/timer.py | 4 ++-- statsd/client/udp.py | 4 ++-- statsd/tests.py | 7 ++++--- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/statsd/client/base.py b/statsd/client/base.py index f6a417620..f5023f7f8 100644 --- a/statsd/client/base.py +++ b/statsd/client/base.py @@ -20,10 +20,10 @@ def _send(self): def pipeline(self): raise NotImplementedError() - def timer(self, stat, rate: int=1): + def timer(self, stat, rate: int = 1): return Timer(self, stat, rate) - def timing(self, stat, delta, rate: int=1) -> None: + def timing(self, stat, delta, rate: int = 1) -> None: """ Send new timing information. @@ -34,15 +34,15 @@ def timing(self, stat, delta, rate: int=1) -> None: delta = delta.total_seconds() * 1000. self._send_stat(stat, '%0.6f|ms' % delta, rate) - def incr(self, stat, count: int=1, rate: int=1) -> None: + def incr(self, stat, count: int = 1, rate: int = 1) -> None: """Increment a stat by `count`.""" self._send_stat(stat, '%s|c' % count, rate) - def decr(self, stat, count: int=1, rate: int=1) -> None: + def decr(self, stat, count: int = 1, rate: int = 1) -> None: """Decrement a stat by `count`.""" self.incr(stat, -count, rate) - def gauge(self, stat, value, rate: int=1, delta: bool=False) -> None: + def gauge(self, stat, value, rate: int = 1, delta: bool = False) -> None: """Set a gauge value.""" if value < 0 and not delta: if rate < 1: @@ -55,7 +55,7 @@ def gauge(self, stat, value, rate: int=1, delta: bool=False) -> None: prefix = '+' if delta and value >= 0 else '' self._send_stat(stat, '%s%s|g' % (prefix, value), rate) - def set(self, stat, value, rate: int=1) -> None: + def set(self, stat, value, rate: int = 1) -> None: """Set a set value.""" self._send_stat(stat, '%s|s' % value, rate) diff --git a/statsd/client/stream.py b/statsd/client/stream.py index 5fc022c0b..8b4d96bf4 100644 --- a/statsd/client/stream.py +++ b/statsd/client/stream.py @@ -40,8 +40,8 @@ def _do_send(self, data) -> None: class TCPStatsClient(StreamClientBase): """TCP version of StatsClient.""" - def __init__(self, host: str='localhost', port: int=8125, prefix=None, - timeout=None, ipv6: bool=False) -> None: + def __init__(self, host: str = 'localhost', port: int = 8125, prefix=None, + timeout=None, ipv6: bool = False) -> None: """Create a new client.""" self._host = host self._port = port diff --git a/statsd/client/timer.py b/statsd/client/timer.py index 173f47618..bd4f6c777 100644 --- a/statsd/client/timer.py +++ b/statsd/client/timer.py @@ -21,7 +21,7 @@ def safe_wraps(wrapper, *args, **kwargs): class Timer(object): """A context manager/decorator for statsd.timing().""" - def __init__(self, client, stat, rate: int=1) -> None: + def __init__(self, client, stat, rate: int = 1) -> None: self.client = client self.stat = stat self.rate = rate @@ -53,7 +53,7 @@ def start(self): self._start_time = time_now() return self - def stop(self, send: bool=True): + def stop(self, send: bool = True): if self._start_time is None: raise RuntimeError('Timer has not started.') dt = time_now() - self._start_time diff --git a/statsd/client/udp.py b/statsd/client/udp.py index 4b3fcdec8..e5a3363ee 100644 --- a/statsd/client/udp.py +++ b/statsd/client/udp.py @@ -27,8 +27,8 @@ def _send(self) -> None: class StatsClient(StatsClientBase): """A client for statsd.""" - def __init__(self, host: str='localhost', port: int=8125, prefix=None, - maxudpsize: int=512, ipv6: bool=False) -> None: + def __init__(self, host: str = 'localhost', port: int = 8125, prefix=None, + maxudpsize: int = 512, ipv6: bool = False) -> None: """Create a new client.""" fam = socket.AF_INET6 if ipv6 else socket.AF_INET family, _, _, _, addr = socket.getaddrinfo( diff --git a/statsd/tests.py b/statsd/tests.py index 7ef1824dc..7da715f6a 100644 --- a/statsd/tests.py +++ b/statsd/tests.py @@ -34,7 +34,7 @@ } -def _udp_client(prefix=None, addr=None, port=None, ipv6: bool=False): +def _udp_client(prefix=None, addr=None, port=None, ipv6: bool = False): if not addr: addr = ADDR[0] if not port: @@ -44,7 +44,8 @@ def _udp_client(prefix=None, addr=None, port=None, ipv6: bool=False): return sc -def _tcp_client(prefix=None, addr=None, port=None, timeout=None, ipv6: bool=False): +def _tcp_client(prefix=None, addr=None, port=None, + timeout=None, ipv6: bool = False): if not addr: addr = ADDR[0] if not port: @@ -598,7 +599,7 @@ def foo(a, b): return [b, a] @cl.timer('bar', rate=0.2) - def bar(a, b: int=2, c: int=3): + def bar(a, b: int = 2, c: int = 3): return [c, b, a] eq_([2, 4], foo(4, 2)) From 0e65b32b1a6f2efd8c4ad1ee17d65c02c4a02b14 Mon Sep 17 00:00:00 2001 From: gram Date: Tue, 2 Nov 2021 11:19:56 +0100 Subject: [PATCH 4/8] manually annotate everything --- statsd/client/__init__.py | 11 ++++++++-- statsd/client/base.py | 46 +++++++++++++++++++++++---------------- statsd/client/stream.py | 43 ++++++++++++++++++++++++++++-------- statsd/client/timer.py | 36 +++++++++++++++++++++--------- statsd/client/udp.py | 21 +++++++++++++----- statsd/tests.py | 2 ++ 6 files changed, 114 insertions(+), 45 deletions(-) diff --git a/statsd/client/__init__.py b/statsd/client/__init__.py index 62cd202c7..c5ea3259f 100644 --- a/statsd/client/__init__.py +++ b/statsd/client/__init__.py @@ -1,4 +1,11 @@ from __future__ import absolute_import, division, unicode_literals -from .stream import TCPStatsClient, UnixSocketStatsClient # noqa -from .udp import StatsClient # noqa +from .stream import TCPStatsClient, UnixSocketStatsClient +from .udp import StatsClient + + +__all__ = [ + 'TCPStatsClient', + 'UnixSocketStatsClient', + 'StatsClient', +] diff --git a/statsd/client/base.py b/statsd/client/base.py index f5023f7f8..8baa0fc5b 100644 --- a/statsd/client/base.py +++ b/statsd/client/base.py @@ -3,27 +3,32 @@ import random from collections import deque from datetime import timedelta +from typing import Deque, Optional, TypeVar from .timer import Timer +P = TypeVar('P', bound='PipelineBase') + + class StatsClientBase(object): """A Base class for various statsd clients.""" + _prefix: Optional[str] - def close(self): + def close(self) -> None: """Used to close and clean up any underlying resources.""" raise NotImplementedError() - def _send(self): + def _send(self) -> None: raise NotImplementedError() - def pipeline(self): + def pipeline(self) -> 'PipelineBase': raise NotImplementedError() - def timer(self, stat, rate: int = 1): + def timer(self, stat: str, rate: float = 1) -> Timer: return Timer(self, stat, rate) - def timing(self, stat, delta, rate: int = 1) -> None: + def timing(self, stat: str, delta, rate: float = 1) -> None: """ Send new timing information. @@ -34,15 +39,15 @@ def timing(self, stat, delta, rate: int = 1) -> None: delta = delta.total_seconds() * 1000. self._send_stat(stat, '%0.6f|ms' % delta, rate) - def incr(self, stat, count: int = 1, rate: int = 1) -> None: + def incr(self, stat: str, count: int = 1, rate: float = 1) -> None: """Increment a stat by `count`.""" self._send_stat(stat, '%s|c' % count, rate) - def decr(self, stat, count: int = 1, rate: int = 1) -> None: + def decr(self, stat: str, count: int = 1, rate: float = 1) -> None: """Decrement a stat by `count`.""" self.incr(stat, -count, rate) - def gauge(self, stat, value, rate: int = 1, delta: bool = False) -> None: + def gauge(self, stat: str, value: float, rate: float = 1, delta: bool = False) -> None: """Set a gauge value.""" if value < 0 and not delta: if rate < 1: @@ -55,17 +60,17 @@ def gauge(self, stat, value, rate: int = 1, delta: bool = False) -> None: prefix = '+' if delta and value >= 0 else '' self._send_stat(stat, '%s%s|g' % (prefix, value), rate) - def set(self, stat, value, rate: int = 1) -> None: + def set(self, stat: str, value: str, rate: float = 1) -> None: """Set a set value.""" self._send_stat(stat, '%s|s' % value, rate) - def _send_stat(self, stat, value, rate) -> None: + def _send_stat(self, stat: str, value: str, rate: float) -> None: self._after(self._prepare(stat, value, rate)) - def _prepare(self, stat, value, rate): + def _prepare(self, stat: str, value: str, rate: float) -> Optional[str]: if rate < 1: if random.random() > rate: - return + return None value = '%s|@%s' % (value, rate) if self._prefix: @@ -73,29 +78,32 @@ def _prepare(self, stat, value, rate): return '%s:%s' % (stat, value) - def _after(self, data) -> None: + def _after(self, data: Optional[str]) -> None: if data: self._send(data) class PipelineBase(StatsClientBase): + _prefix: Optional[str] + _stats: Deque[str] + _client: StatsClientBase - def __init__(self, client) -> None: + def __init__(self, client: StatsClientBase) -> None: self._client = client self._prefix = client._prefix self._stats = deque() - def _send(self): + def _send(self) -> None: raise NotImplementedError() - def _after(self, data) -> None: + def _after(self, data: Optional[str]) -> None: if data is not None: self._stats.append(data) - def __enter__(self): + def __enter__(self: P) -> P: return self - def __exit__(self, typ, value, tb) -> None: + def __exit__(self, *exc_info) -> None: self.send() def send(self) -> None: @@ -103,5 +111,5 @@ def send(self) -> None: return self._send() - def pipeline(self): + def pipeline(self: P) -> P: return self.__class__(self) diff --git a/statsd/client/stream.py b/statsd/client/stream.py index 8b4d96bf4..973915e3c 100644 --- a/statsd/client/stream.py +++ b/statsd/client/stream.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, unicode_literals import socket +from typing import Optional, Union from .base import StatsClientBase, PipelineBase @@ -12,7 +13,9 @@ def _send(self) -> None: class StreamClientBase(StatsClientBase): - def connect(self): + _sock: Optional[socket.socket] + + def connect(self) -> None: raise NotImplementedError() def close(self) -> None: @@ -24,24 +27,37 @@ def reconnect(self) -> None: self.close() self.connect() - def pipeline(self): + def pipeline(self) -> StreamPipeline: return StreamPipeline(self) - def _send(self, data) -> None: + def _send(self, data: str) -> None: """Send data to statsd.""" if not self._sock: self.connect() self._do_send(data) - def _do_send(self, data) -> None: + def _do_send(self, data: str) -> None: + assert self._sock is not None self._sock.sendall(data.encode('ascii') + b'\n') class TCPStatsClient(StreamClientBase): """TCP version of StatsClient.""" - - def __init__(self, host: str = 'localhost', port: int = 8125, prefix=None, - timeout=None, ipv6: bool = False) -> None: + _host: str + _port: int + _prefix: Optional[str] + _timeout: Optional[float] + _ipv6: bool + _sock: Optional[socket.socket] + + def __init__( + self, + host: str = 'localhost', + port: int = 8125, + prefix: Optional[str] = None, + timeout: Optional[float] = None, + ipv6: bool = False, + ) -> None: """Create a new client.""" self._host = host self._port = port @@ -61,8 +77,17 @@ def connect(self) -> None: class UnixSocketStatsClient(StreamClientBase): """Unix domain socket version of StatsClient.""" - - def __init__(self, socket_path, prefix=None, timeout=None) -> None: + _socket_path: Union[str, bytes, tuple] + _prefix: Optional[str] + _timeout: Optional[float] + _sock: Optional[socket.socket] + + def __init__( + self, + socket_path: Union[str, bytes, tuple], + prefix: Optional[str] = None, + timeout: Optional[float] = None, + ) -> None: """Create a new client.""" self._socket_path = socket_path self._timeout = timeout diff --git a/statsd/client/timer.py b/statsd/client/timer.py index bd4f6c777..62025f78b 100644 --- a/statsd/client/timer.py +++ b/statsd/client/timer.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, unicode_literals import functools +from typing import TYPE_CHECKING, Callable, Optional, TypeVar # Use timer that's not susceptible to time of day adjustments. try: @@ -11,17 +12,32 @@ from time import time as time_now -def safe_wraps(wrapper, *args, **kwargs): +if TYPE_CHECKING: + from .base import StatsClientBase + + +F = TypeVar('F', bound=Callable) +C = TypeVar('C', bound=Callable) +T = TypeVar('T', bound='Timer') + + +def safe_wraps(wrapper: F, *args, **kwargs) -> Callable[[C], C]: """Safely wraps partial functions.""" while isinstance(wrapper, functools.partial): - wrapper = wrapper.func + wrapper = wrapper.func # type: ignore[assignment] return functools.wraps(wrapper, *args, **kwargs) class Timer(object): """A context manager/decorator for statsd.timing().""" + client: 'StatsClientBase' + stat: str + rate: float + ms: Optional[float] + _sent: bool + _start_time: Optional[float] - def __init__(self, client, stat, rate: int = 1) -> None: + def __init__(self, client: 'StatsClientBase', stat: str, rate: float = 1) -> None: self.client = client self.stat = stat self.rate = rate @@ -29,7 +45,7 @@ def __init__(self, client, stat, rate: int = 1) -> None: self._sent = False self._start_time = None - def __call__(self, f): + def __call__(self, f: F) -> F: """Thread-safe timing function decorator.""" @safe_wraps(f) def _wrapped(*args, **kwargs): @@ -39,21 +55,21 @@ def _wrapped(*args, **kwargs): finally: elapsed_time_ms = 1000.0 * (time_now() - start_time) self.client.timing(self.stat, elapsed_time_ms, self.rate) - return _wrapped + return _wrapped # type: ignore[return-value] - def __enter__(self): + def __enter__(self: T) -> T: return self.start() - def __exit__(self, typ, value, tb) -> None: + def __exit__(self, *exc_info) -> None: self.stop() - def start(self): + def start(self: T) -> T: self.ms = None self._sent = False self._start_time = time_now() return self - def stop(self, send: bool = True): + def stop(self: T, send: bool = True) -> T: if self._start_time is None: raise RuntimeError('Timer has not started.') dt = time_now() - self._start_time @@ -62,7 +78,7 @@ def stop(self, send: bool = True): self.send() return self - def send(self): + def send(self) -> None: if self.ms is None: raise RuntimeError('No data recorded.') if self._sent: diff --git a/statsd/client/udp.py b/statsd/client/udp.py index e5a3363ee..a33786787 100644 --- a/statsd/client/udp.py +++ b/statsd/client/udp.py @@ -1,13 +1,14 @@ from __future__ import absolute_import, division, unicode_literals import socket +from typing import Optional from .base import StatsClientBase, PipelineBase class Pipeline(PipelineBase): - def __init__(self, client) -> None: + def __init__(self, client: 'StatsClient') -> None: super(Pipeline, self).__init__(client) self._maxudpsize = client._maxudpsize @@ -27,8 +28,18 @@ def _send(self) -> None: class StatsClient(StatsClientBase): """A client for statsd.""" - def __init__(self, host: str = 'localhost', port: int = 8125, prefix=None, - maxudpsize: int = 512, ipv6: bool = False) -> None: + _addr: tuple + _sock: Optional[socket.socket] + _maxudpsize: int + + def __init__( + self, + host: str = 'localhost', + port: int = 8125, + prefix: Optional[str] = None, + maxudpsize: int = 512, + ipv6: bool = False, + ) -> None: """Create a new client.""" fam = socket.AF_INET6 if ipv6 else socket.AF_INET family, _, _, _, addr = socket.getaddrinfo( @@ -38,7 +49,7 @@ def __init__(self, host: str = 'localhost', port: int = 8125, prefix=None, self._prefix = prefix self._maxudpsize = maxudpsize - def _send(self, data) -> None: + def _send(self, data: str) -> None: """Send data to statsd.""" try: self._sock.sendto(data.encode('ascii'), self._addr) @@ -51,5 +62,5 @@ def close(self) -> None: self._sock.close() self._sock = None - def pipeline(self): + def pipeline(self) -> Pipeline: return Pipeline(self) diff --git a/statsd/tests.py b/statsd/tests.py index 7da715f6a..4e93d1231 100644 --- a/statsd/tests.py +++ b/statsd/tests.py @@ -1030,6 +1030,7 @@ def test_tcp_timeout(mock_socket) -> None: test_timeout = 321 cl = TCPStatsClient(timeout=test_timeout) cl.incr('foo') + assert cl._sock is not None cl._sock.settimeout.assert_called_once_with(test_timeout) @@ -1039,4 +1040,5 @@ def test_unix_socket_timeout(mock_socket) -> None: test_timeout = 321 cl = UnixSocketStatsClient(UNIX_SOCKET, timeout=test_timeout) cl.incr('foo') + assert cl._sock is not None cl._sock.settimeout.assert_called_once_with(test_timeout) From afb7cb095933f6bfb4a669cf487cf5c124eeb4e2 Mon Sep 17 00:00:00 2001 From: gram Date: Tue, 2 Nov 2021 11:49:41 +0100 Subject: [PATCH 5/8] add py.typed --- MANIFEST.in | 1 + setup.py | 1 - statsd/py.typed | 0 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 statsd/py.typed diff --git a/MANIFEST.in b/MANIFEST.in index 72ce170db..9c7227b08 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include AUTHORS CHANGES LICENSE MANIFEST.in README.rst include setup.py +include statsd/py.typed recursive-include docs * diff --git a/setup.py b/setup.py index 197757f58..6be33d6a2 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,6 @@ license='MIT', packages=find_packages(), include_package_data=True, - package_data={'': ['README.rst']}, test_suite='nose.collector', classifiers=[ 'Development Status :: 5 - Production/Stable', diff --git a/statsd/py.typed b/statsd/py.typed new file mode 100644 index 000000000..e69de29bb From 3ece35a0faff91129db1603dba42a773cf0e39a5 Mon Sep 17 00:00:00 2001 From: gram Date: Tue, 2 Nov 2021 11:52:22 +0100 Subject: [PATCH 6/8] wrap long lines --- statsd/client/base.py | 3 ++- statsd/client/timer.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/statsd/client/base.py b/statsd/client/base.py index 8baa0fc5b..b5b76c7ce 100644 --- a/statsd/client/base.py +++ b/statsd/client/base.py @@ -47,7 +47,8 @@ def decr(self, stat: str, count: int = 1, rate: float = 1) -> None: """Decrement a stat by `count`.""" self.incr(stat, -count, rate) - def gauge(self, stat: str, value: float, rate: float = 1, delta: bool = False) -> None: + def gauge(self, stat: str, value: float, rate: float = 1, + delta: bool = False) -> None: """Set a gauge value.""" if value < 0 and not delta: if rate < 1: diff --git a/statsd/client/timer.py b/statsd/client/timer.py index 62025f78b..c18e2c434 100644 --- a/statsd/client/timer.py +++ b/statsd/client/timer.py @@ -37,7 +37,8 @@ class Timer(object): _sent: bool _start_time: Optional[float] - def __init__(self, client: 'StatsClientBase', stat: str, rate: float = 1) -> None: + def __init__(self, client: 'StatsClientBase', + stat: str, rate: float = 1) -> None: self.client = client self.stat = stat self.rate = rate From ba59343d099b4a11a60018cceded8b8f1e80f399 Mon Sep 17 00:00:00 2001 From: gram Date: Tue, 2 Nov 2021 12:05:09 +0100 Subject: [PATCH 7/8] annotate delta --- statsd/client/base.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/statsd/client/base.py b/statsd/client/base.py index b5b76c7ce..9a614f17e 100644 --- a/statsd/client/base.py +++ b/statsd/client/base.py @@ -3,7 +3,7 @@ import random from collections import deque from datetime import timedelta -from typing import Deque, Optional, TypeVar +from typing import Deque, Optional, TypeVar, Union from .timer import Timer @@ -28,7 +28,12 @@ def pipeline(self) -> 'PipelineBase': def timer(self, stat: str, rate: float = 1) -> Timer: return Timer(self, stat, rate) - def timing(self, stat: str, delta, rate: float = 1) -> None: + def timing( + self, + stat: str, + delta: Union[timedelta, float], + rate: float = 1, + ) -> None: """ Send new timing information. @@ -47,8 +52,13 @@ def decr(self, stat: str, count: int = 1, rate: float = 1) -> None: """Decrement a stat by `count`.""" self.incr(stat, -count, rate) - def gauge(self, stat: str, value: float, rate: float = 1, - delta: bool = False) -> None: + def gauge( + self, + stat: str, + value: float, + rate: float = 1, + delta: bool = False, + ) -> None: """Set a gauge value.""" if value < 0 and not delta: if rate < 1: From 6adab9eb7c4d27b66078d74b0f989886a4ff7847 Mon Sep 17 00:00:00 2001 From: gram Date: Fri, 5 Nov 2021 08:59:54 +0100 Subject: [PATCH 8/8] add mypy and flake8 into tox --- requirements.txt | 3 ++- tox.ini | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index 6aa9af6b5..d3f5f6073 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ mock==1.0.1 nose==1.2.1 -flake8==1.7.0 +flake8 +mypy==0.910 diff --git a/tox.ini b/tox.ini index f75a9787b..1731ded49 100644 --- a/tox.ini +++ b/tox.ini @@ -2,10 +2,16 @@ envlist = py27,pypy,py34,py35,py36,py37 [testenv] -deps= - mock==1.0.1 - nose==1.2.1 - coverage==3.5.2 - -commands= +deps = -r requirements.txt +commands = nosetests statsd --with-coverage --cover-package=statsd [] + +[testenv:mypy] +deps = -r requirements.txt +commands = + mypy statsd/ + +[testenv:flake8] +deps = -r requirements.txt +commands = + flake8 statsd/