Skip to content

Commit bb2d628

Browse files
committed
Support Python 3.10 through 3.14
Use Python 3.10-compatible enum, typing, and integer conversion forms while preserving existing behavior. Advertise and test Python 3.10 through 3.14, with non-blocking Python 3.15 prerelease coverage. Security: no intended change to codex32 encoding, recovery, or wallet behavior. Validation: - python -m pytest -q (864 passed) - python -m mypy src/codex32 - python -m ruff check --target-version py310 . - python -m ruff format --check . - python -m build --no-isolation - python -m twine check built artifacts Refs #18.
1 parent 341ccc7 commit bb2d628

7 files changed

Lines changed: 51 additions & 10 deletions

File tree

‎.github/workflows/python-package.yml‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,35 @@ jobs:
3131
- run: python tools/differential_correction.py --verify
3232
- run: python -m build --no-isolation
3333
- run: python tools/verify_wheel_environment.py
34+
35+
compatibility:
36+
runs-on: ubuntu-latest
37+
strategy:
38+
fail-fast: false
39+
matrix:
40+
python-version: ["3.10", "3.11", "3.14"]
41+
steps:
42+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
43+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
44+
with:
45+
python-version: ${{ matrix.python-version }}
46+
- run: python -m pip install --upgrade pip
47+
- run: python -m pip install --require-hashes -r requirements/cli-build-dependencies.txt
48+
- run: python -m pip install --no-build-isolation -e '.[dev]'
49+
- run: python -m pip check
50+
- run: python -m pytest -q
51+
52+
prerelease:
53+
continue-on-error: true
54+
runs-on: ubuntu-latest
55+
steps:
56+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
57+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
58+
with:
59+
python-version: "3.15"
60+
allow-prereleases: true
61+
- run: python -m pip install --upgrade pip
62+
- run: python -m pip install --require-hashes -r requirements/cli-build-dependencies.txt
63+
- run: python -m pip install --no-build-isolation -e '.[dev]'
64+
- run: python -m pip check
65+
- run: python -m pytest -q

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ obtain an independent review before relying on it with funds. See
2727

2828
## Install
2929

30-
Python 3.12 or 3.13 is required. The installed package has no third-party
30+
Python 3.10 through 3.14 is supported. The installed package has no third-party
3131
runtime dependencies. To install it with the pinned build backend, run these
3232
commands from the project folder:
3333

‎pyproject.toml‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ authors = [
1010
]
1111
description = "Python reference implementation for codex32 (BIP93) and codex32-encoded master seeds."
1212
readme = "README.md"
13-
requires-python = ">=3.12,<3.14"
13+
requires-python = ">=3.10"
1414
license = "MIT"
1515
license-files = ["LICENSE*"]
1616
maintainers = [
@@ -22,8 +22,11 @@ classifiers = [
2222
"Intended Audience :: Developers",
2323
"Programming Language :: Python :: 3",
2424
"Programming Language :: Python :: 3 :: Only",
25+
"Programming Language :: Python :: 3.10",
26+
"Programming Language :: Python :: 3.11",
2527
"Programming Language :: Python :: 3.12",
2628
"Programming Language :: Python :: 3.13",
29+
"Programming Language :: Python :: 3.14",
2730
"Operating System :: OS Independent",
2831
"Topic :: Security :: Cryptography",
2932
"Topic :: Software Development :: Libraries",
@@ -52,7 +55,7 @@ where = ["src"]
5255
testpaths = ["tests"]
5356

5457
[tool.mypy]
55-
python_version = "3.12"
58+
python_version = "3.10"
5659
strict = true
5760

5861
[tool.ruff]

‎src/codex32/generation.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from collections.abc import Callable, Sequence
77
from collections.abc import Set as AbstractSet
88
from dataclasses import dataclass
9-
from typing import Never, SupportsIndex, cast
9+
from typing import NoReturn, SupportsIndex, cast
1010

1111
from codex32._bip32 import _valid_root
1212
from codex32.bech32 import CHARSET, _u5_to_chars, convertbits
@@ -198,7 +198,7 @@ class CreationCeremony:
198198
def __init__(self) -> None:
199199
raise TypeError("use a CreationCeremony class constructor")
200200

201-
def __reduce_ex__(self, _protocol: SupportsIndex) -> Never:
201+
def __reduce_ex__(self, _protocol: SupportsIndex) -> NoReturn:
202202
raise TypeError("creation ceremonies cannot be copied or serialized")
203203

204204
@classmethod

‎src/codex32/profiles/__init__.py‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@
22

33
from __future__ import annotations
44

5-
from enum import StrEnum
6-
from typing import TYPE_CHECKING
5+
from enum import Enum
6+
from typing import TYPE_CHECKING, TypeAlias, Union
77

88
from codex32.errors import UnknownProfile
99

1010
if TYPE_CHECKING:
1111
from codex32.profiles.bip39 import _Bip39Rules
1212
from codex32.profiles.cl32 import _Cl32Rules
1313
from codex32.profiles.ms32 import _Ms32Rules
14-
type _ProfileRules = _Ms32Rules | _Cl32Rules | _Bip39Rules
14+
_ProfileRules: TypeAlias = Union["_Ms32Rules", "_Cl32Rules", "_Bip39Rules"]
1515

1616

17-
class Profile(StrEnum):
17+
class Profile(str, Enum):
18+
__str__ = str.__str__
19+
1820
MS = "ms"
1921
CL = "cl"
2022
BIP39_12W = "bip39_12w"

‎src/codex32/profiles/bip39.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def validate_payload(self, symbols: tuple[int, ...], index: str) -> None:
5757
semantic = value >> self.outer_padding
5858
embedded = semantic & ((1 << self.checksum_bits) - 1)
5959
entropy = semantic >> self.checksum_bits
60-
entropy_bytes = entropy.to_bytes(self.entropy_bits // 8)
60+
entropy_bytes = entropy.to_bytes(self.entropy_bits // 8, "big")
6161
expected = hashlib.sha256(entropy_bytes).digest()[0] >> (8 - self.checksum_bits)
6262
if embedded != expected:
6363
raise InvalidBip39Checksum("embedded BIP39 entropy checksum is invalid")

‎tests/test_bip93.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
from codex32.errors import CodexError, InvalidChecksum
3333

3434

35+
def test_profile_string_matches_its_value() -> None:
36+
assert str(Profile.MS) == "ms"
37+
38+
3539
def test_vector_1_parts_and_seed() -> None:
3640
secret = parse_codex32(VECTOR_1["secret_s"])
3741
assert isinstance(secret, MasterSeed)

0 commit comments

Comments
 (0)