From adc3443e5b98eae09cbcb2c201dfb1ef65c24d86 Mon Sep 17 00:00:00 2001 From: Jeffrie Budde <39542105+JSBtechnologies@users.noreply.github.com> Date: Thu, 27 Aug 2026 23:26:50 -0500 Subject: [PATCH] docs: add stdlib dev.py runner + README install/dev sections - dev.py: stdlib-only local task runner (check/test/cov/guard) so the tight dev loop runs locally for free instead of via paid agents - README: Installation + Development sections documenting the local loop Closes #8. --- README.md | 23 ++++++++++++++++++++++ dev.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 dev.py diff --git a/README.md b/README.md index b2c5076..cbf9489 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,26 @@ print(resp) You can also retrieve the value directly with `fp.client.get_fingerprint()` if you prefer to handle network communication yourself. + +## Installation + +```bash +pip install fp-py # from a checkout: pip install . +``` + +`fp-py` has **zero runtime dependencies** — it imports only the Python standard +library. This is declared in `pyproject.toml` (`dependencies = []`) and enforced +in CI by `tools/dep_guard.py`. + +## Development + +Run the full dev loop locally — no external services, no paid tooling: + +```bash +python -m pip install -e ".[dev]" # one-time (adds coverage, dev-only) +python dev.py check # zero-dependency guard + unittest suite +python dev.py cov # tests + coverage report +``` + +`dev.py` is a stdlib-only runner (`python dev.py {check,test,cov,guard}`). CI runs +the same `unittest` suite across Python 3.9–3.13 plus the dependency guard. diff --git a/dev.py b/dev.py new file mode 100644 index 0000000..c92b680 --- /dev/null +++ b/dev.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Local dev runner for fp-py — stdlib only, no third-party task runner. + +Run the tight dev loop on your own machine (free) instead of via paid agents: + + python -m pip install -e ".[dev]" # one-time setup + python dev.py check # guard + tests (what CI runs) + python dev.py test # unittest suite + python dev.py cov # tests + coverage report + python dev.py guard # zero-dependency guard +""" +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent +PY = sys.executable + + +def run(*args: str) -> int: + print(f"$ {' '.join(args)}") + return subprocess.call(args, cwd=str(ROOT)) + + +def test() -> int: + return run(PY, "-m", "unittest", "discover", "-s", "tests", "-t", ".") + + +def cov() -> int: + rc = run(PY, "-m", "coverage", "run", "--source=fp", + "-m", "unittest", "discover", "-s", "tests", "-t", ".") + return rc or run(PY, "-m", "coverage", "report", "-m") + + +def guard() -> int: + return run(PY, "tools/dep_guard.py") + + +def check() -> int: + return guard() or test() + + +COMMANDS = {"test": test, "cov": cov, "guard": guard, "check": check} + + +def main(argv: list[str]) -> int: + cmd = argv[1] if len(argv) > 1 else "check" + fn = COMMANDS.get(cmd) + if fn is None: + print(f"unknown command {cmd!r}; choose from: {', '.join(COMMANDS)}") + return 2 + return fn() + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv))