Development Guide¶
Toolchain¶
This project uses modern Python tooling:
- Package Manager: uv - Fast Python package installer and resolver
- Formatter & Linter: ruff - Fast Python linter and formatter
- Task Runner: poethepoet - Task runner for Python projects
Getting Started¶
Prerequisites¶
- Python 3.12 or higher
- Git
- uv (recommended) or pip
Clone and Setup¶
# Clone the repository
git clone https://github.com/sean2077/gopro-sdk-py.git
cd gopro-sdk-py
# Install dependencies
uv sync --extra dev
# Install pre-commit hooks (optional)
pre-commit install
Tip
uv is significantly faster than pip for dependency resolution and installation.
# Clone the repository
git clone https://github.com/sean2077/gopro-sdk-py.git
cd gopro-sdk-py
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install -e ".[dev]"
# Install pre-commit hooks (optional)
pre-commit install
Development Workflow¶
Common Tasks¶
Quick Commands
- Ctrl+Shift+P in VS Code → "Run Task" → Select task
- Use
poe --helpto see all available tasks
Code Style¶
- Line length: 120 characters
- Formatter: ruff
- Linter: ruff
- Type hints: Required for public APIs
For commit message conventions, see the Contributing Guide.
Project Structure¶
gopro-sdk-py/
├── src/
│ └── gopro_sdk/ # Main package
│ ├── __init__.py # Package exports
│ ├── client.py # GoProClient
│ ├── config.py # Configuration
│ ├── exceptions.py # Custom exceptions
│ ├── state_parser.py # State parsing
│ ├── commands/ # Command implementations
│ │ ├── ble_commands.py
│ │ ├── http_commands.py
│ │ ├── media_commands.py
│ │ └── webcam_commands.py
│ └── connection/ # Connection managers
│ ├── ble_manager.py
│ ├── http_manager.py
│ └── health_check.py
├── tests/ # Test suite
│ └── test_*.py
├── examples/ # Usage examples
├── docs/ # Documentation
├── pyproject.toml # Project configuration
└── .github/workflows/ # CI/CD workflows
Testing¶
Test Configuration¶
Hardware tests require GoPro cameras. Configure using environment variables:
Running Tests¶
# Run all tests
poe test
# or: pytest
# Run with coverage
pytest --cov=src/gopro_sdk --cov-report=html
# Run specific test file
pytest tests/test_basic.py
# Run specific test
pytest tests/test_basic.py::test_client_creation
# Run with markers
pytest -m "not slow" # Skip slow tests
pytest -m "not hardware" # Skip hardware tests (no camera needed)
Writing Tests¶
import pytest
from gopro_sdk import GoProClient
def test_client_creation():
"""Test that a client can be created."""
client = GoProClient(target="1234")
assert client.target == "1234"
@pytest.mark.asyncio
async def test_async_operation():
"""Test async operations."""
client = GoProClient(target="1234")
# Test async code...
@pytest.mark.slow
def test_slow_operation():
"""Test marked as slow."""
# Long-running test...
@pytest.mark.hardware
async def test_real_camera():
"""Test requiring real hardware."""
# Test with actual camera...
Test Markers¶
@pytest.mark.slow: Long-running tests@pytest.mark.hardware: Requires real GoPro camera@pytest.mark.asyncio: Async test functions
Adding New Features¶
1. Add Command Method¶
If adding a new camera command:
# src/gopro_sdk/commands/http_commands.py
class HttpCommands:
async def new_command(self, param: str) -> dict:
"""Execute new command.
Args:
param: Command parameter
Returns:
Command result
Raises:
HttpCommandError: If command fails
"""
response = await self.http_manager.get(f"/gopro/camera/new/{param}")
return response.json()
2. Add Client Method¶
Add convenience method to GoProClient:
# src/gopro_sdk/client.py
class GoProClient:
async def new_feature(self, param: str) -> dict:
"""New feature description.
Args:
param: Parameter description
Returns:
Result description
"""
return await self.http_commands.new_command(param)
3. Export in init.py¶
If adding a new public class:
# src/gopro_sdk/__init__.py
from .new_module import NewClass
__all__ = [
# ... existing exports
"NewClass",
]
4. Add Tests¶
# tests/test_new_feature.py
import pytest
from gopro_sdk import GoProClient
@pytest.mark.asyncio
async def test_new_feature():
"""Test new feature."""
client = GoProClient(target="1234")
# Test implementation...
5. Update Documentation¶
- Add to
docs/api-reference.md - Update examples if needed
- Add to README if user-facing
Debugging¶
Enable Logging¶
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("gopro_sdk")
logger.setLevel(logging.DEBUG)
Using Debugger¶
Async Debugging¶
Performance Profiling¶
Using cProfile¶
import cProfile
import pstats
profiler = cProfile.Profile()
profiler.enable()
# Your code here
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative')
stats.print_stats(20)
Memory Profiling¶
Release Process¶
Releases are automated via semantic-release. Just follow these steps:
- Make changes following conventional commits
- Push to main branch
- Automatic release:
- Version determined from commits
- CHANGELOG generated
- Git tag created
- GitHub release published
- PyPI package uploaded (main branch only)
Manual Version Check¶
To see what version would be released:
CI/CD¶
GitHub Actions Workflows¶
CI Workflow (.github/workflows/ci.yml):
- Runs on push and PR
- Tests on multiple Python versions (3.12-3.13)
- Tests on multiple OS (Ubuntu, Windows, macOS)
- Linting and type checking
- Coverage reporting
Release Workflow (.github/workflows/release.yml):
- Runs on push to main/dev branches
- Automatic versioning
- CHANGELOG generation
- GitHub release creation
- PyPI publication (main only)
Running CI Locally¶
Documentation¶
Building Documentation¶
Documentation is in Markdown format in docs/ directory.
Adding Documentation¶
- Create new
.mdfile indocs/ - Update
README.mdto link to it - Use clear headings and examples
- Keep language professional and technical
Troubleshooting¶
Import Errors¶
Pre-commit Hooks Failing¶
Type Errors¶
Type checking is handled by Pyright (via VS Code Python extension) or ruff's type checker.
COHN Configuration Timeout¶
Symptom: Camera connects to WiFi but COHN configuration times out, stuck at COHN_STATE_Idle.
Root Cause: Camera has cached network connections from previous WiFi networks. When switching between networks, the camera may fail to obtain a new IP address due to stale network cache.
Solution - Manual Camera Reset (only method):
- Open camera menu
- Navigate to: Preferences → Connections → Reset Connections
- Confirm reset
- Retry connection
Prevention: Always reset camera network settings when: - Switching between different WiFi networks - Moving camera between test environments - COHN provisioning fails repeatedly
Note: GoPro API does not provide programmatic network reset functionality. Manual camera menu operation is required.
Getting Help¶
- Open an issue on GitHub
- Check existing issues and discussions
- Review the documentation in
docs/ - Look at
examples/directory
Contributing¶
See Contributing for detailed contribution guidelines.