Development¤
Setup and tests¤
git clone https://github.com/ts-shape/ts-shape.git
cd ts-shape
pip install -e ".[dev]" # editable install with all extras except postgres, and the dev tools
pytest
Lint and type check¤
black --check src tests examples scripts # formatting
ruff check src tests examples scripts # linting
mypy # type checking (configured in pyproject.toml)
pre-commit install # optional: black and ruff on every commit
The [dev] extra pins black, ruff and mypy; CI installs it, and the
pre-commit hook revs match the same versions. All three block CI. mypy checks src/ts_shape with
disallow_untyped_defs, disallow_incomplete_defs, warn_return_any and
warn_unused_ignores and no exempt modules, so every function needs full
annotations. pandas results are typed Any; where a function returns one
under a narrower declared type, annotate a local (out: np.ndarray = ...) or
use typing.cast with a comment saying why. Converting with float() or
int() changes the returned scalar type.
Docs¤
pip install -e ".[docs]"
mkdocs serve
DISABLE_MKDOCS_2_WARNING=true mkdocs build --strict # what CI runs
pytest tests/docs # code blocks bind against real signatures
Build¤
python -m build
twine check --strict dist/*
The version comes from the git tags (setuptools-scm): a checkout 13 commits
after 0.0.62 builds 0.0.63.dev13. Releases are published by CI; do not
upload by hand.
Releases¤
Two GitHub Actions workflows:
.github/workflows/ci.ymlruns on every pull request commit (once; there is no separate push trigger) and can be started by hand. It lints (black, ruff, mypy), builds the docs withmkdocs build --strict, runs the tests on Python 3.11 to 3.14 and once with every core dependency pinned to its declared minimum (min-deps), and builds the package (twine check --strict, no0.0.0version, the wheel imports). A non-blockingscanjob runsscripts/scan_deprecations.sh(tests with DeprecationWarning as an error, mypy[deprecated], pip-audit, outdated packages) and uploads the reports..github/workflows/release.ymlruns on every push tomain:- it runs the full CI workflow;
- it decides the version (
.github/scripts/next_version.py); - it builds and checks the sdist and wheel with that version;
- it publishes them to PyPI;
- only then it pushes the
X.Y.Ztag and creates the GitHub release; - it builds the docs and deploys them to GitHub Pages.
Every merge to main is a release, a patch release by default. To choose
another version, add a trailer to the message of any commit since the last
release (the most recent trailer wins):
Release-Bump: minor # patch | minor | major | none (no release)
Release-As: 0.1.0 # an exact version, higher than the last release
A manual run of the Release workflow (Actions, Release Pipeline, Run workflow) takes the same choice as inputs.
One-time repository settings: set the PYPI_API_TOKEN secret (or configure
PyPI Trusted Publishing for release.yml and drop the token). The workflows
declare their token permissions per job, so the repository default
(Settings, Actions, General, Workflow permissions) can stay read-only.
Dependencies¤
Dependencies are declared only in pyproject.toml: the core ones under
[project].dependencies (with minimum versions; CI tests those floors in the
min-deps job) and the optional ones as extras. There are no requirements
files; Renovate updates pyproject.toml, the pre-commit hooks and the
GitHub Actions directly.