Contributing¶
We welcome contributions to busylight-core! This guide will help you get started with development, testing, and submitting changes.
We have issues labeled as Good First Issue and Help Wanted which are good opportunities for new contributors.
Development Setup¶
Prerequisites¶
- Python 3.11+
- uv for dependency management
- Git for version control
- Optional: direnv for automatic environment activation
Initial Setup¶
-
Clone the repository:
-
Install dependencies and create virtual environment:
-
Optional: Enable direnv for automatic environment activation:
-
Verify installation:
Development Workflow¶
Available Commands¶
The project uses Poe the Poet for task
automation. Run poe without arguments to see all available tasks:
Code Quality:
- poe test - Run test suite with pytest
- poe ruff-check - Run ruff linting checks
- poe ruff-format - Format code with ruff
- poe ruff - Run both check and format
- poe coverage - Generate and open HTML coverage report
Development:
- poe docs-serve - Serve documentation locally for development
- poe docs-build - Build documentation
- poe clean - Remove build artifacts and cache files
Making Changes¶
-
Create a feature branch:
-
Make your changes following the project conventions
-
Run quality checks:
-
Commit your changes using conventional commit format (see below)
-
Push and create a pull request
Testing¶
Running Tests¶
The project uses pytest with coverage reporting:
# Run unit tests
poe test
# Run doc tests (validates all Python code blocks in docs/)
poe doc-test
# Run tests with coverage report
poe coverage
# Run specific test file
uv run pytest tests/test_specific.py
# Run tests with verbose output
uv run pytest -v
Test Structure¶
tests/- Main test directorytests/vendor_examples/- Vendor-specific test examples- Tests follow the naming convention
test_*.py - Mock hardware devices are used for testing (no real hardware required)
Doc Tests¶
All Python code blocks in docs/ are automatically tested using
pytest-markdown-docs. This catches hallucinated
or outdated API examples before they ship.
- Every fenced Python code block is executed during CI
- USB hardware is mocked via
docs/conftest.py(no devices needed) - If a block depends on imports from the previous block, use:
```python continuation - To exclude a block from testing:
```python notest - CI runs doc tests on every PR, push to main, and release
Writing Tests¶
When adding new features:
- Create tests first (TDD approach encouraged)
- Aim for >90% test coverage for production code
- Test both success and error cases
- Use descriptive test names that explain the behavior being tested
- Mock hardware dependencies - tests should not require real devices
- Update doc examples - if you change an API, update the docs and the doc tests will catch any mismatches
Example test structure:
def test_device_on_method_sets_color(self, mock_device) -> None:
"""Test that on() method properly sets device color."""
mock_device.on((255, 0, 0))
assert mock_device.color == (255, 0, 0)
Architecture Overview¶
Core Components¶
busylight-core provides a unified interface for controlling USB-connected status lights through a plugin-style architecture:
- Light (src/busylight_core/light.py) - Abstract base class for all devices
- Hardware (src/busylight_core/hardware.py) - Hardware detection and connection
- Vendor implementations (src/busylight_core/vendors/) - Device-specific classes
- Mixins (src/busylight_core/mixins/) - Shared functionality (ColorableMixin, TaskableMixin)
Adding Device Support¶
To add support for a new device:
- Create or use existing vendor package in
src/busylight_core/vendors/ - Implement Light subclass with required abstract methods:
__bytes__()- Device protocol serializationon()- Turn on with colorcolorproperty - Get/set current color- Define supported_device_ids with vendor/product ID mappings
- Import in vendor's init.py and add to
__all__ - Import in main init.py and add to main
__all__ - Add comprehensive tests following existing patterns
Key Design Principles¶
- Device-specific classes enable type safety and plugin discovery
- Minimal code duplication (~5-10% is acceptable for hardware abstraction)
- Clear inheritance hierarchies with vendor base classes
- Comprehensive test coverage with mocked hardware
Commit Message Format¶
This project uses conventional commits for automatic changelog generation. Use these prefixes in your commit messages:
- feat: New features and capabilities
- fix: Bug fixes and corrections
- docs: Documentation updates
- refactor: Code restructuring without behavior changes
- perf: Performance improvements
- test: Test additions or modifications
Examples:
feat: add support for NewVendor SuperLight device
fix: resolve color parsing issue in Blynclight
docs: update contributing guidelines for new developers
refactor: simplify vendor base class hierarchy
perf: optimize Word.__str__ BitField introspection
test: add comprehensive tests for multi-LED devices
Commit format:
Code Style¶
Formatting and Linting¶
The project uses ruff for code formatting and linting:
- Line length: 80 characters for markdown, flexible for code
- Docstrings: Sphinx reStructuredText format with single-line summary
- Type hints: Required for all public APIs
- Import sorting: Automatic via ruff
Code Conventions¶
- Descriptive variable names that explain purpose, not implementation
- No comments - prefer clear code and comprehensive docstrings
- Exception handling: Use descriptive variable names (
errornote) - Clean exceptions: Use
contextlib.suppressinstead oftry/except/pass
Docstring Format¶
Use Sphinx reStructuredText format focusing on programmer intent rather than type information (which is covered by type hints):
def on(self, color: tuple[int, int, int], led: int = 0) -> None:
"""Turn on the light with specified RGB color.
Sets the device to display the given color immediately. For devices
with multiple LEDs, use led parameter to target specific LEDs or
0 for all LEDs.
:param color: RGB values from 0-255 for desired color intensity
:param led: Target LED index, 0 affects all LEDs
:raises LightUnavailableError: If device communication fails
"""
def get_colors(self) -> list[tuple[int, int, int]]:
"""Get current colors of all LEDs.
Returns the current color state for each LED in device order.
Use this to save current state before making temporary changes.
:return: List of RGB tuples, one per LED in device order
"""
Key principles:
- Single-line summary describing the action or purpose
- Document programmer intent - how and why other code should use this
- Expected inputs - value ranges, expected content, usage patterns
- Actions taken - what happens when called, side effects
- Exception conditions - when might this fail and why
- Return value usage - how should returned values be used
- Avoid type redundancy - type hints handle type information
- Compact format - use :param name: description rather than verbose sections
Documentation¶
Building Documentation¶
The project uses MkDocs with the Material theme:
# Serve locally for development
poe docs-serve
# Build for production
poe docs-build
# Deploy to GitHub Pages (maintainers only)
poe docs-deploy
Writing Documentation¶
- API documentation is auto-generated from docstrings
- Guides and examples go in the
docs/directory - Follow 80-column line width for markdown files
- Use descriptive headers and clear examples
- All Python code blocks are tested - doc examples must use real
method names and signatures. Mock infrastructure in
docs/conftest.pyhandles USB hardware. Run doc tests locally before submitting.
Releases¶
Python Version Configuration¶
The CI/CD test matrix uses Python versions configured in pyproject.toml:
Benefits: - Single source of truth for CI test versions - Automatic workflow updates when versions change - Graceful fallback if configuration missing
Note: Changing these versions affects all CI/CD testing across the project.
For Maintainers¶
Releases are handled through Poe tasks and GitHub Actions:
-
Create release:
-
Automated CI/CD pipeline:
- Version bump in pyproject.toml
- Git commit, tag, and push
- GitHub Actions:
get-python-versions→test→build→ [publish,github-release] →docs - Parallel execution: PyPI publishing and GitHub release creation run simultaneously
- Documentation deployment: Only triggered after successful releases
Changelog¶
The CHANGELOG.md is automatically updated using conventional commits:
- Features from feat: commits
- Bug Fixes from fix: and bug: commits
- Documentation from docs: commits
- Refactoring from refactor: commits
- Performance from perf: commits
Getting Help¶
- GitHub Discussions: Ask questions and share ideas
- Issues: Report bugs or request features
- Code Review: All PRs receive thorough review and feedback
Code of Conduct¶
We are committed to providing a welcoming and inclusive environment for all contributors. Please be respectful and constructive in all interactions.
Questions?¶
Don't hesitate to ask questions! Create an issue or discussion, and we'll help you get started. New contributors are always welcome, and we're happy to provide guidance on getting familiar with the codebase.