Thank you for your interest in contributing to Gesture Arm. This document covers how to set up a development environment, the code standards enforced in CI, and the process for submitting changes.
- Development setup
- Code standards
- Testing
- Submitting a pull request
- Adding a new feature
- Reporting a bug
# Fork the repo on GitHub, then:
git clone https://github.com/<your-username>/gesture-arm.git
cd gesture-arm
# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # Linux / macOS
.venv\Scripts\activate # Windows
# Install in editable mode with all dev dependencies
pip install -e ".[ml,dev]"
# Verify
pytest tests/ -v # all tests should pass
ruff check gesture_arm/ # no lint errorsAll of these are enforced automatically in CI. A PR that fails any check will not be merged.
black gesture_arm/ scripts/ tests/Line length is 100 characters. Do not configure your editor to use a different line length.
ruff check gesture_arm/ scripts/ tests/Rules enforced: pyflakes (F), pycodestyle (E, W), isort (I), pyupgrade (UP), bugbear (B). The full config is in pyproject.toml.
Fix automatically:
ruff check --fix gesture_arm/mypy gesture_arm/ --ignore-missing-importsAll public function signatures must have type annotations. -> None is required on functions that return nothing. Optional[X] is required instead of X | None for Python 3.9 compatibility.
black --check gesture_arm/ scripts/ tests/ && \
ruff check gesture_arm/ scripts/ tests/ && \
mypy gesture_arm/ --ignore-missing-imports && \
pytest tests/ -vpytest tests/ -v # all tests
pytest tests/ -v -k "Config" # one class
pytest tests/ -v --cov=gesture_arm # with coverageAll tests live in tests/test_core.py. Tests must:
- Run without hardware, network access, or TensorFlow (mark TF tests with
pytest.skipif TF unavailable) - Follow the
TestClassName.test_method_namenaming pattern - Cover the normal path, boundary conditions, and at least one error path for any new function
Do not:
- Open a real camera (
cv2.VideoCapture) in a test - Connect to a real serial port
- Make network requests
Use fixtures for shared setup:
@pytest.fixture
def mapper(self):
from gesture_arm.models.stabilizer import BaselineMapper
return BaselineMapper({"x":(60,180),"y":(40,140),"z":(100,150)})Aim for >80% line coverage on any new module. Check with:
pytest tests/ --cov=gesture_arm --cov-report=term-missing-
Create a branch from
main:git checkout -b feature/transformer-stabilizer
-
Make your changes. Keep commits small and focused. One logical change per commit.
-
Write or update tests for your change.
-
Run the full check suite (see Section 2) and confirm everything passes locally before pushing.
-
Push and open a PR against
main. Fill in the PR template:- What does this change do?
- Why is it needed?
- How was it tested?
- Does it change any public API? (update
docs/API_REFERENCE.mdif so)
-
CI runs automatically. Fix any failures — do not ask reviewers to ignore CI.
-
One approval from a maintainer is required to merge.
Keep PRs small. A PR that changes 5 files is reviewed in 20 minutes. A PR that changes 30 files takes days or gets rubber-stamped. If your feature is large, split it into multiple PRs: interface first, then implementation, then tests, then documentation.
-
Add a new class in
gesture_arm/models/that implements this interface:class MyStabilizer: def update(self, feature_vector: np.ndarray) -> Tuple[Optional[np.ndarray], str]: ... def reset(self) -> None: ...
-
Add a config section in
config/default.yamlif the model has tunable parameters. -
Add a
--modelCLI flag togesture_arm/run.pyto select between stabilizers. -
Add benchmark comparison to
notebooks/benchmark_analysis.ipynb. -
Add unit tests in
tests/test_core.py.
-
Create
gesture_arm/hardware/<backend>.pywithArmControllerandBaseControllerclasses that match the existing interface. -
Add a
--hardwareCLI flag or config option torun.py. -
No hardware-specific code should appear outside the
hardware/module.
-
Add the command string and motor args to
speech.commandsinconfig/default.yaml. -
No code changes required — the ASR listener reads the command vocabulary from config at startup.
Open a GitHub issue with:
- Description — what happened vs what you expected
- Reproduction steps — exact commands run
- Environment — OS, Python version,
pip freezeoutput - Logs — full terminal output including any tracebacks
- Hardware — Arduino model, camera model (if hardware-related)
For crashes, run with PYTHONFAULTHANDLER=1 to get a full traceback:
PYTHONFAULTHANDLER=1 python -m gesture_arm.run