How to Contribute to Yohou¶
Thank you for your interest in contributing to Yohou! This document provides guidelines for contributing to the project.
Code of Conduct¶
We are committed to providing a welcoming and inclusive environment for all contributors. Please be respectful and considerate in all interactions.
Getting Started¶
Prerequisites¶
Development Setup¶
-
Fork the repository on GitHub
-
Clone your fork:
- Install dependencies:
- Install pre-commit hooks:
Development Workflow¶
Making Changes¶
- Create a new branch:
-
Make your changes
-
Run tests:
- Format and fix code:
- Commit your changes:
We follow Conventional Commits for commit messages. The commit message format is enforced by commitizen pre-commit hooks, which will validate your commit messages automatically. See Commit Message Convention below for the full list of types and examples.
Running Tests¶
Yohou uses pytest with markers to categorize tests.
Test Markers¶
| Marker | Description | When to Use |
|---|---|---|
| (none) | Fast unit tests without subprocess calls or heavy I/O | Default for most tests |
@pytest.mark.slow |
Tests that take more than a few seconds, perform heavy computations, make network requests, or access external resources | Long running computations, network access |
@pytest.mark.integration |
Tests that run subprocesses, test multiple components together, require complex setup/teardown, or exercise end-to-end workflows | Cross-component validation |
@pytest.mark.example |
Validates example notebooks execute without errors (used in tests/test_examples.py) |
Notebook smoke tests |
Markers can be combined. A test marked with both @pytest.mark.slow and @pytest.mark.integration is excluded from fast test runs but included in both test-slow and test-full sessions.
Marking Tests¶
import pytest
@pytest.mark.slow
def test_large_computation():
# Long-running test
pass
@pytest.mark.integration
@pytest.mark.slow
def test_end_to_end_workflow():
# Complex integration test
pass
Test Organization¶
- Class-based structure: Group related tests into classes using the
Test<Component><Scenario>naming pattern. - Fixture usage: Prefer fixtures from
conftest.pyover module-level data. Seetests/conftest.pyfor available factories. - Property-based testing: Hypothesis is available for property-based testing of edge cases and invariants.
Running Test Suites¶
Run fast tests during development:
Run all tests:
Code Quality¶
Run linters and type checkers:
Format code and fix issues:
Run all quality checks by combining the fix and test steps above:
Docstring Standards¶
All public functions, methods, and classes require NumPy-style docstrings. Coverage is enforced at 100% by interrogate.
Check docstring coverage:
Required sections (as applicable):
Parameters: all function/method parameters with types and descriptionsReturns: return value type and descriptionRaises: exceptions raisedSee Also: related classes/functionsReferences: academic references for algorithms or methods usedNotes: implementation details, mathematical backgroundExamples: usage examples (tested viapytest --doctest-modules)
See Also format:
Use standard numpydoc format with short backtick names. The mkdocs-autorefs plugin automatically links backtick references (e.g., `ClassName`) to the corresponding API pages in rendered documentation. This means plain backtick-wrapped names in docstrings become clickable links in the docs site without any special syntax.
For hyperlinks, always use Markdown syntax: [text](url).
Documentation¶
Build documentation:
Serve documentation locally:
View all available commands:
Glossary System¶
Yohou has a two-layer glossary system that automatically annotates domain terms across the documentation site:
- Hover tooltips: The
pymdownx.snippetsextension auto-appendsdocs/includes/glossary.md(abbreviation definitions in*[term]: definitionformat) to every page. Combined with theabbrMarkdown extension and Material'scontent.tooltipsfeature, every occurrence of a glossary term shows a tooltip on hover. - Navigation links:
docs/hooks.pywraps the first occurrence of each glossary term on a page with a link to the Glossary definition. The_GLOSSARY_TERMSdictionary maps display text to anchor slugs.
Both layers work automatically. When writing documentation, use glossary terms as plain text. Never write manual links like [forecasting horizon](glossary.md#forecasting-horizon).
To add a new glossary term, update three files:
docs/pages/explanation/glossary.md: Add the definition with an anchor attribute ({ #my-term }).docs/includes/glossary.md: Add an abbreviation entry (*[my term]: Short definition.).docs/hooks.py: Add an entry to the_GLOSSARY_TERMSdictionary ("my term": "my-term").
Adding Examples¶
All examples are interactive marimo notebooks that combine code, markdown, and visualizations.
Creating a Notebook¶
Create a new marimo notebook in examples/<name>.py:
Required Structure¶
Notebooks serve tutorials or how-to guides only, never explanation or reference. The structure depends on the category:
Tutorial notebooks (category: tutorial):
- Title:
# In this notebook, we will [goal] - Prerequisites: One-liner stating required prior knowledge
- Numbered sections:
## 1. Section Name,## 2. Section Name, etc. with visible output every cell - What You Built: Closing section summarizing what was accomplished and linking to next steps
How-to notebooks (category: how-to):
- Title:
# How to [Verb] [Object] - Prerequisites: One-liner stating required prior knowledge
- Numbered sections:
## 1. Section Name,## 2. Section Name, etc. with action-only prose - No closing summary; the notebook ends after the last step
Example intro cell (tutorial):
# Your First Hyperparameter Search
In this notebook, we will run a hyperparameter search using OptunaSearchCV
and inspect the results.
**Prerequisites:** Python 3.11+ and familiarity with sklearn's fit/predict API.
Example intro cell (how-to):
# How to Stop Optimization Early with Callbacks
This notebook shows how to attach Optuna callbacks to OptunaSearchCV
to stop a search after a fixed number of trials.
**Prerequisites:** Familiarity with the
OptunaSearchCV quickstart
([View](../../../examples/quickstart/) ยท [Open in marimo](https://marimo.app/github.com/stateful-y/yohou/blob/52fb806975efd9be7f932fdc868147d6e543c43f/examples/quickstart.py)).
Marimo Cell Conventions¶
- Use
hide_code=Trueon all markdown cells, import cells, and utility/helper cells - Use
r"""..."""(raw triple-quoted strings) for markdown cell content -
All notebooks declare dependencies using PEP 723 inline script metadata at the top of the file:
-
Dependencies are sorted alphabetically and only list third-party packages actually imported by the notebook.
marimoitself is NOT listed as a dependency (it is the runner, not a dependency of the script).- To add a dependency:
uv add --script notebook.py <package>or edit the header manually. - To run in an isolated sandbox:
uv run marimo edit --sandbox notebook.py. - Group all imports into a single hidden cell after the metadata header
Content Guidelines¶
- Gallery metadata: Every example notebook should include a
__gallery__variable definingtitle,description, andcategory("tutorial"or"how-to") for the example gallery. Add acompanionkey pointing to the matching doc page path when one exists. - Markdown density: Each numbered section should open with a short markdown cell (one to two sentences) before any code cells. Tutorial sections may be slightly longer; how-to sections should be action-only.
- No emojis: Do not use emojis anywhere in notebooks whether it is in headings, content bullets, or concluding remarks.
- API cross-links: When mentioning yohou classes or functions in markdown cells, wrap them in backtick-link syntax pointing to the API page.
- Voice: Tutorials use "we" (first-person plural). How-to guides use imperative or conditional imperatives ("If you need X, pass Y").
Testing and Documentation¶
Run the example test suite to verify your notebook passes:
Add a link to your example in docs/pages/examples/index.md:
The mkdocs hooks automatically export notebooks to HTML during docs build. All notebooks in examples/ are automatically discovered and tested by test_examples.py using pytest's parametrization feature, which runs them in parallel for fast validation.
Submitting Changes¶
- Push your changes to your fork:
-
Open a Pull Request on GitHub
-
Ensure all CI checks pass
-
Wait for review and address any feedback
Pull Request Guidelines¶
- Write clear, descriptive PR titles following Conventional Commits
- Include a description of the changes
- Add tests for new functionality
- Update documentation as needed
- Ensure all tests pass
- Keep PRs focused and atomic
Commit Message Convention¶
We use Conventional Commits enforced by commitizen.
Commit Types¶
| Type | Description | Version Bump | Example |
|---|---|---|---|
feat |
New features | Minor | feat: add new forecaster |
fix |
Bug fixes | Patch | fix: resolve scoring edge case |
docs |
Documentation changes | None | docs: update installation guide |
style |
Code style changes (formatting) | None | style: format with ruff |
refactor |
Code refactoring | None | refactor: simplify validation logic |
test |
Adding or updating tests | None | test: add scorer edge case tests |
chore |
Maintenance tasks | None | chore: update dependencies |
perf |
Performance improvements | None | perf: vectorize weight computation |
ci |
CI/CD changes | None | ci: add Python 3.14 to matrix |
Scopes¶
Scopes are optional and appear in parentheses after the type:
Breaking Changes¶
Breaking changes trigger a major version bump. Signal them with ! after the type or with a BREAKING CHANGE: footer:
feat: redesign authentication system
BREAKING CHANGE: authentication now requires API keys instead of passwords
The pre-commit hook will validate your commit messages and prevent commits that don't follow the convention.
Release Process¶
Releases are fully automated through GitHub Actions when a new tag is pushed, with a manual approval gate before publishing to PyPI to ensure quality control.
graph LR
A[Push Tag<br/>v*.*.*] --> B[changelog.yml]
B --> C[Generate<br/>CHANGELOG.md]
B --> D[Build Package<br/>validation]
C --> E[Create PR]
E --> F[Review & Merge<br/>PR]
F --> G[publish-release.yml]
G --> H[Create GitHub<br/>Release]
H --> I{Manual<br/>Approval}
I -->|Approve| J[Publish to PyPI]
style I fill:#f59e0b,stroke:#333,stroke-width:2px,color:#fff
style J fill:#10b981,stroke:#333,stroke-width:2px,color:#fff
How It Works¶
-
Tag a release:
bash git tag v0.2.0 -m "Release v0.2.0" git push origin v0.2.0 -
Automated changelog workflow (
changelog.yml):- Generates changelog from conventional commits using git-cliff
- Creates a Pull Request with the updated CHANGELOG.md
- Builds the package distributions (wheels and sdist) for immediate validation
- Stores distributions as workflow artifacts (reused later to avoid rebuilding)
-
Review and merge the changelog PR:
- A maintainer reviews the generated changelog
- Once approved, merge the PR to main
-
Automated release workflow (
publish-release.yml):- Creates a GitHub Release with generated release notes
- Attaches distribution files to the release
- Waits for manual approval before proceeding to PyPI
-
Manual approval for PyPI publishing:
- Designated reviewers receive a notification
- Review the GitHub Release to verify everything is correct
- Approve the deployment to publish to PyPI
- Package is published using Trusted Publishing (OIDC, no tokens needed)
-
Release notes generation:
- All commits since the last tag are analyzed
- Commits are grouped by type (Added, Fixed, Documentation, etc.)
- Only commits following conventional format are included
- Breaking changes are highlighted
Version Numbering¶
Yohou follows Semantic Versioning:
| Component | Example | Trigger |
|---|---|---|
| Major | 1.0.0 | Breaking changes |
| Minor | 0.1.0 | New features (backward compatible) |
| Patch | 0.0.1 | Bug fixes (backward compatible) |
CI Test Strategy¶
The CI pipeline uses a two-tier strategy to balance speed with thoroughness:
-
Fast tests (
test-fastjob): Runs on minimum and maximum Python versions (3.11, 3.14).- Draft PRs: Ubuntu only (quick feedback in ~2 minutes)
- Ready PRs and main: All OS (Ubuntu, Windows, macOS)
-
Full test suite (
test-fulljob): Runs all tests (fast + slow + integration) on Ubuntu across all Python versions (3.11 through 3.14) when the PR is not in draft mode or on the main branch. Includes coverage reporting on the minimum supported Python version.
This separation means contributors get rapid feedback during development while still verifying comprehensive correctness before merging. See Running Tests for the marker definitions and commands.
Questions?¶
If you have any questions, feel free to:
Thank you for contributing!