From 91fe57097f88cb284f009e4995454bd291ac3329 Mon Sep 17 00:00:00 2001
From: Matthew Hughes <matthewhughes934@gmail.com>
Date: Thu, 7 Jul 2022 19:51:55 +0100
Subject: [PATCH] Drop unsupported pythons

Drop support for pythons <3.7 (including python 2).

Since this makes it part of the standard library, replace `mock` with
`unittest.mock` in tests.

This also required replacing `nose` with `pytest` in tests This is
because `nose` is not under active development[1], and the version
specified fails to run on python3 (and newer versions fail to run on
python3.10[2]):

    $ python -m nose
    Traceback (most recent call last):
      File "/usr/lib/python3.10/runpy.py", line 187, in _run_module_as_main
        mod_name, mod_spec, code = _get_module_details(mod_name, _Error)
      File "/usr/lib/python3.10/runpy.py", line 146, in _get_module_details
        return _get_module_details(pkg_main_name, error)
      File "/usr/lib/python3.10/runpy.py", line 110, in _get_module_details
        __import__(pkg_name)
      File "/home/mjh/src/pystatsd/.venv/lib/python3.10/site-packages/nose/__init__.py", line 1, in <module>
        from nose.core import collector, main, run, run_exit, runmodule
      File "/home/mjh/src/pystatsd/.venv/lib/python3.10/site-packages/nose/core.py", line 143
        print "%s version %s" % (os.path.basename(sys.argv[0]), __version__)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

`pytest` is pinned at the lowest version supporting python3.10[3],
similarly for coverage[4], `flake8` was also bump to a more recent
version.

[1] https://nose.readthedocs.io/en/latest/#note-to-users
[2] https://github.com/nose-devs/nose/issues/1122
[3] https://docs.pytest.org/en/7.1.x/changelog.html#pytest-6-2-5-2021-08-29
[4] https://coverage.readthedocs.io/en/6.4.1/changes.html#version-6-0-2021-10-03
---
 docs/contributing.rst |  4 ++--
 requirements.txt      |  5 ++---
 setup.py              |  9 +++++----
 statsd/tests.py       | 37 ++++++++++++++++---------------------
 tox.ini               |  7 +++----
 5 files changed, 28 insertions(+), 34 deletions(-)

diff --git a/docs/contributing.rst b/docs/contributing.rst
index 63639a597..1c6d12c5a 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -36,8 +36,8 @@ You can also run the tests with tox::
 
     $ tox
 
-Tox will run the tests in Pythons 2.5, 2.6, 2.7, 3.2, 3.3, 3.4, and
-PyPy, if they're available.
+Tox will run the tests in Pythons 3.7, 3.8, 3.9, 3.10 and
+PyPy3, if they're available.
 
 
 Writing Tests
diff --git a/requirements.txt b/requirements.txt
index 6aa9af6b5..cc825cac3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,2 @@
-mock==1.0.1
-nose==1.2.1
-flake8==1.7.0
+flake8>=4.0
+pytest>=6.2.5
diff --git a/setup.py b/setup.py
index 197757f58..4e75d41b5 100644
--- a/setup.py
+++ b/setup.py
@@ -14,6 +14,7 @@
     include_package_data=True,
     package_data={'': ['README.rst']},
     test_suite='nose.collector',
+    python_requires='>=3.7',
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Environment :: Web Environment',
@@ -21,13 +22,13 @@
         'License :: OSI Approved :: MIT License',
         'Operating System :: OS Independent',
         'Programming Language :: Python',
-        'Programming Language :: Python :: 2',
-        'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.5',
-        'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
+        'Programming Language :: Python :: 3.9',
+        'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: Implementation :: CPython',
+        'Programming Language :: Python :: Implementation :: PyPy',
         'Topic :: Software Development :: Libraries :: Python Modules',
     ],
 )
diff --git a/statsd/tests.py b/statsd/tests.py
index 12fc410cd..a92f83be4 100644
--- a/statsd/tests.py
+++ b/statsd/tests.py
@@ -5,9 +5,7 @@
 import socket
 from datetime import timedelta
 from unittest import SkipTest
-
-import mock
-from nose.tools import eq_
+from unittest import mock
 
 from statsd import StatsClient
 from statsd import TCPStatsClient
@@ -66,7 +64,7 @@ def _unix_socket_client(prefix=None, socket_path=None):
 
 def _timer_check(sock, count, proto, start, end):
     send = send_method[proto](sock)
-    eq_(send.call_count, count)
+    assert send.call_count == count
     value = send.call_args[0][0].decode('ascii')
     exp = re.compile(r'^%s:\d+|%s$' % (start, end))
     assert exp.match(value)
@@ -74,14 +72,11 @@ def _timer_check(sock, count, proto, start, end):
 
 def _sock_check(sock, count, proto, val=None, addr=None):
     send = send_method[proto](sock)
-    eq_(send.call_count, count)
+    assert send.call_count == count
     if not addr:
         addr = ADDR
     if val is not None:
-        eq_(
-            send.call_args,
-            make_val[proto](val, addr),
-        )
+        assert send.call_args == make_val[proto](val, addr)
 
 
 class assert_raises(object):
@@ -443,7 +438,7 @@ def _test_prepare(cl, proto):
 
     def _check(o, s, v, r):
         with mock.patch.object(random, 'random', lambda: -1):
-            eq_(o, cl._prepare(s, v, r))
+            assert o == cl._prepare(s, v, r)
 
     for o, (s, v, r) in tests:
         _check(o, s, v, r)
@@ -519,13 +514,13 @@ def bar(a, b):
 
     # make sure it works with more than one decorator, called multiple
     # times, and that parameters are handled correctly
-    eq_([4, 2], foo(4, 2))
+    assert [4, 2] == foo(4, 2)
     _timer_check(cl._sock, 1, proto, 'foo', 'ms')
 
-    eq_([2, 4], bar(4, 2))
+    assert [2, 4] == bar(4, 2)
     _timer_check(cl._sock, 2, proto, 'bar', 'ms')
 
-    eq_([6, 5], bar(5, 6))
+    assert [6, 5] == bar(5, 6)
     _timer_check(cl._sock, 3, proto, 'bar', 'ms')
 
 
@@ -543,7 +538,7 @@ def test_timer_decorator_tcp():
 
 def _test_timer_capture(cl, proto):
     with cl.timer('woo') as result:
-        eq_(result.ms, None)
+        assert result.ms is None
     assert isinstance(result.ms, float)
 
 
@@ -587,7 +582,7 @@ def test_timer_decorator_partial_function():
     foo = functools.partial(lambda x: x * x, 2)
     func = cl.timer('foo')(foo)
 
-    eq_(4, func())
+    assert 4 == func()
 
     _timer_check(cl._sock, 1, 'tcp', 'foo', 'ms|@0.1')
 
@@ -601,10 +596,10 @@ def foo(a, b):
     def bar(a, b=2, c=3):
         return [c, b, a]
 
-    eq_([2, 4], foo(4, 2))
+    assert [2, 4] == foo(4, 2)
     _timer_check(cl._sock, 1, proto, 'foo', 'ms|@0.1')
 
-    eq_([3, 2, 5], bar(5))
+    assert [3, 2, 5] == bar(5)
     _timer_check(cl._sock, 2, proto, 'bar', 'ms|@0.2')
 
 
@@ -906,8 +901,8 @@ def test_pipeline_timer_object_tcp():
 def _test_pipeline_empty(cl):
     with cl.pipeline() as pipe:
         pipe.incr('foo')
-        eq_(1, len(pipe._stats))
-    eq_(0, len(pipe._stats))
+        assert 1 == len(pipe._stats)
+    assert 0 == len(pipe._stats)
 
 
 def test_pipeline_empty_udp():
@@ -1006,7 +1001,7 @@ def test_pipeline_packet_size():
         # 32 * 16 = 512, so this will need 2 packets.
         pipe.incr('sixteen_char_str')
     pipe.send()
-    eq_(2, sc._sock.sendto.call_count)
+    assert 2 == sc._sock.sendto.call_count
     assert len(sc._sock.sendto.call_args_list[0][0][0]) <= 512
     assert len(sc._sock.sendto.call_args_list[1][0][0]) <= 512
 
@@ -1017,7 +1012,7 @@ def test_tcp_raises_exception_to_user(mock_socket):
     addr = ('127.0.0.1', 1234)
     cl = _tcp_client(addr=addr[0], port=addr[1])
     cl.incr('foo')
-    eq_(1, cl._sock.sendall.call_count)
+    assert 1 == cl._sock.sendall.call_count
     cl._sock.sendall.side_effect = socket.error
     with assert_raises(socket.error):
         cl.incr('foo')
diff --git a/tox.ini b/tox.ini
index f75a9787b..f992052a6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,9 +3,8 @@ envlist = py27,pypy,py34,py35,py36,py37
 
 [testenv]
 deps=
-    mock==1.0.1
-    nose==1.2.1
-    coverage==3.5.2
+    coverage>=6.0
+    pytest>=6.2.5
 
 commands=
-    nosetests statsd --with-coverage --cover-package=statsd []
+    pytest statsd/tests.py