Guidelines for contributing
Table of Contents
Summary
PRs welcome!
- Consider starting a discussion to see if there's interest in what you want to do.
- Submit PRs from feature branches on forks to the
developbranch. - Ensure PRs pass all CI checks.
- Maintain test coverage at 100%.
Git
- Why use Git? Git enables creation of multiple versions of a code repository called branches, with the ability to track and undo changes in detail.
- Install Git by downloading from the website, or with a package manager like Homebrew.
- Configure Git to connect to GitHub with SSH.
- Fork this repo.
- Create a branch in your fork.
- Commit your changes with a properly-formatted Git commit message.
- Create a pull request (PR) to incorporate your changes into the upstream project you forked.
Hatch
This project uses Hatch for dependency management and packaging.
Highlights
- Automatic virtual environment management: Hatch automatically manages the application environment.
- Dependency resolution: Hatch will automatically resolve any dependency version conflicts using the
pipdependency resolver. - Dependency separation: Hatch supports separate lists of optional dependencies in the pyproject.toml. Production installs can skip optional dependencies for speed.
- Builds: Hatch has features for easily building the project into a Python package and publishing the package to PyPI.
Installation
Hatch can be installed with Homebrew or pipx.
Install project with all dependencies: hatch env create.
Key commands
# Basic usage: https://hatch.pypa.io/latest/cli/reference/ hatch env create # create virtual environment and install dependencies hatch env find # show path to virtual environment hatch env show # show info about available virtual environments hatch run COMMAND # run a command within the virtual environment hatch shell # activate the virtual environment, like source venv/bin/activate hatch version # list or update version of this package export HATCH_ENV_TYPE_VIRTUAL_PATH=.venv # install virtualenvs into .venv
Testing with pytest
- Tests are in the tests/ directory.
- pytest features used include:
- pytest plugins include:
- pytest configuration is in pyproject.toml.
- Run tests with pytest and coverage.py with
hatch run coverage run. - Test coverage reports are generated by coverage.py. To generate test coverage reports:
- Run tests with
hatch run coverage run - Generate a report with
coverage report. To see interactive HTML coverage reports, runcoverage htmlinstead ofcoverage report.
- Run tests with
Code quality
Running code quality checks
Code quality checks can be run using the Hatch scripts in pyproject.toml.
- Check:
hatch run check - Format:
hatch run format
Code style
- Python code is formatted with Ruff. Ruff configuration is stored in pyproject.toml.
- Other web code (JSON, Markdown, YAML) is formatted with Prettier.
Static type checking
- To learn type annotation basics, see the Python typing module docs, Python type annotations how-to, the Real Python type checking tutorial, and this gist.
- Type annotations are not used at runtime. The standard library
typingmodule includes aTYPE_CHECKINGconstant that isFalseat runtime, butTruewhen conducting static type checking prior to runtime. Type imports are included underif TYPE_CHECKING:conditions so that they are not imported at runtime. These conditions are ignored when calculating test coverage. - Type annotations can be provided inline or in separate stub files. Much of the Python standard library is annotated with stubs. For example, the Python standard library
logging.configmodule uses type stubs. The typeshed types for thelogging.configmodule are used solely for type-checking usage of thelogging.configmodule itself. They cannot be imported and used to type annotate other modules. - The standard library
typingmodule includes aNoReturntype. This would seem useful for unreachable code, including functions that do not return a value, such as test functions. Unfortunately mypy reports an error when usingNoReturn, "Implicit return in function which does not return (misc)." To avoid headaches from the opaque "misc" category of mypy errors, these functions are annotated as returningNone. - Mypy is used for type-checking. Mypy configuration is included in pyproject.toml.
- Mypy strict mode is enabled. Strict includes
--no-explicit-reexport(implicit_reexport = false), which means that objects imported into a module will not be re-exported for import into other modules. Imports can be made into explicit exports with the syntaxfrom module import x as x(i.e., changing fromimport loggingtoimport logging as logging), or by including imports in__all__. This explicit import syntax can be confusing. Another option is to apply mypy overrides to any modules that need to leverage implicit exports.
Spell check
Spell check is performed with CSpell. The CSpell command is included in the Hatch script for code quality checks (hatch run check).
GitHub Actions workflows
GitHub Actions is a continuous integration/continuous deployment (CI/CD) service that runs on GitHub repos. It replaces other services like Travis CI. Actions are grouped into workflows and stored in .github/workflows. See Getting the Gist of GitHub Actions for more info.
Maintainers
- The default branch is
develop. - PRs should be merged into
develop. Head branches are deleted automatically after PRs are merged. - The only merges to
mainshould be fast-forward merges fromdevelop. - Branch protection is enabled on
developandmain.develop:- Require signed commits
- Include administrators
- Allow force pushes
main:- Require signed commits
- Include administrators
- Do not allow force pushes
- Require status checks to pass before merging (commits must have previously been pushed to
developand passed all checks)
- To create a release:
- Bump the version number in
__version__withhatch versionand commit the changes todevelop.- Follow SemVer guidelines when choosing a version number. Note that PEP 440 Python version specifiers and SemVer version specifiers differ, particularly with regard to specifying prereleases. Use syntax compatible with both.
- The PEP 440 default (like
1.0.0a0) is different from SemVer. Hatch and PyPI will use this syntax by default. - An alternative form of the Python prerelease syntax permitted in PEP 440 (like
1.0.0-alpha.0) is compatible with SemVer, and this form should be used when tagging releases. As Hatch uses PEP 440 syntax by default, prerelease versions need to be written directly into__version__. - Examples of acceptable tag names:
1.0.0,1.0.0-alpha.0,1.0.0-beta.1
- Push to
developand verify all CI checks pass. - Fast-forward merge to
main, push, and verify all CI checks pass. - Create an annotated and signed Git tag.
- List PRs and commits in the tag message:
git log --pretty=format:"- %s (%h)" \ "$(git describe --abbrev=0 --tags)"..HEAD
- Omit the leading
v(use1.0.0instead ofv1.0.0) - Example:
git tag -a -s 1.0.0
- List PRs and commits in the tag message:
- Push the tag. GitHub Actions will build and publish the Python package.
- Bump the version number in
- Consider keeping a changelog. There are many tools and approaches for this. Most of them work like this:
- Accumulate "fragments" as the project is developed. These could be text files in a directory under version control, or could also be Git commit/PR/tag messages. Fragments should contain human-readable summaries of code changes.
- Collect the fragments and combine them into a text file like CHANGELOG.md.