Development environment

The repository uses a local .venv for tests, linting, typing, documentation, and package builds. The environment is machine-specific and is ignored by Git.

Windows setup

From the repository root, create or update the environment with:

.\make.bat setup

The bootstrap script creates .venv with an available Python 3.10 or later, upgrades pip, installs .[dev] in editable mode, and checks the installed dependency set.

Select a bootstrap interpreter explicitly when automatic discovery is not appropriate during initial creation:

.\make.bat setup -Python "C:\path\to\python.exe"

An existing .venv is always reused with its current interpreter, even when -Python is supplied. To change Python, deliberately remove and recreate the environment first; make.bat clean never removes it.

The commands invoke .venv\Scripts\python.exe directly, so activation is optional. For an interactive PowerShell session:

.\.venv\Scripts\Activate.ps1

POSIX setup

The Windows helpers are convenience wrappers. On Linux and macOS, create the same environment directly:

python3 -m venv .venv
./.venv/bin/python -m pip install --upgrade pip
./.venv/bin/python -m pip install -e '.[dev]'

Project checks

The main Windows entry points are:

Command

Action

.\make.bat test

Run the pytest suite in an isolated temporary directory.

.\make.bat check

Check Ruff formatting and linting, then run mypy in strict mode.

.\make.bat docs

Build this Sphinx site with warnings treated as errors.

.\make.bat build

Build the source distribution and wheel, then validate their metadata.

.\make.bat clean

Remove generated builds and caches without deleting .venv.

Equivalent tools can be run directly through the environment interpreter:

.\.venv\Scripts\python.exe -m pytest
.\.venv\Scripts\python.exe -m ruff format --check upref tests examples scripts docs\conf.py
.\.venv\Scripts\python.exe -m ruff check upref tests examples scripts docs\conf.py
.\.venv\Scripts\python.exe -m mypy upref examples
.\.venv\Scripts\python.exe -m sphinx -E -a -W --keep-going -b html docs docs\_build\html

Pytest manages temporary directories separately for each user and test run. Avoid forcing a shared --basetemp when running tests from different Windows accounts: directories created by an isolated tool account may be inaccessible to your editor.

Continuous integration measures statement and branch coverage and requires 100 percent. Reproduce that gate locally with:

.\.venv\Scripts\python.exe -m pytest --cov=upref --cov-report=term-missing

Behavioral checks matter in addition to coverage. test_properties.py uses Hypothesis to generate nested configurations and verify YAML round trips, independence of mutable containers, merge identities, and preservation of the original file after a failed replacement. NaN is excluded from equality-based properties because it does not compare equal to itself.

test_examples.py runs storage and advanced recipes in isolated directories, supplies deterministic terminal answers, and verifies cancellation without saving. Tests for GUI arguments and lifecycle use a wxPython substitute and do not require a display. Native GUI integration tests run in a dedicated Windows CI job. Each scenario uses a separate process with a 40-second timeout and drives real modal dialogs through wx’s event loop. They check app ownership, repeated dialogs, formatted prefill, cancellation, password controls, and cleanup after an injected error. They are skipped in the ordinary suite. Run them in a desktop session with:

.\.venv\Scripts\python.exe -m pip install -e '.[test,gui]'
$env:UPREF_RUN_GUI_TESTS = '1'
.\.venv\Scripts\python.exe -m pytest tests/integration -q --no-cov
Remove-Item Env:UPREF_RUN_GUI_TESTS

The dedicated run disables coverage because the complete coverage gate belongs to the ordinary unit suite. Missing wxPython is an error when native tests are explicitly enabled. Visual layout can additionally be inspected with examples/gui_collection.py.

CI also runs the ordinary suite on Python 3.10 with platformdirs==4.0.0 and PyYAML==6.0 to check the advertised minimum runtime dependencies. The wheel job and release workflow invoke scripts/check_installed_package.py using Python’s isolated mode in a clean environment: it checks packaged resources and the save/load/update/delete cycle without importing the checkout.

VS Code tests

After creating .venv, open the command palette and run Python: Select Interpreter. On Windows, choose .venv\Scripts\python.exe; on Linux and macOS, choose .venv/bin/python. If it is missing from the list, use Enter interpreter path.... VS Code remembers this selection; changing python.defaultInterpreterPath does not replace a previously selected interpreter.

Use Test: Refresh Tests to discover tests, then run them from the Testing view. Refreshing only lists tests and does not execute them. The workspace passes --cov-fail-under=0 to pytest so discovery and individual test runs do not fail the full-suite coverage requirement. Coverage remains available, including through Run Tests with Coverage. The VS Code Test task, make.bat test, direct pytest runs, and CI still require 100 percent coverage.

If the Python output reports No module named pytest, check the selected interpreter before installing dependencies again. SKIPPED results are expected for native GUI tests unless explicitly enabled, POSIX-only checks on Windows, and symbolic-link tests when the account cannot create links.

Branch maintenance with Git Bonsai

Git Bonsai is an optional developer tool for updating and cleaning local Git branches. The Windows x64 installer pins the stable 0.3.0 release and checks the downloaded archive against a recorded SHA-256 hash:

.\make.bat bonsai-setup
git bonsai --version
git bonsai -h

Setup requires Git for Windows, PowerShell, and tar (included with current Windows versions). The first installation also needs access to GitHub. It installs the executable and upstream license under the ignored .tools directory and registers an alias in this checkout’s .git/config. It can be run again to reinstall from the cached archive. Python and Rust are not needed for this installation.

The local configuration sets git-bonsai.default-branch to master and adds master to git-bonsai.protected-branches. Other protected branches are preserved. To protect another long-lived branch, use its exact name:

git config --local --add git-bonsai.protected-branches develop

After committing or stashing your work, launch the interactive command in a terminal:

git bonsai

On Windows, .\make.bat bonsai launches the same installed executable. Version 0.3.0 fetches and prunes remote-tracking references, fast-forwards local tracking branches where possible, and prompts for local branches to delete. Review the selection before confirming. It refuses a dirty working tree, including untracked files, and does not delete remote branches. The --yes option skips confirmation; neither setup nor the launcher enables it.

Each clone needs its own setup. If using Linux or macOS, download the matching archive from the official releases and put the executable on PATH, then configure this checkout:

git config --local git-bonsai.default-branch master
git config --local --add git-bonsai.protected-branches master
git bonsai -h

Documentation workflow

The source distribution includes documentation, examples, and test support files through MANIFEST.in. Generated HTML is excluded. The packaging job checks archive contents so tests and examples remain usable outside Git.

User guides live in docs/*.rst. docs/readme_link.md includes the root README through MyST, keeping installation and quickstart content in one source. API pages use autodoc, so public docstrings must remain meaningful on their own and all public objects should be reachable from the navigation.

Before submitting documentation changes, run .\make.bat docs. A clean build must have no missing references, duplicate targets, malformed markup, or undocumented pages.

Ruff enforces Google-style docstrings on package, example, and maintenance code. The test suite also checks every Python module, class, method, and function in upref and scripts for a non-empty docstring, including private helpers.

License header maintenance

scripts/add_license_headers.py scans supported text files and reports missing MIT headers without changing files by default:

.\.venv\Scripts\python.exe scripts\add_license_headers.py --root .

Review that dry-run output before adding --write. Write mode edits files directly without locking or atomic replacement, so keep the tree in version control and avoid concurrent edits. Use --verbose to show every inspected file. By default the scanner skips .git, .venv, .eggs, __pycache__, node_modules, dist, build, .mypy_cache, .pytest_cache, .ruff_cache, and tests. --include-hidden disables that exclusion list. Symbolic-link files are never modified.