chore(python): migrate to pyproject.toml and uv

This commit is contained in:
matejcik
2025-09-03 12:46:19 +02:00
committed by matejcik
parent 54df8f4b3b
commit 85a3463587
16 changed files with 134 additions and 143 deletions

View File

@@ -0,0 +1 @@
`trezorlib.__version__` is deprecated, use `importlib.metadata.version("trezor")`.

View File

@@ -0,0 +1 @@
Dropped support for Python 3.8.

View File

@@ -0,0 +1 @@
Changed build system to uv.

2
python/.gitignore vendored
View File

@@ -8,3 +8,5 @@ MANIFEST
*.py.cache
/.tox
mypy_report
uv.lock
/.venv

View File

@@ -1,13 +0,0 @@
recursive-include bash_completion.d *.sh
include tools/*
graft src
graft tests
graft stubs
include AUTHORS README.md COPYING CHANGELOG.md
include requirements*.txt
include tox.ini pyrightconfig.json
exclude src/trezorlib/_proto_messages.mako
exclude tests/*.bin
global-exclude *.pyc
global-exclude */__pycache__/*

91
python/pyproject.toml Normal file
View File

@@ -0,0 +1,91 @@
[project]
name = "trezor"
version = "0.14.0"
description = "Python library for communicating with Trezor Hardware Wallet"
readme = "README.md"
license = "LGPL-3.0-only"
license-files = ["COPYING"]
authors = [
{name = "Trezor", email = "info@trezor.io"}
]
maintainers = [
{name = "matejcik", email = "jan.matejek@satoshilabs.com"}
]
keywords = ["trezor", "hardware", "wallet", "cryptocurrency", "bitcoin", "ethereum"]
classifiers = [
"Operating System :: POSIX :: Linux",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS :: MacOS X",
"Programming Language :: Python :: 3 :: Only",
]
requires-python = ">=3.9"
dependencies = [
"ecdsa>=0.9",
"mnemonic>=0.20",
"shamir-mnemonic>=0.3.0",
"slip10>=1.0.1",
"requests>=2.4.0",
"click>=8,<8.3",
"libusb1>=1.6.4",
"construct>=2.9,!=2.10.55",
"typing_extensions>=4.7.1",
"construct-classes>=0.1.2",
"cryptography>=41",
"noiseprotocol>=0.3.1,<0.4.0",
]
[project.optional-dependencies]
hidapi = ["hidapi>=0.7.99.post20"]
ethereum = ["web3>=5"]
qt-widgets = ["PyQt5"]
extra = ["Pillow>=10"]
stellar = ["stellar-sdk>=6"]
full = [
"hidapi>=0.7.99.post20",
"web3>=5",
"PyQt5",
"Pillow>=10",
"stellar-sdk>=6",
]
[project.urls]
Homepage = "https://github.com/trezor/trezor-firmware/tree/main/python"
Repository = "https://github.com/trezor/trezor-firmware"
Documentation = "https://github.com/trezor/trezor-firmware/tree/main/python"
[project.scripts]
trezorctl = "trezorlib.cli.trezorctl:cli"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.uv]
package = true
[tool.hatch.build.targets.wheel]
packages = ["src/trezorlib"]
[tool.hatch.build.targets.sdist]
exclude = [
"/.*",
"/helper-scripts",
"/CHANGELOG.unreleased",
"/default.nix",
"/Makefile",
"/towncrier.toml",
"tests/*.bin",
"src/trezorlib/_proto_messages.mako",
]
[dependency-groups]
dev = [
"autoflake>=2.3.1",
"black>=25",
"flake8>=2.3.0",
"isort>=5.13.2",
"pytest>=8.3.5",
"pytest-random-order>=1.2.0",
"tox>=4.25.0",
"tox-uv>=1.13.1",
]

View File

@@ -1,4 +0,0 @@
hidapi>=0.7.99.post20
web3>=5
Pillow>=10
stellar-sdk>=6

View File

@@ -1,12 +0,0 @@
ecdsa>=0.9
mnemonic>=0.20
shamir-mnemonic>=0.3.0
slip10>=1.0.1
requests>=2.4.0
click>=7,<8.2
libusb1>=1.6.4
construct>=2.9,!=2.10.55
typing_extensions>=4.7.1
construct-classes>=0.1.2
cryptography>=41
noiseprotocol>=0.3.1,<0.4.0

View File

@@ -2,6 +2,7 @@
filename = *.py
exclude =
.tox/,
.venv/,
build/,
dist/,
vendor/,

View File

@@ -1,76 +0,0 @@
#!/usr/bin/env python3
# This file is part of the Trezor project.
#
# Copyright (C) SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import re
from pathlib import Path
from setuptools import find_packages, setup
CWD = Path(__file__).resolve().parent
install_requires = (CWD / "requirements.txt").read_text().splitlines()
extras_require = {
"hidapi": ["hidapi>=0.7.99.post20"],
"ethereum": ["web3>=5"],
"qt-widgets": ["PyQt5"],
"extra": ["Pillow>=10"],
"stellar": ["stellar-sdk>=6"],
}
extras_require["full"] = sum(extras_require.values(), [])
def find_version():
version_file = (CWD / "src" / "trezorlib" / "__init__.py").read_text()
version_match = re.search(r"^__version__ = \"(.*)\"$", version_file, re.M)
if version_match:
return version_match.group(1)
else:
raise RuntimeError("Version string not found")
setup(
name="trezor",
version=find_version(),
author="Trezor",
author_email="info@trezor.io",
license="LGPLv3",
description="Python library for communicating with Trezor Hardware Wallet",
long_description=(CWD / "README.md").read_text()
+ "\n\n"
+ (CWD / "CHANGELOG.md").read_text(),
long_description_content_type="text/markdown",
url="https://github.com/trezor/trezor-firmware/tree/master/python",
package_data={"trezorlib": ["py.typed"]},
packages=find_packages("src"),
package_dir={"": "src"},
entry_points={"console_scripts": ["trezorctl=trezorlib.cli.trezorctl:cli"]},
install_requires=install_requires,
extras_require=extras_require,
python_requires=">=3.8",
include_package_data=True,
zip_safe=False,
classifiers=[
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Operating System :: POSIX :: Linux",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS :: MacOS X",
"Programming Language :: Python :: 3 :: Only",
],
)

View File

@@ -14,4 +14,15 @@
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
__version__ = "0.14.0"
import importlib.metadata
import warnings
def __getattr__(name: str) -> str:
if name == "__version__":
warnings.warn(
"__version__ is deprecated and will be removed in 0.15.0, use importlib.metadata.version('trezor') instead",
DeprecationWarning,
)
return importlib.metadata.version("trezor")
raise AttributeError(f"module {__name__} has no attribute {name}")

View File

@@ -16,6 +16,7 @@
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import importlib.metadata
import json
import logging
import os
@@ -24,7 +25,7 @@ from typing import TYPE_CHECKING, Any, Callable, Iterable, Optional, TypeVar, ca
import click
from .. import __version__, log, messages, protobuf
from .. import log, messages, protobuf
from ..transport import DeviceIsBusy, enumerate_devices
from ..transport.session import Session
from ..transport.udp import UdpTransport
@@ -141,7 +142,7 @@ class TrezorctlGroup(AliasedGroup):
# This means that there is no reasonable way to use `hasattr` to detect where we
# are, unless we want to look at the private `_result_callback` attribute.
# Instead, we look at Click version and hope for the best.
from click import __version__ as click_version
click_version = importlib.metadata.version("click")
if click_version.startswith("7."):
return super().resultcallback() # type: ignore [Cannot access attribute]
@@ -194,7 +195,7 @@ def configure_logging(verbose: int) -> None:
"--record",
help="Record screen changes into a specified directory.",
)
@click.version_option(version=__version__)
@click.version_option(package_name="trezor")
@click.pass_context
def cli_main(
ctx: click.Context,
@@ -310,7 +311,7 @@ def list_devices(no_resolve: bool) -> Optional[Iterable["Transport"]]:
@cli.command()
def version() -> str:
"""Show version of trezorctl/trezorlib."""
return __version__
return importlib.metadata.version("trezor")
#

View File

@@ -165,7 +165,7 @@ def test_simple_message():
uvarint=12345678910,
svarint=-12345678910,
bool=True,
bytes=b"\xDE\xAD\xCA\xFE",
bytes=b"\xde\xad\xca\xfe",
unicode="Příliš žluťoučký kůň úpěl ďábelské ódy 😊",
enum=SomeEnum.Five,
)
@@ -177,7 +177,7 @@ def test_simple_message():
assert retr.uvarint == 12345678910
assert retr.svarint == -12345678910
assert retr.bool is True
assert retr.bytes == b"\xDE\xAD\xCA\xFE"
assert retr.bytes == b"\xde\xad\xca\xfe"
assert retr.unicode == "Příliš žluťoučký kůň úpěl ďábelské ódy 😊"
assert retr.enum == SomeEnum.Five
assert retr.enum == 5

View File

@@ -25,6 +25,7 @@ encfs --standard --extpass=./encfs_aes_getpass.py ~/.crypt ~/crypt
"""
import hashlib
import importlib.metadata
import json
import os
import sys
@@ -36,7 +37,8 @@ from trezorlib.client import TrezorClient
from trezorlib.tools import Address
from trezorlib.transport import enumerate_devices
version_tuple = tuple(map(int, trezorlib.__version__.split(".")))
trezor_version = importlib.metadata.version("trezor")
version_tuple = tuple(map(int, trezor_version.split(".")))
if not (0, 11) <= version_tuple < (0, 14):
raise RuntimeError("trezorlib version mismatch (required: 0.13, 0.12, or 0.11)")

View File

@@ -5,21 +5,17 @@
[tox]
envlist =
py{38,39,310,311,312}-{minimal,default,full}
py{38,39,310,311,312}-click{7,80}
py{38,39,310,311,312}-click81
py{39,310,311,312,313}-{minimal,default,full}
py{39,310,311,312,313}-click8{0,1}
py{310,311,312,313}-click82
[testenv]
deps =
-rrequirements.txt
!minimal: pytest>=3.6
!minimal: pytest-random-order
!minimal: importlib-metadata!=0.21
full: -rrequirements-optional.txt
py312: setuptools
runner = uv-venv-runner
dependency_groups =
!minimal: dev
extras =
full: full
commands =
# Generate local files
python setup.py build
# Working in the local directory, try to compile all bytecode
python -m compileall src tests
# Smoke-test trezorctl
@@ -27,12 +23,10 @@ commands =
# Run test suite
!minimal: pytest -c setup.cfg --random-order tests
[testenv:py{38,39,310,311,312}-click{7,80,81}]
deps =
-rrequirements.txt
click7: click>=7,<8
click80: click>=8.0,<8.1
click81: click>=8.1,<8.2
[testenv:py{39,310,311,312,313}-click{80,81,82}]
commands =
click80: uv pip install "click>=8.0,<8.1"
click81: uv pip install "click>=8.1,<8.2"
click82: uv pip install "click>=8.2,<8.3"
# Smoke-test trezorctl
trezorctl --version

View File

@@ -8,7 +8,6 @@ import click
VERSION_RE = re.compile(r"^(\d+)[.](\d+)[.](\d+)$")
HEADER_LINE_RE = re.compile(r"^#define ([A-Z_]+) \S+$")
PYTHON_VERSION_RE = re.compile(r'^__version__ = "\d+[.]\d+[.]\d+"$', flags=re.MULTILINE)
def bump_header(filename, **kwargs):
@@ -29,14 +28,8 @@ def bump_header(filename, **kwargs):
fh.write(line)
def bump_python(filename, new_version):
with open(filename, "r+") as fh:
contents = fh.read()
result = PYTHON_VERSION_RE.sub(f'__version__ = "{new_version}"', contents)
fh.seek(0)
fh.truncate(0)
fh.write(result)
def bump_python(subdir: Path, new_version: str):
subprocess.check_call(["uv", "version", new_version], cwd=subdir)
def hex_lit(version):
@@ -89,9 +82,7 @@ def cli(project, version):
VERSION_PATCH=patch,
)
elif parts[-1] == "python":
bump_python(
project / "src" / "trezorlib" / "__init__.py", f"{major}.{minor}.{patch}"
)
bump_python(project / "python", f"{major}.{minor}.{patch}")
else:
raise click.ClickException(f"Unknown project {project}.")