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):