Contributor Workflow
This page provides practical contribution guidance: environment setup, the test system, and three common implementation scenarios—adding a CLI command, adding a configuration-step schema, and using the Web GUI bridge. See Contributing for general pull-request and commit conventions.
Environment setup
conda activate habit # Python 3.10
pip install -e . # editable install
pytest tests/ # Run the tests
Code conventions
Convention |
Description |
|---|---|
Type annotations |
Annotate function inputs and outputs explicitly, for example
|
Comment language |
Write detailed code comments in English. |
Plot text |
All text generated inside programmatic plots must be English. |
Utility organization |
Put shared utilities in |
Code style |
Follow PEP 8 and add docstrings to public functions. |
Testing
Tests are located in the repository-level tests/ directory and configured
by tests/pytest.ini. There are two main categories:
pytest unit and CLI tests (
test_*.py), usingclick.testing.CliRunneror direct API calls.Executable demo scripts, such as
tests/habitat/habitat_two_step_voxel_radiomics_train.py. These use demo YAML files underconfig/and provide end-to-end smoke coverage.
Select tests by marker (markers are defined in pytest.ini):
pytest tests/ -m unit # Unit tests only
pytest tests/ -m "habitat and not slow"
pytest tests/ -m cli # CLI tests only
pytest tests/test_architecture_contracts.py -m unit # Architecture contracts
Available markers: slow / integration / unit / preprocessing /
habitat / ml / utils / cli.
Architecture contract tests
tests/test_architecture_contracts.py protects two cross-subsystem
contracts. It must pass after adding a factory or orchestrator:
flowchart TD
TST["test_architecture_contracts.py"]
TST --> R1["test_registry_subclasses_class_registry"]
TST --> R2["test_registry_exposes_uniform_contract"]
TST --> R3["test_registries_do_not_share_storage"]
TST --> O1["test_orchestrator_exposes_terminal_methods"]
R1 & R2 & R3 --> CR["ClassRegistry + 6 factories"]
O1 --> OC["ORCHESTRATOR_CONTRACT + 7 orchestrators"]
For a new class-based factory, subclass ClassRegistry and add the
factory to the CLASS_REGISTRIES dictionary in the test file.
For a new orchestrator, append an
(import_path, class_name, terminal_methods) tuple to
ORCHESTRATOR_CONTRACT.
Tests for missing optional dependencies are automatically skipped, so the
minimal installation remains usable.
Demo data flow
tests/conftest.py provides fixtures such as project_root and
demo_data_dir; example data is stored in demo_data/.
End-to-end examples are in tests/integration/ (for example,
workflow_preprocess_to_compare.py runs preprocess → get-habitat → extract
→ model → compare). Configuration paths are resolved relative to the YAML
file; see PathResolver in Configuration System.
Scenario 1: Add a CLI command
Keep the command declaration separate from its implementation:
habit/cli.py declares options and forwards the call, while the
implementation lives in habit/commands/cmd_*.py.
1. Declare the command in ``habit/cli.py`` (use lazy imports to keep startup fast):
@cli.command('my-task')
@config_option()
def my_task(config):
"""One-line help shown in `habit --help`."""
from habit.commands.cmd_my_task import run_my_task
run_my_task(config)
2. Implement it in ``habit/commands/cmd_my_task.py`` (follow the
structure of cmd_preprocess.py):
from habit.commands.common import (
echo_success, exit_with_error, load_config_or_exit,
)
from habit.core.my_subsystem.config_schemas import MyConfig
from habit.core.my_subsystem.run import run_my_task_from_config
def run_my_task(config_path: str) -> None:
"""Run my task from a config file."""
config = load_config_or_exit(MyConfig, config_path)
try:
run_my_task_from_config(config)
except Exception as exc: # noqa: BLE001
exit_with_error(f"Error: {exc}")
echo_success("My task completed successfully!")
3. Put the core logic in a run_*_from_config function under
habit/core/.../run.py. Keep the command layer thin and the core layer
focused; Python API users can then call the core function directly.
4. Add a CLI test in tests/.../test_cli_my_task.py using CliRunner.
Scenario 2: Add a configuration-step schema
If a new algorithm has configurable params, define and register a
parameter schema to obtain type validation and GUI form generation.
1. Define the parameter model under habit/core/schemas/steps/:
from pydantic import BaseModel, Field
class MyStepParams(BaseModel):
# Each field: type + default + constraint gives validation + GUI widget.
n_clusters: int = Field(3, ge=2, description="Number of clusters")
metric: str = Field("euclidean", description="Distance metric")
2. Register it with ``ParamSchemaRegistry`` (see the initialization logic
in schemas/registry.py):
ParamSchemaRegistry.register("habitat", "my_step", MyStepParams)
3. Result: validate_step_params() validates the step params while
loading configuration, and the GUI reflects the corresponding form fields from
schemas/reflect.py. See Configuration System for details.
Tip
BaseConfig uses extra='forbid'. Declare every new field in the
schema; otherwise the configuration will report an unknown field.
Scenario 3: Web GUI and bridge
habit-gui/ is an optional, separate Web GUI (not checked out by default;
see Code Map). It reuses the core through two channels and does
not duplicate business logic:
flowchart LR
WEB["React UI (habit-gui/web)"] -->|HTTP| API["FastAPI (habit-gui/api)"]
API -->|subprocess| CLI["habit CLI (habit <cmd> --config)"]
API -->|bridge worker| BR["habit-gui/bridge"]
BR -->|import| SCH["habit.core.schemas (reflect)"]
Execution channel: The GUI generates YAML configuration and invokes the
habitCLI in a subprocess, producing the same behavior as the CLI.Schema channel:
habit-gui/bridgereflects parameter models fromhabit.core.schemasand sends field descriptors to the frontend for dynamic form rendering. Form fields therefore remain aligned with the CLI YAML schema.
This design means that adding a core component and parameter schema usually adds the corresponding GUI inputs without frontend changes.
See also
See Architecture for the architecture overview and Configuration System for configuration details.
See Extension and Plugin Guide for extension points and Customization and Extension Guide for code templates.