Skip to content

Development Guide

Contributing, architecture, test commands, and the release process for qrtransfer.

Table of Contents

Layout

qrtransfer/
├── pyproject.toml            # package metadata, extras, ruff/pyright/pytest config
├── mkdocs.yml                # docs site config (MkDocs + Material)
├── README.md                 # user-facing README (also the PyPI long description)
├── LICENSE
├── CHANGELOG.md
├── SECURITY.md / CONTRIBUTING.md / CODE_OF_CONDUCT.md / ACCESSIBILITY.md
├── CODEOWNERS / CONTRIBUTORS / Dockerfile / .version
├── logo/logo.svg             # project logo
├── Screenshots/              # page screenshots (for docs/screenshots.md)
├── docs/                     # MkDocs source (published to GitHub Pages)
│   ├── index.md              # home page
│   ├── getting-started.md, installation.md, usage.md, cli.md, api.md
│   ├── configuration.md, architecture.md, deployment.md
│   ├── faq.md, troubleshooting.md, development.md, screenshots.md
├── .github/workflows/        # ci.yml (tests/lint/types), container.yml, docs.yml
├── src/qrtransfer/
│   ├── __init__.py           # __version__ (single source of truth)
│   ├── __main__.py           # python -m qrtransfer
│   ├── api.py                # public library surface (start/stop + re-exports)
│   ├── cli.py                # argparse, orchestration, session lifecycle
│   ├── session.py            # Session dataclass (token, limits, expiry, ...)
│   ├── server.py             # HTTP handler + threading server, auth flow
│   ├── network.py            # interface listing, IP detection, free ports
│   ├── config.py             # persistent interface/port settings
│   ├── history.py            # past-transfers JSON store
│   ├── zipper.py             # temporary zip creation
│   ├── upload.py             # streaming multipart parser + sanitization
│   ├── web.py                # dependency-free send/receive HTML pages
│   ├── qr.py                 # QR rendering (qrcode-terminal)
│   └── tls.py                # self-signed cert generation + SSL context
└── tests/                    # pytest suite
    ├── conftest.py           # in-process HTTP server fixture
    ├── test_config.py
    ├── test_network.py
    ├── test_zipper.py
    ├── test_upload.py
    ├── test_server.py        # localhost integration tests
    ├── test_cli.py           # argparse + subprocess end-to-end tests
    └── test_qr.py

Architecture notes

  • No module globals. A single mutable Session object carries the token, password, expiry, download counter, and limits; the HTTP handler reads it from an instance attribute, not from global state.
  • Auth ordering matters. TransferHandler._authorize checks expiry → token → password → limit in that order. Checking the token before the password means a wrong token always returns 404, never 401, so an attacker can't probe for the existence of a password.
  • Receive uploads are streamed. upload.MultipartStreamReader parses multipart/form-data incrementally from the socket, so a large upload never has to be buffered in memory. The stdlib cgi module is deliberately avoided (deprecated in 3.13).
  • Cross-platform config. platformdirs resolves config/data/cache locations; on Windows these honour %APPDATA%/%LOCALAPPDATA%. Config migration from the legacy ~/.qr-filetransfer.json is handled in config.load_config.

Setting up a development environment

git clone https://github.com/rkriad585/qrtransfer.git
cd qrtransfer
python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install -e ".[dev]"

.[dev] installs the package plus pytest, ruff, pyright, and build.

Commands

Run the test suite:

pytest -v

Lint and format:

ruff check .
ruff format --check .     # check only
ruff format .              # apply formatting

Type-check (uses the .venv configured in pyproject.toml):

pyright

Build distributions:

python -m build

Build and preview the documentation site (MkDocs):

pip install -e ".[docs]"
mkdocs serve     # http://127.0.0.1:8000
mkdocs build     # writes ./site (this is what the Docs CI workflow runs)

Smoke-test the console script:

qrtransfer --help
qrtransfer --version
qrtransfer myfile.jpg      # then scan the QR code
qrtransfer receive         # then upload from a phone

Test strategy

  • Unit tests mock psutil for network code and the filesystem for config, zipper, and upload parsing — no real network access.
  • Integration tests start a real TransferServer on an ephemeral port bound to 127.0.0.1 (see tests/conftest.py) and exercise the full auth flow over http.client, including multipart receive uploads.
  • CLI tests run python -m qrtransfer as a subprocess with a redirected LOCALAPPDATA/XDG_* to keep tests hermetic, and verify the default exit-after-first-download behaviour end-to-end.
  • Test matrix in CI: Ubuntu / Windows / macOS × Python 3.9 / 3.11 / 3.13.

Continuous integration

.github/workflows/ci.yml runs on push to main and on pull requests:

  • test — the pytest matrix above.
  • lintruff check . and ruff format --check . (Ubuntu, Python 3.12).
  • typecheckpyright inside the repo venv (Ubuntu, Python 3.12).

All three jobs must be green before merging.

.github/workflows/docs.yml builds the MkDocs site and deploys it to GitHub Pages on pushes to main that touch the docs (and on workflow_dispatch). See deployment.md.

Release process

  1. Bump __version__ in src/qrtransfer/__init__.py and add a CHANGELOG.md entry.
  2. Run pytest, ruff, and pyright locally; make sure the wheel builds with python -m build.
  3. Tag the release: git tag v0.1.0 && git push origin v0.1.0. The tag push also triggers .github/workflows/container.yml, which publishes the container image to GHCR.
  4. Verify on a clean machine: python -m venv /tmp/fresh && pip install git+https://github.com/rkriad585/qrtransfer.git@main && qrtransfer --version, or run the published container image (see deployment.md). The console script, import package, and repository name are all qrtransfer.

Manual acceptance checklist

Before each release, verify on the target platform:

  • [ ] qrtransfer myfile → scan with a phone → file downloads, counter shows 1.
  • [ ] Directory + --zip, password + expiry, --once, receive mode, --tls, --port 8080.
  • [ ] Windows console: unicode filenames, Ctrl+C cleanup, no leftover temp zips.

Back to README