Developer Guide
AI Note: Keep this file under 500 lines for AI assistant readability
This guide covers local development workflows, tools, and conventions for contributing to the LouieAI Python client library.
Quick Start (30 seconds)
# Clone and setup
git clone <repo-url>
cd louie-py
uv venv --python 3.12 .venv
source .venv/bin/activate
uv pip install -e ".[dev]"
pre-commit install
# Verify setup (use smart scripts)
./scripts/ci-quick.sh
Local Development Environment
Prerequisites
- Python 3.10+ (3.12+ recommended)
- uv package manager (faster than pip)
- Git for version control
- Graphistry account for testing against real API
Environment Setup
# Create virtual environment with specific Python version
uv venv --python 3.12 .venv
source .venv/bin/activate
# Install in development mode with all dev dependencies
uv pip install -e ".[dev]"
# Set up pre-commit hooks (runs on every commit)
pre-commit install
Project Structure
src/louieai/ # Main package code
tests/ # Test suite
docs/ # Documentation source
scripts/ # Development scripts (CI simulation)
.github/workflows/ # CI/CD configuration
pyproject.toml # Project configuration
Tool Usage
uv (Package Manager)
# Install dependencies
uv pip install -e ".[dev]" # Dev install with all tools
uv pip install -e ".[docs]" # Just docs dependencies
# Run commands (project uses ./bin/uv wrapper)
./bin/uv run pytest # Auto-manages environment
./bin/uv run ruff check . # No activation needed
./bin/uv run mypy . # Handles dependencies automatically
# Environment management
uv venv --python 3.12 .venv # Create virtual environment
uv pip compile --upgrade # Update lockfile
uv venv --python 3.12 .venv-clean # Fresh environment
ruff (Linter + Formatter)
# Check code (linting)
ruff check . # Check all files
ruff check src/ # Check specific directory
ruff check --fix . # Auto-fix issues
# Format code
ruff format . # Format all files
ruff format --check . # Check if formatting needed
ruff format --diff . # Show formatting changes
mypy (Type Checker)
# Type check
mypy . # Check all files
mypy src/louieai/ # Check specific package
mypy --no-error-summary . # Less verbose output
# Common issues:
# - Missing imports: Add to pyproject.toml [[tool.mypy.overrides]]
# - Test files: Use ignore_errors = true for complex mocking
pytest (Test Runner)
# Run tests (always use ./bin/uv for correct environment)
./bin/uv run pytest # All tests
./bin/uv run pytest -v # Verbose output
./bin/uv run pytest -x # Stop on first failure
./bin/uv run pytest -q # Quiet output
# Or use the smart script (recommended)
./scripts/pytest.sh # Includes coverage + threshold
./scripts/pytest.sh -v # Your args + smart defaults
# Parallel testing (faster)
./bin/uv run pytest -n auto # Use all CPU cores
./bin/uv run pytest -n 4 # Use 4 processes
# Specific tests
./bin/uv run pytest tests/unit/test_client.py # Single file
./bin/uv run pytest -k "test_error" # Tests matching pattern
Important Python Environment Note:
- Always use ./bin/uv run or our smart scripts to ensure correct Python version
- The project requires Python 3.10+ (we use 3.12 in development)
- A .python-version file pins the version for consistency
- If you see Python 3.8 errors, you're likely using global Python instead of venv
Local CI Simulation
Smart Development Scripts
We provide intelligent wrapper scripts that mirror CI exactly with sensible defaults:
# Individual tool scripts (smart defaults)
./scripts/ruff.sh # Default: check all files
./scripts/format.sh # Default: format all files
./scripts/mypy.sh # Default: check all files
./scripts/pytest.sh # Default: coverage + 85% threshold
# CI orchestration scripts
./scripts/ci-quick.sh # Fast feedback (errors only + tests)
./scripts/ci-local.sh # Full CI pipeline locally
Smart Defaults vs Custom Arguments
No arguments = Smart defaults:
./scripts/pytest.sh # Runs with coverage + threshold
./scripts/ruff.sh # Checks all files
With arguments = Full flexibility:
./scripts/pytest.sh -v -k specific # Adds coverage to your args
./scripts/ruff.sh format --check # Pass-through to ruff format --check
./scripts/pytest.sh --no-cov # User overrides, no smart defaults added
Development Workflow
Quick iteration cycle:
# Edit code
./scripts/ci-quick.sh # Fast feedback (~5 seconds)
# Continue development
Before push/PR:
./scripts/ci-local.sh # Full CI simulation (~30 seconds)
# Confident push
Coverage Requirements
85% threshold enforced:
- CI fails if total coverage drops below 85%
- Local scripts use same threshold for consistency
- Check coverage: ./scripts/pytest.sh (shows percentage)
Coverage bypass for development:
./scripts/pytest.sh --no-cov # Skip coverage entirely
./scripts/pytest.sh -x # Fail-fast without coverage reporting
CI Workflow Integration
ReadTheDocs Configuration Validation
The project includes validation for .readthedocs.yml to catch configuration errors before they reach ReadTheDocs:
# Test validation with known errors
./scripts/test-rtd-validation.sh
How it works:
- Downloads official RTD JSON schema from their repository
- Validates .readthedocs.yml against the schema using jsonschema
- Catches common errors like:
- Invalid build.jobs structure (must be dict, not list)
- Missing required fields (e.g., version)
- Invalid field values
Integrated into CI:
- Runs in ci-quick.sh for fast local feedback
- Runs in ci-local.sh for full validation
- Runs in GitHub Actions docs-test job
Local Testing (Match CI)
# Modern approach (recommended)
./scripts/ci-local.sh # Exact CI replication
# Manual approach (legacy)
ruff check .
ruff format --check .
mypy .
pytest -q --cov=louieai --cov-report=xml --cov-report=term --cov-fail-under=85
Pre-commit Hooks
Our pre-commit configuration runs:
- ruff check --fix (auto-fix linting issues)
- ruff format (auto-format code)
- mypy (type checking)
- python-check-blanket-noqa (prevent lazy # noqa usage)
CI Pipeline
- Matrix: Tests on Python 3.10, 3.11, 3.12, 3.13
- Steps: Lint → Format → Type Check → Test + Coverage (85% threshold)
- Triggers: PRs and pushes to main/develop/feature/*
- Coverage: XML reports generated for all runs
Debugging CI Failures
Use local CI simulation:
./scripts/ci-local.sh # Replicate exact CI environment
Individual debugging:
1. Lint failures: ./scripts/ruff.sh then ./scripts/format.sh --fix
2. Format failures: ./scripts/format.sh to auto-fix
3. Type failures: ./scripts/mypy.sh, check overrides in pyproject.toml
4. Test failures: ./scripts/pytest.sh -v for detailed output
5. Coverage failures: ./scripts/pytest.sh shows current percentage
Development Conventions
Code Style
- Line length: 88 characters (Black/Ruff standard)
- Imports: Organized by ruff (stdlib, third-party, local)
- Type hints: Required for all public functions
- Docstrings: Required for all public APIs
Commit Messages
type: brief description
Longer explanation if needed.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Types: feat, fix, docs, chore, test, refactor
Branch Naming
feature/description- New featuresbugfix/description- Bug fixesdocs/description- Documentation changeschore/description- Maintenance tasks
Pull Requests
- Link to relevant issues
- Include test coverage for new features
- Ensure all CI checks pass
- Update documentation if needed
Testing Guide
Test Organization
# tests/unit/test_client.py structure:
def test_feature_success(): # Happy path
def test_feature_error_handling(): # Error conditions
def test_feature_edge_cases(): # Boundary conditions
Mocking Patterns
# Mock external dependencies
def test_api_call(monkeypatch):
monkeypatch.setattr(graphistry, "api_token", lambda: "fake-token")
monkeypatch.setattr(httpx, "post", mock_response)
# Test logic here
Test Data
- Keep test data minimal and focused
- Use factories for complex objects
- Mock external API calls (don't hit real services in tests)
Coverage Goals
- Aim for >90% code coverage
- Focus on critical paths and error handling
- Use
./scripts/pytest.shto check coverage
Troubleshooting
Common Issues
Import errors in tests:
# Fix: Ensure proper imports
import louieai # Not from louieai import ...
Mypy errors with external libraries:
# Fix: Add to pyproject.toml
[[tool.mypy.overrides]]
module = "problematic_library.*"
ignore_missing_imports = true
Ruff format conflicts:
- Ruff formatter replaces Black - don't use both
- Use ruff format not black
Pre-commit failures:
# Skip pre-commit for emergency fixes
git commit --no-verify -m "emergency fix"
# Fix pre-commit issues
pre-commit run --all-files
Environment Issues
Python Version Conflicts:
# Check environment (run our diagnostic script)
./scripts/test-env-check.sh
# Common issue: global Python being used instead of venv
# Solution 1: Always use ./bin/uv run
./bin/uv run python --version # Should show 3.12.x
./bin/uv run pytest # Correct way to run tests
# Solution 2: Use python -m pattern
./bin/uv run python -m pytest # Even more explicit
# Solution 3: Reset environment if corrupted
rm -rf .venv
uv venv --python 3.12
uv pip install -e ".[dev]"
Other Environment Issues:
- Dependencies: Fresh install with uv pip install -e ".[dev]"
- Cache issues: Clear with rm -rf .ruff_cache .mypy_cache
- Global tools interfering: Check which pytest (should be in .venv)
Release Process
Dynamic Versioning
We use setuptools_scm for automatic version management:
- Version is determined by git tags (no manual version files)
- Development builds show commit hash: 0.1.1.dev0+g130bd33
- Tagged releases show clean version: 0.1.0
Changelog Management
We use dual changelog approach for maximum compatibility: - CHANGELOG.md: Portable format following keepachangelog.com - GitHub Releases: Rich formatting with API accessibility
Updating CHANGELOG.md
# Add new version section (copy from [Unreleased])
## [0.2.0] - 2025-07-27
### Added
- New feature description
### Changed
- Modified functionality
### Fixed
- Bug fix description
Creating Releases
- Update CHANGELOG.md:
- Move changes from
[Unreleased]to new version section - Use format:
## [X.Y.Z] - YYYY-MM-DD -
Organize by: Added, Changed, Deprecated, Removed, Fixed, Security
-
Test locally:
ruff check . && mypy . && pytest -
Commit changes:
git commit -m "docs: update CHANGELOG for vX.Y.Z" -
Create tag:
git tag vX.Y.Z -
Push tag:
git push origin vX.Y.Z -
Create GitHub Release: ```bash # Using GitHub CLI gh release create vX.Y.Z --title "vX.Y.Z" --notes-from-tag
# Or manually: copy CHANGELOG.md section to GitHub release notes ```
-
CI: GitHub Actions automatically builds and publishes to PyPI
-
Verify: Check PyPI and test
uv pip install louieai==X.Y.Z
Version Detection
# Check current version (includes git info if after tag)
python -c "import louieai; print(louieai.__version__)"
# Check what setuptools_scm would generate
python -c "from setuptools_scm import get_version; print(get_version())"
Pre-release Checklist
- [ ] All tests pass locally and in CI
- [ ] Documentation is up to date
- [ ] CHANGELOG.md includes all changes for this version
- [ ] Tag follows semantic versioning (vX.Y.Z format)
- [ ] PyPI credentials are configured in GitHub secrets
For contribution workflows and community guidelines, see CONTRIBUTING.md.