Development

Create a local environment

Symusic development is easiest when you keep everything inside a project-local virtual environment. Clone the repository with submodules, create .venv, and install the package through pip so the compiled extension and Python package stay in sync.

git clone --recursive https://github.com/Yikai-Liao/symusic
cd symusic
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install -e ".[test]"
pip install -r docs/requirements.txt

On Windows, activate the environment with .venv\\Scripts\\activate instead.

Symusic targets C++20. GCC 11+, Clang 15+, and MSVC 2022 are the supported compiler baselines. CPython 3.12 can still emit harmless nanobind leak warnings during local work; 3.11 remains the smoothest choice if you want less noise.

Build the Python extension

The supported contributor workflow is to build the extension through pip, not by manually driving the Python bindings from raw CMake targets.

pip install .

That command rebuilds symusic.core, regenerates the installed core.pyi / py.typed artifacts, and refreshes the active environment. If you changed bindings, factories, or typing-related files, reinstall before running tests. Otherwise you will be debugging stale binaries or stale stubs.

Run the test suite

Python tests

pytest -s --cov=symusic --cov-report=xml -n auto --durations=0 -v tests

If you only need the typing regressions:

pytest tests/typecheck/test_typing_mypy.py

C++ tests

cmake -S . -B build -DBUILD_SYMUSIC_TEST:BOOL=ON -DCMAKE_BUILD_TYPE=Debug
cmake --build build --config Debug
ctest --test-dir build -C Debug --output-on-failure --verbose

Use a Visual Studio Developer shell on Windows if you want pip install . or CMake to pick up the expected MSVC toolchain automatically.

Stubs and type hints

nanobind generates the installed stubs as part of the normal build. Whenever pip install . or pip install -e . rebuilds the package, the build also refreshes symusic/core.pyi and the py.typed marker.

The typing surface intentionally mirrors the tick / quarter / second specializations exposed by the compiled extension. When you need explicit annotations, prefer the generic helpers from symusic.types and symusic.core, for example:

import symusic.core as core
import symusic.types as smt

score: smt.Score[core.Tick] = core.ScoreTick(480)
notes: core.PyVec[core.Note[core.Tick]] = core.NoteTickList()

Formatting and hooks

Install the project hooks with:

./scripts/install_git_hooks.sh

Run formatting and lint checks manually with:

pre-commit run --all-files

The pre-commit configuration lives in .pre-commit-config.yaml.