Skip to content

Commit a955707

Browse files
committed
Make littlefs an optional dependency.
This change makes the `littlefs-python` package an optional dependency for the `fs` command in the `mp-image-tool-esp32` package. This is achieved by moving the `littlefs-python` package from the `dependencies` section to the `project.optional-dependencies` section in the `pyproject.toml` file. The `littlefs` extra is now required to install the `littlefs-python` package. pyproject.toml: - move the `littlefs-python` package to the `project.optional-dependencies` section. - use dependency_groups for the test and typing environments. - Remove dependencies from the tox configuration and use the test dependency_group instead.
1 parent 478d9ec commit a955707

File tree

5 files changed

+968
-516
lines changed

5 files changed

+968
-516
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ operations on attached esp32 devices:
141141
- Using pip: `pip install mp-image-tool-esp32`, or
142142
- Using uv: `uv tool install mp-image-tool-esp32`.
143143

144+
Support for the `fs` command (reading, checking and copying files to/from
145+
littlefs filesystems on your device), requires selecting the `littlefs` extra
146+
eg: `pip install mp-image-tool-esp32[littlefs]`. This has been made available as
147+
an extra because installing the `littlefs-python` package requires extra build
148+
tools on your computer, which you may avoid if you don't need filesystem access.
149+
144150
### Install from github source
145151

146152
I recommend using [`uv`](https://docs.astral.sh/uv/) to install and manage
@@ -467,7 +473,7 @@ options:
467473
is appropriate for reading app images from flash
468474
storage.
469475
--fs CMD [CMD ...] Operate on files in the `vfs` or other filesystem
470-
partitions.
476+
partitions (requires `littlefs-python` installed).
471477
472478
Where SIZE is a decimal or hex number with an optional suffix (M=megabytes,
473479
K=kilobytes, B=blocks (0x1000=4096 bytes)). --fs commands include: ls, get,

pyproject.toml

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
[build-system]
2-
requires = ["hatchling", "hatch-vcs"]
3-
build-backend = "hatchling.build"
4-
51
[project]
62
name = "mp-image-tool-esp32"
73
description = "Tool for manipulating micropython ESP32 firmware files"
@@ -18,7 +14,6 @@ classifiers = [
1814
requires-python = ">=3.8"
1915
dependencies = [
2016
"esptool>=4.6.2",
21-
"littlefs-python>=0.12.0",
2217
"rich>=10.12.0",
2318
"more-itertools>=8.8.0",
2419
"typing-extensions>=4.12",
@@ -28,10 +23,34 @@ dynamic = ["version"]
2823
[project.scripts] # The entry points for the command line tools
2924
mp-image-tool-esp32 = "mp_image_tool_esp32.main:main"
3025

26+
[project.optional-dependencies]
27+
littlefs = ["littlefs-python>=0.12.0"]
28+
29+
[dependency-groups]
30+
typing = ["mypy>=0.910", "types-requests>=2.32.0.20240914", "types-pyserial>=3.5"]
31+
test = [
32+
{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",
35+
]
36+
dev = [
37+
{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
43+
]
44+
45+
[build-system]
46+
requires = ["hatchling", "hatch-vcs"]
47+
build-backend = "hatchling.build"
48+
49+
# Build python packages and update version number
3150
[tool.hatch]
32-
build.hooks.vcs.version-file = "src/mp_image_tool_esp32/_version.py"
3351
build.targets.wheel.packages = ["src/mp_image_tool_esp32"]
3452
version.source = "vcs" # Get the version from git, eg: 0.0.6.dev0+g1234567
53+
build.hooks.vcs.version-file = "src/mp_image_tool_esp32/_version.py"
3554
# Drop the local version part (eg: +g1234567) or pypi will reject package
3655
version.raw-options.local_scheme = "no-local-version"
3756
# A manually triggered github release workflow may generate a new tag
@@ -45,45 +64,25 @@ version.raw-options.git_describe_command = [
4564
"--match", "v*.[0-9][0-9][0-9]",
4665
]
4766

48-
[tool.uv]
49-
dev-dependencies = [
50-
# For running tests: pytest, pyyaml, requests, tox, mypy, ruff
51-
"pytest>=8.3.2",
52-
"pyyaml>=6.0.2",
53-
"requests>=2.32.3",
54-
"tox>=4.0.0",
55-
"tox-uv>=0.3.0", # uv support for tox
56-
"pytest-cov>=3.0.0",
57-
"mypy>=0.910", # For code type checks: uv run mypy --no-sources mypy src
58-
"types-requests>=2.32.0.20240914",
59-
"types-pyserial>=3.5",
60-
"ruff>=0.6.7", # For linting: uv run --no-sources ruff format --check src
61-
"keyring>=25", # For uv publish --keyring-provider=subprocess
62-
# Used by .git/hooks/post-{commit,checkout} to update _version.py:
63-
# uv run --frozen hatch build --hooks-only
64-
"hatch>=1.12.0",
65-
"hatch-vcs>=0.3.0", # For building and updating _version.py
66-
]
67-
6867
# https://tox.wiki/en/latest/config.html#pyproject-toml-native
69-
[tool.tox] #
68+
[tool.tox]
69+
requires = ["tox>=4.22", "tox-uv>=1.13"]
7070
env_list = [
71-
"clean", "mypy", "lint", "format",
71+
"clean", "typing", "lint", "format",
7272
"3.8", "3.9", "3.10", "3.11", "3.12", "3.13"
7373
]
74-
requires = ["tox>=4.19", "tox-uv>=1.13"]
7574
env.clean.commands = [["coverage", "erase"]] # Cleanup coverage data
76-
env.clean.deps = ["coverage"]
7775
env.clean.skip_install = true
78-
env.mypy.commands = [["mypy"]]
79-
env.mypy.deps = ["mypy", "pytest", "types-requests", "types-pyserial"]
76+
env.typing.commands = [["mypy"]]
8077
env.lint.commands = [["ruff", "check"]]
81-
env.lint.deps = ["ruff"]
8278
env.format.commands = [["ruff", "format", "--check"]]
83-
env.format.deps = ["ruff"]
79+
env.3.8.commands = [["pytest", "--cov=mp_image_tool_esp32"]]
80+
env.3.13.commands = [["pytest", "--cov=mp_image_tool_esp32"]]
8481
env_run_base.commands = [["pytest"]]
85-
env_run_base.deps = ["pytest", "pytest-cov", "requests", "pyyaml"]
86-
env_run_base.package = "editable"
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
8786

8887
[tool.mypy]
8988
files = ["src"]
@@ -98,12 +97,7 @@ include = ["src/**/*.py"]
9897
exclude = ["_version.py"]
9998
lint.extend-select = ["I"] # Enable ruffs isort rules (for compat with vscode ruff)
10099

101-
[tool.pytest.ini_options]
102-
minversion = "6.0"
103-
addopts = ["--cov"]
104-
105100
[tool.coverage]
106-
run.source = ["src"]
107101
run.omit = ["_version.py"]
108102
report.skip_covered = true
109103
append = true

src/mp_image_tool_esp32/logger.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from typing import Any
77

88
import esptool
9-
import littlefs
109
import serial
1110
from rich.console import Console, ConsoleRenderable
1211
from rich.highlighter import NullHighlighter
@@ -52,7 +51,7 @@ def render_message(
5251
show_level=False,
5352
show_path=False,
5453
rich_tracebacks=True,
55-
tracebacks_suppress=[esptool, serial, littlefs],
54+
tracebacks_suppress=[esptool, serial],
5655
)
5756
richhandler.setFormatter(logging.Formatter(FORMAT))
5857

src/mp_image_tool_esp32/main.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,26 @@
2626
from .argparse_typed import parser as typed_parser
2727
from .argtypes import MB, ArgList, B, IntArg, PartList
2828
from .firmware import Firmware
29-
from .lfs import lfs_cmd
3029
from .partition_table import PartitionError, PartitionTable
3130

3231
log = logger.getLogger(__name__)
3332

33+
try:
34+
from .lfs import lfs_cmd
35+
except ImportError as err:
36+
# If the `littlefs-python` package is not installed, disable the "fs"
37+
# command, but allow the rest of the program to run.
38+
if err.name != "littlefs":
39+
raise err
40+
41+
def lfs_cmd(firmware: Firmware, command: str, args: list[str] = []) -> None:
42+
"""A command line processor for LittleFS filesystem operations."""
43+
log.error(
44+
"'fs' commands require the 'littlefs-python' package. "
45+
"Install it with 'pip install littlefs-python'"
46+
)
47+
raise ImportError("Missing 'littlefs-python' package")
48+
3449

3550
# `TypedNamespace` and the `usage` string together provide three things:
3651
# 1. static type checking for the fields in the namespace returned by

0 commit comments

Comments
 (0)