Skip to content

Commit d8d5eb2

Browse files
committed
Minor refactor pyproject.toml and typing fixes.
Update uv.lock to match pyproject.toml changes. Updates to improve function type returns: - tests/conftest.py - tests/test_firmware.py - typings/esptool/__init__.pyi
1 parent dc2c808 commit d8d5eb2

File tree

6 files changed

+178
-82
lines changed

6 files changed

+178
-82
lines changed

pyproject.toml

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,45 @@ classifiers = [
99
"License :: OSI Approved :: MIT License",
1010
"Programming Language :: Python :: 3",
1111
"Operating System :: OS Independent",
12+
"Programming Language :: Python :: 3",
13+
"Programming Language :: Python :: 3.8",
14+
"Programming Language :: Python :: 3.9",
15+
"Programming Language :: Python :: 3.10",
16+
"Programming Language :: Python :: 3.11",
17+
"Programming Language :: Python :: 3.12",
18+
"Programming Language :: Python :: 3.13",
1219
"Topic :: Software Development :: Embedded Systems",
20+
"Topic :: Software Development :: Libraries",
21+
"Typing :: Typed",
1322
]
1423
requires-python = ">=3.8"
24+
dynamic = ["version"]
1525
dependencies = [
1626
"esptool>=4.6.2",
1727
"rich>=10.12.0",
1828
"more-itertools>=8.8.0",
1929
"typing-extensions>=4.12",
2030
]
21-
dynamic = ["version"]
31+
optional-dependencies.littlefs = [
32+
"littlefs-python>=0.12.0",
33+
]
2234

2335
[project.scripts] # The entry points for the command line tools
2436
mp-image-tool-esp32 = "mp_image_tool_esp32.main:main"
2537

26-
[project.optional-dependencies]
27-
littlefs = ["littlefs-python>=0.12.0"]
28-
2938
[dependency-groups]
30-
typing = ["mypy>=0.910", "types-requests>=2.32.0.20240914", "types-pyserial>=3.5"]
39+
typing = [
40+
"mypy>=0.910", "types-requests>=2.32", "types-pyserial>=3.5", "types-pyyaml>=6.0"
41+
]
3142
test = [
3243
{include-group = "typing"},
33-
"ruff>=0.6.7", "pytest>=8.3.2", "pytest-cov>=3.0.0", "requests>=2.32.3",
34-
"pyyaml>=6.0.2", "tox>=4.22.0", "tox-uv>=0.3.0",
44+
"ruff>=0.6.7", "pytest>=8.3.2", "pytest-cov>=3.0.0", "pytest-sugar>=1.0.0",
45+
"requests>=2.32.3", "pyyaml>=6.0.2", "tox>=4.22.0", "tox-uv>=1.13",
3546
]
3647
dev = [
3748
{include-group = "test"},
38-
"keyring>=25", # For uv publish --keyring-provider=subprocess
39-
# Used by .git/hooks/post-{commit,checkout} to update _version.py:
40-
# uv run --frozen hatch build --hooks-only
41-
"hatch>=1.12.0",
42-
"hatch-vcs>=0.3.0", # For building and updating _version.py
49+
# For building and updating _version.py
50+
"hatch>=1.12.0", "hatch-vcs>=0.3.0", "pip>=25.0.1",
4351
]
4452

4553
[build-system]
@@ -48,9 +56,8 @@ build-backend = "hatchling.build"
4856

4957
# Build python packages and update version number
5058
[tool.hatch]
51-
build.targets.wheel.packages = ["src/mp_image_tool_esp32"]
52-
version.source = "vcs" # Get the version from git, eg: 0.0.6.dev0+g1234567
5359
build.hooks.vcs.version-file = "src/mp_image_tool_esp32/_version.py"
60+
version.source = "vcs" # Get the version from git, eg: 0.0.6.dev0+g1234567
5461
# Drop the local version part (eg: +g1234567) or pypi will reject package
5562
version.raw-options.local_scheme = "no-local-version"
5663
# A manually triggered github release workflow may generate a new tag
@@ -64,40 +71,40 @@ version.raw-options.git_describe_command = [
6471
"--match", "v*.[0-9][0-9][0-9]",
6572
]
6673

67-
# https://tox.wiki/en/latest/config.html#pyproject-toml-native
68-
[tool.tox]
69-
requires = ["tox>=4.22", "tox-uv>=1.13"]
70-
env_list = [
71-
"clean", "typing", "lint", "format",
72-
"3.8", "3.9", "3.10", "3.11", "3.12", "3.13"
73-
]
74-
env.clean.commands = [["coverage", "erase"]] # Cleanup coverage data
75-
env.clean.skip_install = true
76-
env.typing.commands = [["mypy"]]
77-
env.lint.commands = [["ruff", "check"]]
78-
env.format.commands = [["ruff", "format", "--check"]]
79-
env.3.8.commands = [["pytest", "--cov=mp_image_tool_esp32"]]
80-
env.3.13.commands = [["pytest", "--cov=mp_image_tool_esp32"]]
81-
env_run_base.commands = [["pytest"]]
82-
env_run_base.dependency_groups = ["test"]
83-
env_run_base.extras = ["littlefs"] # Include optional dependencies
84-
env_run_base.package = "wheel" # Build package wheel and install into environments
85-
env_run_base.wheel_build_env = ".pkg" # Re-use one wheel for each environment
86-
8774
[tool.mypy]
88-
files = ["src"]
75+
files = ["src", "tests"]
8976
python_version = "3.9"
77+
mypy_path = "typings"
9078
disallow_untyped_defs = true
9179
warn_return_any = true
9280
warn_unused_ignores = true
93-
mypy_path = "typings"
9481

9582
[tool.ruff]
96-
include = ["src/**/*.py"]
97-
exclude = ["_version.py"]
83+
exclude = ["_version.py", "tests/data"]
9884
lint.extend-select = ["I"] # Enable ruffs isort rules (for compat with vscode ruff)
9985

10086
[tool.coverage]
87+
run.source = ["src", "tests"]
10188
run.omit = ["_version.py"]
10289
report.skip_covered = true
10390
append = true
91+
92+
# https://tox.wiki/en/latest/config.html#pyproject-toml-native
93+
[tool.tox]
94+
env_list = [
95+
"clean", "typing", "lint", "format",
96+
"3.8", "3.9", "3.10", "3.11", "3.12", "3.13"
97+
]
98+
labels.static = ["clean", "typing", "lint", "format"]
99+
env.clean.commands = [["coverage", "erase"]] # Cleanup coverage data
100+
env.clean.skip_install = true
101+
env.typing.commands = [["mypy"]]
102+
env.lint.commands = [["ruff", "check"]]
103+
env.format.commands = [["ruff", "format", "--check"]]
104+
env_run_base.commands = [["pytest", {replace = "posargs", extend = true}]]
105+
env_run_base.dependency_groups = ["test"]
106+
env_run_base.extras = ["littlefs"] # Include optional dependencies
107+
env_run_base.package = "editable" # Build package wheel and install into environments
108+
env_run_base.runner = "uv-venv-runner" # We love uv
109+
env.3.13.commands = [["pytest", "--cov", {replace = "posargs", extend = true}]]
110+
env.3.13.labels = ["coverage", "latest"]

src/mp_image_tool_esp32/logger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def render_message(
4747
console=console,
4848
highlighter=NullHighlighter(),
4949
markup=True,
50-
show_time=False, # Don't show the timem level or path in log messages
50+
show_time=False, # Don't show the time, level or path in log messages
5151
show_level=False,
5252
show_path=False,
5353
rich_tracebacks=True,

tests/conftest.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class Options(Namespace):
7575
options: Options = Options()
7676

7777

78-
def pytest_addoption(parser: Namespace):
78+
def pytest_addoption(parser: Namespace) -> None:
7979
parser.addoption(
8080
"--port",
8181
dest="port",
@@ -88,7 +88,7 @@ def pytest_addoption(parser: Namespace):
8888
dest="firmware",
8989
action="store",
9090
default="",
91-
help="Nmae of the firmware file to use",
91+
help="Name of the firmware file to use",
9292
)
9393
parser.addoption(
9494
"--args",
@@ -106,7 +106,7 @@ def pytest_addoption(parser: Namespace):
106106
)
107107

108108

109-
def download_firmware_files():
109+
def download_firmware_files() -> None:
110110
"""Download the firmware files if they are not present in 'datadir'."""
111111
if not datadir.exists():
112112
datadir.mkdir()
@@ -120,7 +120,7 @@ def download_firmware_files():
120120

121121

122122
## pytest_configure is called after command line options have been parsed
123-
def pytest_configure(config: Config):
123+
def pytest_configure(config: Config) -> None:
124124
global options
125125
global firmware_file
126126
download_firmware_files() # Download firmware files if needed
@@ -140,7 +140,7 @@ def pytest_configure(config: Config):
140140
def my_setup(
141141
capsys: pytest.CaptureFixture[str],
142142
caplog: pytest.LogCaptureFixture,
143-
):
143+
) -> None:
144144
global capsys_, caplog_
145145
capsys_ = capsys
146146
caplog_ = caplog
@@ -207,7 +207,7 @@ def testdir(tmp_path_factory: Any) -> Path:
207207
"""A fixture to create a temporary directory for testing (session scope).
208208
Will set the current working directory to the temporary directory
209209
and return the directory as a Path object."""
210-
path: Path = tmp_path_factory.mktemp("test_dir") # type: ignore
210+
path: Path = tmp_path_factory.mktemp("test_dir")
211211
assert isinstance(path, Path)
212212
os.chdir(path)
213213
return path

tests/test_firmware.py

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,39 +26,31 @@ def mpi_check_output(firmware: Path, args: str) -> None:
2626
assert_output(OUTPUTS[args], output)
2727

2828

29-
# Unnecessary if running mp_image_tool_esp32 as a module rather than a script
30-
# def test_python_version(firmware: Path):
31-
# mpi_run(firmware)
32-
# match = re.search(r"Running pytest .*Python ([0-9.]+)", log_messages())
33-
# assert match is not None
34-
# assert match.group(1) == platform.python_version()
35-
36-
37-
def test_firmware_file_valid(firmware: Path):
29+
def test_firmware_file_valid(firmware: Path) -> None:
3830
mpi_check(firmware, "")
3931

4032

41-
def test_table_ota(firmware: Path):
33+
def test_table_ota(firmware: Path) -> None:
4234
mpi_check_output(firmware, "--table ota --app-size 0x1f0B")
4335

4436

45-
def test_table_default(firmware: Path):
37+
def test_table_default(firmware: Path) -> None:
4638
mpi_check_output(firmware, "--table default")
4739

4840

49-
def test_table_custom(firmware: Path):
41+
def test_table_custom(firmware: Path) -> None:
5042
mpi_check_output(firmware, "--table nvs=7B,factory=2M,vfs=1M,vfs2=fat:0")
5143

5244

53-
def test_delete(firmware: Path):
45+
def test_delete(firmware: Path) -> None:
5446
mpi_check_output(firmware, "--delete phy_init")
5547

5648

57-
def test_resize(firmware: Path):
49+
def test_resize(firmware: Path) -> None:
5850
mpi_check_output(firmware, "--resize vfs=1M")
5951

6052

61-
def test_add(firmware: Path):
53+
def test_add(firmware: Path) -> None:
6254
mpi_check_output(firmware, "--resize vfs=1M --add data=fat:50B")
6355

6456

@@ -71,19 +63,19 @@ def mpi_check_output_flash_size(firmware: Path, args: str) -> bool:
7163
return True
7264

7365

74-
def test_flash_size(firmware: Path):
66+
def test_flash_size(firmware: Path) -> None:
7567
if not mpi_check_output_flash_size(firmware, "--flash-size 8M"):
7668
pytest.skip("Skipping test_flash_size because device flash size is too small")
7769

7870

79-
def test_flash_size_vfs(firmware: Path):
71+
def test_flash_size_vfs(firmware: Path) -> None:
8072
if not mpi_check_output_flash_size(firmware, "--flash-size 8M --resize vfs=0"):
8173
pytest.skip(
8274
"Skipping test_flash_size_vfs because device flash size is too small"
8375
)
8476

8577

86-
def test_read(firmware: Path, bootloader: bytes):
78+
def test_read(firmware: Path, bootloader: bytes) -> None:
8779
out = Path("out.bin")
8880
mpi_run(firmware, f"--read bootloader={out.name}")
8981
output = out.read_bytes()
@@ -95,20 +87,20 @@ def test_read(firmware: Path, bootloader: bytes):
9587
assert output == bootloader
9688

9789

98-
def test_write(firmware: Path):
90+
def test_write(firmware: Path) -> None:
9991
input = bytes(range(32)) * 8 # 256 data bytes to write
10092
infile, outfile = Path("input.bin"), Path("out2.bin")
10193
infile.write_bytes(bytes(range(32)) * 8)
10294
mpi_run(firmware, f"--write phy_init={infile}")
10395
mpi_run(firmware, f"--read phy_init={outfile}")
10496
output = outfile.read_bytes()
10597
assert len(output) == 4096
106-
output = output.rstrip(b"\xFF")
98+
output = output.rstrip(b"\xff")
10799
assert len(output) == len(input)
108100
assert output == input
109101

110102

111-
def test_erase(firmware: Path):
103+
def test_erase(firmware: Path) -> None:
112104
Path("input.bin").write_bytes(bytes(range(32)) * 8)
113105
mpi_run(firmware, "--write phy_init=input.bin")
114106
mpi_run(firmware, "--read phy_init=output1.bin")
@@ -117,20 +109,20 @@ def test_erase(firmware: Path):
117109
input = Path("input.bin").read_bytes()
118110
output1 = Path("output1.bin").read_bytes()
119111
output2 = Path("output2.bin").read_bytes()
120-
assert output1.rstrip(b"\xFF") == input
112+
assert output1.rstrip(b"\xff") == input
121113
assert len(output2) == 4096
122114
assert output2.count(0xFF) == len(output2)
123115

124116

125-
def test_extract_app(firmware: Path, app_image: bytes):
117+
def test_extract_app(firmware: Path, app_image: bytes) -> None:
126118
app = Path("app.bin")
127119
mpi_run(firmware, "--extract-app", output=app)
128120
output = app.read_bytes()
129121
assert len(output) == len(app_image)
130122
assert output == app_image
131123

132124

133-
def test_check_app(firmware: Path, app_image: bytes, bootloader: bytes):
125+
def test_check_app(firmware: Path, app_image: bytes, bootloader: bytes) -> None:
134126
mpi_run(firmware, "--check-app")
135127
output = log_messages()
136128
for line in (
@@ -142,7 +134,7 @@ def test_check_app(firmware: Path, app_image: bytes, bootloader: bytes):
142134
assert line in output
143135

144136

145-
def test_file_integrity(firmware: Path):
137+
def test_file_integrity(firmware: Path) -> None:
146138
if options.port:
147139
pytest.skip("Skipping test_file_integrity because --port is set")
148140
sha1 = hashlib.sha256(firmware.read_bytes()).hexdigest()
@@ -154,7 +146,7 @@ def test_file_integrity(firmware: Path):
154146
assert sha1 == sha3
155147

156148

157-
def test_read_write(firmware: Path):
149+
def test_read_write(firmware: Path) -> None:
158150
if options.port:
159151
pytest.skip("Skipping test_read_write because --port is set")
160152
sha1 = hashlib.sha256(firmware.read_bytes()).hexdigest()

typings/esptool/__init__.pyi

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ from typing import Callable, Sequence
22

33
import serial
44

5-
65
class StubFlasher: ...
76

8-
97
class ESPLoader:
108
CHIP_NAME: str
119
_port: serial.Serial
@@ -20,5 +18,4 @@ class ESPLoader:
2018
def run_stub(self, stub: StubFlasher | None = None) -> "ESPLoader": ...
2119
def hard_reset(self) -> None: ...
2220

23-
2421
def main(argv: Sequence[str], esp: ESPLoader | None) -> None: ...

0 commit comments

Comments
 (0)