Thank you for contributing to vision-synth. Bug reports, documentation improvements, focused fixes, and reviewed pull requests are welcome. Please read the Code of Conduct before participating.
Note
Project configuration is the source of truth. If this guide conflicts with pyproject.toml, .pre-commit-config.yaml, or a workflow under .github/workflows/, follow the configuration and open an issue so this guide can be corrected.
Please read the relevant documentation, search existing issues, and inspect the current implementation and tests before starting work. Open an issue before implementing a new public API, architecture change, or substantial feature so the scope can be discussed first.
Bug fixes and small documentation changes can usually proceed directly when their scope is clear. Keep every change focused and explain its motivation in the pull request.
- Report a reproducible bug or documentation error.
- Improve examples, API documentation, or benchmark explanations.
- Add or improve tests, including edge cases and regression coverage.
- Fix an approved bug or implement an approved feature.
- Review pull requests and help answer questions in issues or discussions.
Open an issue with the bug report or feature request form; each lists the required fields. In short: search existing issues first, give a minimal reproduction for bugs, and describe the user problem, proposed interface, and alternatives for features.
Wait for maintainer feedback before implementing a new public API, architecture change, or substantial feature, then keep the implementation, tests, and docs in one focused pull request.
This is a Python 3.10+ project managed with uv.
uv sync --all-extras --group devFor documentation work, also install the docs group. Build once with --strict to catch broken links and nav entries, or serve with live reload while editing:
uv sync --all-extras --group dev --group docs
uv run --group docs mkdocs build --strict
uv run --group docs mkdocs serve
# INFO - Building documentation...
# INFO - Documentation built in 0.69 seconds
# INFO - [22:44:18] Watching paths for changes: 'docs', 'mkdocs.yml', 'src/fused_transforms'
# INFO - [22:44:18] Serving on http://127.0.0.1:8000/vision-synth/mkdocs serve watches docs/, mkdocs.yml, and src/fused_transforms (docstrings feed the API reference pages) and rebuilds at http://127.0.0.1:8000 on save.
Run before opening a pull request:
pre-commit run --all-filesThe CI matrix tests Python 3.10, 3.11, 3.12, 3.13, and 3.14, the supported optional backends, and the all-extras configuration. Keep tests deterministic and include specific assertions that would fail for plausible but incorrect behavior.
Each backend leg installs exactly one optional backend, so a test that imports albumentations, kornia or torchvision without guarding it on the matching fused_transforms._compat flag passes in a development environment with every backend installed and fails only once CI runs. make test-matrix reproduces those legs locally — one cached environment per extra under .venv-ci-<extra>, running the same suite CI does — and make test-kornia runs a single leg. Guard the import and the module-level objects built from it: a module-level skipif marks the tests skipped but does not stop the module body from executing.
The ordinary test jobs use CPU Torch builds, so they do not establish CUDA numeric correctness. A separate manual/scheduled GPU workflow is defined in ci_gpu.yml, but this guide does not verify whether its self-hosted runner is currently attached or when it last completed. If you change GPU-specific code, run the gpu-marked tests locally on a CUDA-capable machine or record a successful GPU workflow run before opening a pull request.
Python examples in README.md and docs/**/*.md are generated into ignored pytest modules. Keep each example runnable from a clean generated test context:
- write the code in a plain
```pythonfence — a normal script body, not a```pyconREPL session with>>>prompts. The>>>/```pyconstyle is reserved for docstrings undersrc/**(rendered by Griffe, enforced bycheck_doctest_fences.py); Markdown examples pair apythonfence with a following output fence instead; - include imports, deterministic seeds, and all required setup in the block or an explicitly shared setup block;
- assert the user-visible contract, including output shape, dtype, and important invariants;
- use
print(...)only for meaningful information a reader should see when running the example, such as a fusion plan, capability summary, descriptor, or recorded metadata; - do not print smoke-test messages such as
print("imports ok"); - when a block depends on names from an earlier block, put
<!--phmdoctest-share-names-->immediately before the setup block; - when printing, place the exact output in an unlabelled Markdown fence inside a collapsible
<details>block immediately after the Python fence. The summary must describe what the output represents, not use a generic label such asExpected output; - make printed output deterministic by sorting sets and mappings, normalizing environment-specific values, and avoiding version/device output unless the example deliberately tests it;
- mark only genuinely non-standalone examples with
# phmdoctest:skip, and explain why in the surrounding prose. Installed optional backends are tested in the all-extras CI job and should not be skipped merely because they are optional dependencies.
Example:
print(pipe.fusion_plan)Fusion plan for the configured pipeline
fused(_DirectParamTransform, _DirectFlipTransform)
Generate and run the documentation tests with:
python .github/scripts/generate_doc_tests.py
python -m pytest tests/integration -qFor a README-only check, use the original focused command:
python -m phmdoctest README.md -s "phmdoctest:skip" --outfile tests/integration/test_readme.py
python -m pytest tests/integration/test_readme.py -qThe generator scans README.md and all Markdown files below docs/, removes stale generated modules, and fails if a non-skipped Python block contains print(...) without an exact output fence. CI regenerates the complete suite in the all-extras test job. Do not commit files under tests/integration/; the directory is intentionally ignored.
The pull request template carries the full checklist; complete it when you open a PR. Use a descriptive branch name such as fix/123-short-description, feat/123-short-description, docs/update-examples, or test/add-regression-case.
Releases are cut from a tag, never from a branch push, so publishing is always a deliberate act:
- Move the
## [Unreleased]entries inCHANGELOG.mdunder a## [X.Y.Z] - YYYY-MM-DDheading. - Set
__version__insrc/fused_transforms/__about__.pytoX.Y.Zand commit both. - Tag that commit
vX.Y.Z(annotated) and push the tag.
.github/workflows/release.yml then builds the distributions, checks the manifest, verifies that the tag and __version__ agree — a mismatch fails the run before anything is uploaded — runs twine check, and publishes through PyPI Trusted Publishing. No API token is stored: PyPI verifies the workflow's OIDC identity, which requires a one-time trusted publisher configured for this repository and this workflow file, and the pypi GitHub environment gates the upload behind whatever protection rules the repository sets.
Pre-release tags (vX.Y.Za1, vX.Y.Zrc1) are accepted by the same workflow. Note that a dev version is not: a >=X.Y,<X.Y+1 pin cannot resolve one without --pre, which is why versions below 0.12.0 — published only as dev0 snapshots — are not usable as ordinary dependencies.
Review comments should explain why an issue matters and distinguish required changes from suggestions. Reviewers should check correctness, tests, documentation, reproducibility, compatibility, performance, and security as applicable. Defer automatically fixable style issues to ruff or pre-commit.
This guide is adapted from Borda's shared contributing guide for this project's Python, CI, and executable-documentation workflow.