Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
58 changes: 58 additions & 0 deletions dev.py
Original file line number Diff line number Diff line change
@@ -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")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run the guard with a tomllib-capable interpreter

When dev.py check is run under supported Python 3.9/3.10 interpreters (requires-python = ">=3.9"), this delegates the guard to the same interpreter, but tools/dep_guard.py imports tomllib, which is only in the stdlib from Python 3.11 onward. I reproduced this with PYENV_VERSION=3.10.20 python dev.py guard, which exits with ModuleNotFoundError: No module named 'tomllib', so developers on supported Python versions cannot use the newly documented check loop.

Useful? React with 👍 / 👎.



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))
Loading