[build-system] # Hatchling 1.26 is the first version that writes the PEP 639 fields below # (`License-Expression`, `License-File`) into the metadata. An older version builds a # wheel that says nothing about the licence, and it gives no warning. requires = ["hatchling>=1.26"] build-backend = "hatchling.build" [project] # This is only the name of the distribution. The import package and the console script stay # `meshterm`. PyPI prohibits that name (refer to pypi/support#12162), but the app does not # change because of this. The `[tool.hatch.build.targets.wheel]` section below makes the # wheel contain the `meshterm/` package under this different name. name = "mesh-term" dynamic = ["version"] description = "MeshTerm is a full-featured TUI MeshCore client for your terminal. Connects to a companion over USB, Bluetooth, or TCP. For Windows, macOS, and Linux." readme = "README.md" requires-python = ">=3.10" authors = [{ name = "Jean-Pierre Martineau" }] license = "Apache-2.0" license-files = ["LICENSE", "NOTICE"] keywords = ["meshcore", "lora", "mesh", "radio", "cli"] # There is no `License ::` entry, because PEP 639 does not permit one with the SPDX expression above. classifiers = [ "Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: End Users/Desktop", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.14", "Topic :: Communications :: Ham Radio", "Topic :: System :: Networking", ] dependencies = [ "meshcore>=2.3", # companion-device protocol (serial/BLE/TCP) "pycryptodome>=3.20", # AES/HMAC for decrypting overheard channel packets "pyserial>=3.5", # serial-port enumeration for device discovery "bleak>=0.22", # Bluetooth LE scanning + companion connection "typer>=0.12", # scripted CLI. The subcommands mirror the menu "rich>=13.7", # modern console rendering (progress, tables, panels) "markdown-it-py>=3.0", # CommonMark parser behind the written About pages "prompt_toolkit>=3.0", # full-screen interactive TUI (terminal, input, resize) "segno>=1.6", # pure-Python QR codes (channel share links) "tomli>=2.0; python_version < '3.11'", # TOML config reader on 3.10 "tomli-w>=1.0", # TOML writer: preferences and device-config backups ] [project.optional-dependencies] dev = [ "pytest>=8.0", "pytest-asyncio>=0.23", # The version is pinned to one minor line. A formatter that is different on different # machines is not a gate. Ruff 0.16 formats Python inside Markdown and 0.15 does not. # With an unpinned `ruff>=0.4`, CI and a laptop disagreed about a tree with no errors. "ruff>=0.16.6,<0.17", # The node of the SPI radio. tests/test_radionode_live.py runs it with a fake radio. # The library alone is pure Python on all systems. Only the `hardware` extra needs Linux. "openhop-core>=1.1.3", # scripts/screenshots.py: it draws the screenshots of the website (Pillow, with the # glyph tables of fontTools) at a fixed time on the clock (time-machine). "pillow>=10.0", "fonttools>=4.40", "time-machine>=2.14", ] # A LoRa chip on this machine's own SPI bus (the uConsole AIO). A node that MeshTerm runs # itself controls it (refer to meshterm/core/spiradio.py). It works only on Linux, so # the other systems do not need it. spi = [ "openhop-core[hardware]>=1.1.3", ] [project.urls] Homepage = "https://github.com/jpmartineau/MeshTerm" Repository = "https://github.com/jpmartineau/MeshTerm" Issues = "https://github.com/jpmartineau/MeshTerm/issues" Changelog = "https://github.com/jpmartineau/MeshTerm/blob/main/CHANGELOG.md" [project.scripts] meshterm = "meshterm.cli:main" # This is the one place where we write the version. `[project] version` is dynamic, so the # package attribute and the distribution metadata cannot become different. [tool.hatch.version] path = "meshterm/__init__.py" [tool.hatch.build.targets.wheel] packages = ["meshterm"] # The sdist is the source of the package. It is not the source of the repository. It has # what a person needs to build and test the wheel. By default, Hatchling includes each file # that git does not ignore. This included the Claude skills, the GitHub configuration, the # PyInstaller spec, the shell scripts for the device (one of which has data under another # licence), and the template of the developer environment. None of these builds, tests, or # documents the package. The sdist keeps the package, the tests and their fixture, the # manual, and the files that a build reads. [tool.hatch.build.targets.sdist] exclude = [ "/.claude", "/.github", "/.dev.env.example", "/.git-blame-ignore-revs", "/.gitattributes", "/CLAUDE.md", "/docs/development/survey-*.md", "/packaging", "/scripts", ] [tool.ruff] line-length = 100 target-version = "py310" # The helper scripts for the Claude skills are not part of the package. Also exclude `docs/`. # It has survey documents with dates. Ruff formats Python blocks inside Markdown, and the # code in these documents is an excerpt that shows how a file looked on a past date. If # ruff formats a quotation, it changes the record. The excerpts are fragments of calls and # not complete programs. Thus the formatter would change them into something that never # existed. extend-exclude = [".claude", "docs"] [tool.ruff.lint] select = ["E", "F", "I", "UP", "B", "D"] ignore = ["D203", "D213"] # select one style for the blank line before a class and for the multi-line summary [tool.ruff.lint.pydocstyle] convention = "google" [tool.ruff.lint.flake8-bugbear] # Typer declares CLI parameters when the default value calls these functions. The objects # that they return are inert markers, and the command never changes them. B008 guards # against this kind of change. extend-immutable-calls = ["typer.Argument", "typer.Option"] [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"]