From 0edcc812994df273b86f815db66476c22a67969f Mon Sep 17 00:00:00 2001
From: Hubert Kario <hubert@kario.pl>
Date: Thu, 23 Feb 2023 22:37:29 +0100
Subject: [PATCH 1/9] Drop support for python 3.3 and python 3.4

As Github actions is dropping support for Ubuntu-18.04 images and
those are the only ones that support easy execution of python 3.3 and
python 3.4 environments, support for py3.3 and 3.4 would be hard to
ensure.

For old systems python 2.6 and 2.7 support will remain.
---
 .github/workflows/ci.yml   |  8 --------
 README.md                  |  2 +-
 build-requirements-3.3.txt |  9 ---------
 build-requirements-3.4.txt |  7 -------
 setup.py                   |  4 +---
 src/ecdsa/_compat.py       | 25 +++++--------------------
 tox.ini                    | 12 +++---------
 7 files changed, 10 insertions(+), 57 deletions(-)
 delete mode 100644 build-requirements-3.3.txt
 delete mode 100644 build-requirements-3.4.txt

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 112719c0..44c169ea 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -47,14 +47,6 @@ jobs:
             os: ubuntu-18.04
             python-version: 2.7
             tox-env: gmpy2py27
-          - name: py3.3
-            os: ubuntu-18.04
-            python-version: 3.3
-            tox-env: py33
-          - name: py3.4
-            os: ubuntu-18.04
-            python-version: 3.4
-            tox-env: py34
           - name: py3.5
             os: ubuntu-18.04
             python-version: 3.5
diff --git a/README.md b/README.md
index 11a00160..7cffeb17 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ curves over prime fields.
 ## Dependencies
 
 This library uses only Python and the 'six' package. It is compatible with
-Python 2.6, 2.7, and 3.3+. It also supports execution on alternative
+Python 2.6, 2.7, and 3.5+. It also supports execution on alternative
 implementations like pypy and pypy3.
 
 If `gmpy2` or `gmpy` is installed, they will be used for faster arithmetic.
diff --git a/build-requirements-3.3.txt b/build-requirements-3.3.txt
deleted file mode 100644
index d817d2b3..00000000
--- a/build-requirements-3.3.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-git+https://github.com/tomato42/coveralls-python.git@add-py26#egg=coveralls
-pluggy<0.6
-tox<3
-wheel<0.30
-virtualenv==15.2.0
-enum34
-hypothesis<3.44
-coverage<5.0
-urllib3<=1.25.8
diff --git a/build-requirements-3.4.txt b/build-requirements-3.4.txt
deleted file mode 100644
index ee734d11..00000000
--- a/build-requirements-3.4.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-tox
-git+https://github.com/tomato42/coveralls-python.git@add-py26#egg=coveralls
-hypothesis
-pytest>=4.6.0
-PyYAML<5.3
-coverage
-attrs<21
diff --git a/setup.py b/setup.py
index f6a1dd1e..6294117a 100755
--- a/setup.py
+++ b/setup.py
@@ -27,15 +27,13 @@
     package_dir={"": "src"},
     license="MIT",
     cmdclass=commands,
-    python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*",
+    python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
     classifiers=[
         "Programming Language :: Python",
         "Programming Language :: Python :: 2",
         "Programming Language :: Python :: 2.6",
         "Programming Language :: Python :: 2.7",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.3",
-        "Programming Language :: Python :: 3.4",
         "Programming Language :: Python :: 3.5",
         "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
diff --git a/src/ecdsa/_compat.py b/src/ecdsa/_compat.py
index 83d41a5f..f409a1a2 100644
--- a/src/ecdsa/_compat.py
+++ b/src/ecdsa/_compat.py
@@ -91,28 +91,13 @@ def int_to_bytes(val, length=None, byteorder="big"):
         raise ValueError("Only 'big' or 'little' endian supported")
 
 else:
-    if sys.version_info < (3, 4):  # pragma: no branch
-        # on python 3.3 hmac.hmac.update() accepts only bytes, on newer
-        # versions it does accept memoryview() also
-        def hmac_compat(data):
-            if not isinstance(data, bytes):  # pragma: no branch
-                return bytes(data)
-            return data
-
-        def normalise_bytes(buffer_object):
-            """Cast the input into array of bytes."""
-            if not buffer_object:
-                return b""
-            return memoryview(buffer_object).cast("B")
 
-    else:
-
-        def hmac_compat(data):
-            return data
+    def hmac_compat(data):
+        return data
 
-        def normalise_bytes(buffer_object):
-            """Cast the input into array of bytes."""
-            return memoryview(buffer_object).cast("B")
+    def normalise_bytes(buffer_object):
+        """Cast the input into array of bytes."""
+        return memoryview(buffer_object).cast("B")
 
     def compat26_str(val):
         return val
diff --git a/tox.ini b/tox.ini
index 46864866..390b7373 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,18 +1,13 @@
 
 [tox]
-envlist = py26, py27, py33, py34, py35, py36, py37, py38, py39, py310, py311, py, pypy, pypy3, gmpy2py27, gmpy2py39, gmpy2py310, gmpypy27, gmpypy39, gmpypy310, codechecks
+envlist = py26, py27, py35, py36, py37, py38, py39, py310, py311, py, pypy, pypy3, gmpy2py27, gmpy2py39, gmpy2py310, gmpypy27, gmpypy39, gmpypy310, codechecks
 
 [testenv]
 deps =
-     py{33}: py<1.5
-     py{33}: pytest<3.3
-     py{33}: enum34
-     py{33}: hypothesis<3.44
      py{26}: unittest2
      py{26}: hypothesis<3
-     py{34}: attrs<21
-     py{26,27,34,35,36,37,38,39,310,311,py,py3}: pytest
-     py{27,34,35,36,37,38,39,310,311,py,py3}: hypothesis
+     py{26,27,35,36,37,38,39,310,311,py,py3}: pytest
+     py{27,35,36,37,38,39,310,311,py,py3}: hypothesis
      gmpy2py{27,39,310,311}: gmpy2
      gmpypy{27,39,310,311}: gmpy
      gmpy{2py27,2py39,2py310,2py311,py27,py39,py310,py311}: pytest
@@ -31,7 +26,6 @@ deps =
      py27_old_gmpy2: hypothesis
      py: pytest
      py: hypothesis
-     py{33}: wheel<0.30
      coverage
 commands = coverage run --branch -m pytest {posargs:src/ecdsa}
 

From 7a0101901a3704a997fcfabdb34b34b887aa0bf2 Mon Sep 17 00:00:00 2001
From: Hubert Kario <hubert@kario.pl>
Date: Thu, 23 Feb 2023 22:39:59 +0100
Subject: [PATCH 2/9] upgrade containers to ubuntu-20.04

as ubuntu-18.04 is no longer supported, we need to start using newer
version
---
 .github/workflows/ci.yml | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 44c169ea..f16d881c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,35 +24,35 @@ jobs:
             python-version: "3.10"
             tox-env: py310
           - name: py2.7
-            os: ubuntu-18.04
+            os: ubuntu-20.04
             python-version: 2.7
             tox-env: py27
           - name: py2.7 with old gmpy
-            os: ubuntu-18.04
+            os: ubuntu-20.04
             python-version: 2.7
             tox-env: py27_old_gmpy
           - name: py2.7 with old gmpy2
-            os: ubuntu-18.04
+            os: ubuntu-20.04
             python-version: 2.7
             tox-env: py27_old_gmpy2
           - name: py2.7 with old six
-            os: ubuntu-18.04
+            os: ubuntu-20.04
             python-version: 2.7
             tox-env: py27_old_six
           - name: py2.7 with gmpy
-            os: ubuntu-18.04
+            os: ubuntu-20.04
             python-version: 2.7
             tox-env: gmpypy27
           - name: py2.7 with gmpy2
-            os: ubuntu-18.04
+            os: ubuntu-20.04
             python-version: 2.7
             tox-env: gmpy2py27
           - name: py3.5
-            os: ubuntu-18.04
+            os: ubuntu-20.04
             python-version: 3.5
             tox-env: py35
           - name: py3.6
-            os: ubuntu-18.04
+            os: ubuntu-20.04
             python-version: 3.6
             tox-env: py36
           - name: py3.7
@@ -93,7 +93,7 @@ jobs:
             tox-env: pypy3
           # special configurations
           - name: py2.7 with instrumental
-            os: ubuntu-18.04
+            os: ubuntu-20.04
             python-version: 2.7
             opt-deps: ['instrumental']
           - name: code checks

From c2d9fe1d8cc1daaa2036751fc3a41b9ec7b7e140 Mon Sep 17 00:00:00 2001
From: Hubert Kario <hubert@kario.pl>
Date: Thu, 23 Feb 2023 22:41:24 +0100
Subject: [PATCH 3/9] use release version of python 3.11 for testing

---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f16d881c..976359a8 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -81,7 +81,7 @@ jobs:
             tox-env: gmpy2py310
           - name: py3.11
             os: ubuntu-latest
-            python-version: '3.11.0-beta.3'
+            python-version: '3.11'
             tox-env: py311
           - name: pypy
             os: ubuntu-latest

From 5a114fddcc4c336d53180270d81d6aa42c5ee95b Mon Sep 17 00:00:00 2001
From: Hubert Kario <hubert@kario.pl>
Date: Fri, 24 Feb 2023 15:41:11 +0100
Subject: [PATCH 4/9] add testing on python 3.12

# Conflicts:
#	tox.ini
---
 .github/workflows/ci.yml |  4 ++++
 setup.py                 |  1 +
 tox.ini                  | 20 +++++++++++++-------
 3 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 976359a8..8e2920c0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -83,6 +83,10 @@ jobs:
             os: ubuntu-latest
             python-version: '3.11'
             tox-env: py311
+          - name: py3.12
+            os: ubuntu-latest
+            python-version: '3.12.0-alpha.5'
+            tox-env: py312
           - name: pypy
             os: ubuntu-latest
             python-version: pypy-2.7
diff --git a/setup.py b/setup.py
index 6294117a..a9ae2439 100755
--- a/setup.py
+++ b/setup.py
@@ -41,6 +41,7 @@
         "Programming Language :: Python :: 3.9",
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
+        "Programming Language :: Python :: 3.12",
     ],
     install_requires=["six>=1.9.0"],
     extras_require={"gmpy2": "gmpy2", "gmpy": "gmpy"},
diff --git a/tox.ini b/tox.ini
index 390b7373..c9b07c9a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,17 +1,17 @@
 
 [tox]
-envlist = py26, py27, py35, py36, py37, py38, py39, py310, py311, py, pypy, pypy3, gmpy2py27, gmpy2py39, gmpy2py310, gmpypy27, gmpypy39, gmpypy310, codechecks
+envlist = py26, py27, py35, py36, py37, py38, py39, py310, py311, py312, py, pypy, pypy3, gmpy2py27, gmpy2py39, gmpy2py310, gmpypy27, gmpypy39, gmpypy310, codechecks
 
 [testenv]
 deps =
      py{26}: unittest2
      py{26}: hypothesis<3
-     py{26,27,35,36,37,38,39,310,311,py,py3}: pytest
-     py{27,35,36,37,38,39,310,311,py,py3}: hypothesis
-     gmpy2py{27,39,310,311}: gmpy2
-     gmpypy{27,39,310,311}: gmpy
-     gmpy{2py27,2py39,2py310,2py311,py27,py39,py310,py311}: pytest
-     gmpy{2py27,2py39,2py310,2py311,py27,py39,py310,py311}: hypothesis
+     py{26,27,35,36,37,38,39,310,311,312,py,py3}: pytest
+     py{27,35,36,37,38,39,310,311,312,py,py3}: hypothesis
+     gmpy2py{27,39,310,311,312}: gmpy2
+     gmpypy{27,39,310,311,312}: gmpy
+     gmpy{2py27,2py39,2py310,2py311,2py312,py27,py39,py310,py311,py312}: pytest
+     gmpy{2py27,2py39,2py310,2py311,2py312,py27,py39,py310,py311,py312}: hypothesis
 # six==1.9.0 comes from setup.py install_requires
      py27_old_six: six==1.9.0
      py27_old_six: pytest
@@ -50,6 +50,9 @@ basepython=python3.10
 [testenv:gmpypy311]
 basepython=python3.11
 
+[testenv:gmpypy312]
+basepython=python3.12
+
 [testenv:gmpy2py27]
 basepython=python2.7
 
@@ -62,6 +65,9 @@ basepython=python3.10
 [testenv:gmpy2py311]
 basepython=python3.11
 
+[testenv:gmpy2py312]
+basepython=python3.12
+
 [testenv:instrumental]
 basepython = python2.7
 deps =

From f10e6d9be1251f9a109c0c2002ed12fee818b883 Mon Sep 17 00:00:00 2001
From: Hubert Kario <hubert@kario.pl>
Date: Thu, 23 Feb 2023 22:57:35 +0100
Subject: [PATCH 5/9] skip tests breaking coverage on pypy

---
 src/ecdsa/test_jacobi.py | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/ecdsa/test_jacobi.py b/src/ecdsa/test_jacobi.py
index 71fb33e5..b3e69c00 100644
--- a/src/ecdsa/test_jacobi.py
+++ b/src/ecdsa/test_jacobi.py
@@ -558,8 +558,12 @@ def test_pickle(self):
         self.assertEqual(pickle.loads(pickle.dumps(pj)), pj)
 
     @settings(**NO_OLD_SETTINGS)
+    @pytest.mark.skipif(
+        platform.python_implementation() == "PyPy",
+        reason="threading on PyPy breaks coverage",
+    )
     @given(st.integers(min_value=1, max_value=10))
-    def test_multithreading(self, thread_num):
+    def test_multithreading(self, thread_num):  # pragma: no cover
         # ensure that generator's precomputation table is filled
         generator_112r2 * 2
 
@@ -592,10 +596,12 @@ def runner(generator):
         )
 
     @pytest.mark.skipif(
-        platform.system() == "Windows",
-        reason="there are no signals on Windows",
+        platform.system() == "Windows"
+        or platform.python_implementation() == "PyPy",
+        reason="there are no signals on Windows, and threading breaks coverage"
+        " on PyPy",
     )
-    def test_multithreading_with_interrupts(self):
+    def test_multithreading_with_interrupts(self):  # pragma: no cover
         thread_num = 10
         # ensure that generator's precomputation table is filled
         generator_112r2 * 2

From 50c1fa44f35b9d383ffbc3e045a94419f6c347d1 Mon Sep 17 00:00:00 2001
From: Hubert Kario <hubert@kario.pl>
Date: Thu, 23 Feb 2023 23:03:24 +0100
Subject: [PATCH 6/9] use recommended convention for testing that no warnings
 were raised

---
 src/ecdsa/test_der.py | 30 ++++++++++++------------------
 1 file changed, 12 insertions(+), 18 deletions(-)

diff --git a/src/ecdsa/test_der.py b/src/ecdsa/test_der.py
index 0ca5bd7f..833d12d8 100644
--- a/src/ecdsa/test_der.py
+++ b/src/ecdsa/test_der.py
@@ -144,26 +144,22 @@ def test_old_call_convention(self):
 
     def test_new_call_convention(self):
         """This is how it should be called now."""
-        warnings.simplefilter("always")
-        with pytest.warns(None) as warns:
+        # make sure no warnings are raised
+        with warnings.catch_warnings():
+            warnings.simplefilter("error")
             der = encode_bitstring(b"\xff", 0)
 
-        # verify that new call convention doesn't raise Warnings
-        self.assertEqual(len(warns), 0)
-
         self.assertEqual(der, b"\x03\x02\x00\xff")
 
     def test_implicit_unused_bits(self):
         """
         Writing bit string with already included the number of unused bits.
         """
-        warnings.simplefilter("always")
-        with pytest.warns(None) as warns:
+        # make sure no warnings are raised
+        with warnings.catch_warnings():
+            warnings.simplefilter("error")
             der = encode_bitstring(b"\x00\xff", None)
 
-        # verify that new call convention doesn't raise Warnings
-        self.assertEqual(len(warns), 0)
-
         self.assertEqual(der, b"\x03\x02\x00\xff")
 
     def test_explicit_unused_bits(self):
@@ -203,22 +199,20 @@ def test_old_call_convention(self):
         self.assertEqual(rest, b"")
 
     def test_new_call_convention(self):
-        warnings.simplefilter("always")
-        with pytest.warns(None) as warns:
+        # make sure no warnings are raised
+        with warnings.catch_warnings():
+            warnings.simplefilter("error")
             bits, rest = remove_bitstring(b"\x03\x02\x00\xff", 0)
 
-        self.assertEqual(len(warns), 0)
-
         self.assertEqual(bits, b"\xff")
         self.assertEqual(rest, b"")
 
     def test_implicit_unexpected_unused(self):
-        warnings.simplefilter("always")
-        with pytest.warns(None) as warns:
+        # make sure no warnings are raised
+        with warnings.catch_warnings():
+            warnings.simplefilter("error")
             bits, rest = remove_bitstring(b"\x03\x02\x00\xff", None)
 
-        self.assertEqual(len(warns), 0)
-
         self.assertEqual(bits, (b"\xff", 0))
         self.assertEqual(rest, b"")
 

From 62f8e7c2b647eb526bd55e945ca0c01dae09ea96 Mon Sep 17 00:00:00 2001
From: Hubert Kario <hubert@kario.pl>
Date: Fri, 24 Feb 2023 15:10:26 +0100
Subject: [PATCH 7/9] test_jacobi: make coverage realistic

Since it's a test case, only one course of action is actually expected
so there is no branch at this point, even if the with: never returns
---
 src/ecdsa/test_jacobi.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/ecdsa/test_jacobi.py b/src/ecdsa/test_jacobi.py
index b3e69c00..322b568c 100644
--- a/src/ecdsa/test_jacobi.py
+++ b/src/ecdsa/test_jacobi.py
@@ -50,7 +50,7 @@ def test_add_with_different_curves(self):
         p_a = PointJacobi.from_affine(generator_256)
         p_b = PointJacobi.from_affine(generator_224)
 
-        with self.assertRaises(ValueError):
+        with self.assertRaises(ValueError):  # pragma: no branch
             p_a + p_b
 
     def test_compare_different_curves(self):

From 27db17c27427b0d852b5edfc5ddc98bfc56274f9 Mon Sep 17 00:00:00 2001
From: Hubert Kario <hubert@kario.pl>
Date: Fri, 24 Feb 2023 15:57:43 +0100
Subject: [PATCH 8/9] test_numbertheory: make test coverage realistic

---
 src/ecdsa/test_numbertheory.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/ecdsa/test_numbertheory.py b/src/ecdsa/test_numbertheory.py
index 8bc787f1..239eaa8c 100644
--- a/src/ecdsa/test_numbertheory.py
+++ b/src/ecdsa/test_numbertheory.py
@@ -182,7 +182,7 @@ def st_comp_with_com_fac(draw):
     # select at most 20 lists (returned numbers),
     # each having at most 30 primes (factors) including none (then the number
     # will be 1)
-    comp_primes = draw(
+    comp_primes = draw(  # pragma: no branch
         st.integers(min_value=1, max_value=20).flatmap(
             lambda n: st.lists(
                 st.lists(st.sampled_from(primes), max_size=30),
@@ -225,7 +225,7 @@ def st_comp_no_com_fac(draw):
 
     # select at most 20 lists, each having at most 30 primes
     # selected from the leftover_primes list
-    number_primes = draw(
+    number_primes = draw(  # pragma: no branch
         st.integers(min_value=1, max_value=20).flatmap(
             lambda n: st.lists(
                 st.lists(st.sampled_from(leftover_primes), max_size=30),

From cc26d74b5a5cdc5c0134a4bfc56e604a0e9e00a9 Mon Sep 17 00:00:00 2001
From: Hubert Kario <hubert@kario.pl>
Date: Fri, 24 Feb 2023 16:01:51 +0100
Subject: [PATCH 9/9] test_pyecdsa: make coverage realistic

we want to test both with and without openssl command present
---
 src/ecdsa/test_pyecdsa.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/ecdsa/test_pyecdsa.py b/src/ecdsa/test_pyecdsa.py
index d61f5083..59c876cc 100644
--- a/src/ecdsa/test_pyecdsa.py
+++ b/src/ecdsa/test_pyecdsa.py
@@ -1359,12 +1359,12 @@ def do_test_to_openssl(self, curve, hash_name="SHA1"):
     OPENSSL_SUPPORTED_TYPES = set()
     try:
         if "-rawin" in run_openssl("pkeyutl -help"):
-            OPENSSL_SUPPORTED_TYPES = set(
+            OPENSSL_SUPPORTED_TYPES = set(  # pragma: no branch
                 c.lower()
                 for c in ("ED25519", "ED448")
                 if c in run_openssl("list -public-key-methods")
             )
-    except SubprocessError:
+    except SubprocessError:  # pragma: no cover
         pass
 
     def do_eddsa_test_to_openssl(self, curve):