diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..b9b253c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,91 @@ +# Copilot Instructions for Mixpanel Python SDK + +## Project Overview +This is the official Mixpanel Python library for server-side analytics integration. It provides event tracking, user profile updates, group analytics, and feature flags with both synchronous and asynchronous support. + +## Core Architecture + +### Main Components +- **Mixpanel class** (`mixpanel/__init__.py`): Primary entry point supporting both sync/async operations +- **Consumer pattern**: `Consumer` (immediate) vs `BufferedConsumer` (batched, default 50 messages) +- **Feature Flags**: Local (client-side evaluation) vs Remote (server-side) providers in `mixpanel/flags/` +- **Dual sync/async API**: Most flag operations have both variants (e.g., `get_variant`/`aget_variant`) + +### Key Design Patterns +```python +# Context manager pattern for resource cleanup +async with Mixpanel(token, local_flags_config=config) as mp: + await mp.local_flags.astart_polling_for_definitions() + +# Consumer customization for delivery behavior +mp = Mixpanel(token, consumer=BufferedConsumer()) + +# Custom serialization via DatetimeSerializer +mp = Mixpanel(token, serializer=CustomSerializer) +``` + +## Development Workflows + +### Testing +- **Run tests**: `pytest` (current Python) or `python -m tox` (all supported versions 3.9-3.13) +- **Async testing**: Uses `pytest-asyncio` with `asyncio_mode = "auto"` in pyproject.toml +- **HTTP mocking**: `responses` library for sync code, `respx` for async code +- **Test structure**: `test_*.py` files in root and package directories + +### Building & Publishing +```bash +pip install -e .[test,dev] # Development setup +python -m build # Build distributions +python -m twine upload dist/* # Publish to PyPI +``` + +## Important Conventions + +### API Endpoints & Authentication +- Default endpoint: `api.mixpanel.com` (override via `api_host` parameter) +- **API secret** (not key) required for `import` and `merge` endpoints +- Feature flags use `/decide` endpoint; events use `/track` + +### Error Handling & Retries +- All consumers use urllib3.Retry with exponential backoff (default 4 retries) +- `MixpanelException` for domain-specific errors +- Feature flag operations degrade gracefully with fallback values + +### Version & Dependencies Management +- Version defined in `mixpanel/__init__.py` as `__version__` +- Uses Pydantic v2+ for data validation (`mixpanel/flags/types.py`) +- json-logic library for runtime flag evaluation rules + +## Feature Flag Specifics + +### Local Flags (Client-side evaluation) +- Require explicit polling: `start_polling_for_definitions()` or context manager +- Default 60s polling interval, configurable via `LocalFlagsConfig` +- Runtime evaluation using json-logic for dynamic targeting + +### Remote Flags (Server-side evaluation) +- Each evaluation makes API call to Mixpanel +- Better for sensitive targeting logic +- Configure via `RemoteFlagsConfig` + +### Flag Configuration Pattern +```python +local_config = mixpanel.LocalFlagsConfig( + api_host="api-eu.mixpanel.com", # EU data residency + enable_polling=True, + polling_interval_in_seconds=90 +) +mp = Mixpanel(token, local_flags_config=local_config) +``` + +## Testing Patterns +- Mock HTTP with `responses.activate` decorator for sync tests +- Use `respx.mock` for async HTTP testing +- Test consumer behavior via `LogConsumer` pattern (see `test_mixpanel.py`) +- Always test both sync and async variants of flag operations + +## Critical Implementation Notes +- `alias()` method always uses synchronous Consumer regardless of main consumer type +- Local flags require explicit startup; use context managers for proper cleanup +- DateTime serialization handled by `DatetimeSerializer` class +- All flag providers support custom API endpoints for data residency requirements \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bcf4578 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,122 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is the official Mixpanel Python library for server-side integration. It provides event tracking, user profile updates, group analytics, and feature flags functionality. The library supports both synchronous and asynchronous operations. + +## Development Commands + +### Environment Setup +```bash +# Install development and test dependencies +pip install -e .[test,dev] +``` + +### Testing +```bash +# Run all tests across all Python versions (3.9-3.13, PyPy) +python -m tox + +# Run tests for current Python version only +pytest + +# Run with coverage +python -m coverage run -m pytest +python -m coverage report -m +python -m coverage html + +# Run specific test file +pytest test_mixpanel.py +pytest mixpanel/flags/test_local_feature_flags.py +``` + +### Building and Publishing +```bash +# Build distribution packages +python -m build + +# Publish to PyPI +python -m twine upload dist/* +``` + +### Documentation +```bash +# Build documentation +python -m sphinx -b html docs docs/_build/html + +# Publish docs to GitHub Pages +python -m ghp_import -n -p docs/_build/html +``` + +## Architecture + +### Core Components + +**Mixpanel Class** (`mixpanel/__init__.py`) +- Main entry point for all tracking operations +- Supports context managers (both sync and async) +- Integrates with Consumer classes for message delivery +- Optional feature flags providers (local and remote) + +**Consumers** +- `Consumer`: Sends HTTP requests immediately (one per call) +- `BufferedConsumer`: Batches messages (default max 50) before sending +- Both support retry logic (default 4 retries with exponential backoff) +- All consumers support custom API endpoints via `api_host` parameter + +**Feature Flags** (`mixpanel/flags/`) +- `LocalFeatureFlagsProvider`: Client-side evaluation with polling (default 60s interval) +- `RemoteFeatureFlagsProvider`: Server-side evaluation via API calls +- Both providers support async operations +- Types defined in `mixpanel/flags/types.py` using Pydantic models + +### Key Design Patterns + +1. **Dual Sync/Async Support**: Most feature flag operations have both sync and async variants (e.g., `get_variant` / `aget_variant`) + +2. **Consumer Pattern**: Events/updates are sent via consumer objects, allowing customization of delivery behavior without changing tracking code + +3. **Context Managers**: The Mixpanel class supports both `with` and `async with` patterns to manage flag provider lifecycle + +4. **JSON Serialization**: Custom `DatetimeSerializer` handles datetime objects; extensible via `serializer` parameter + +5. **Runtime Rules Engine**: Local flags support runtime evaluation using json-logic library for dynamic targeting + +## Testing Patterns + +- Tests use `pytest` with `pytest-asyncio` for async support +- `responses` library mocks HTTP requests for sync code +- `respx` library mocks HTTP requests for async code +- Test files follow pattern: `test_*.py` in root or within package directories +- Pytest config: `asyncio_mode = "auto"` in pyproject.toml + +## Dependencies + +- `requests>=2.4.2, <3`: HTTP client (sync) +- `httpx>=0.27.0`: HTTP client (async) +- `pydantic>=2.0.0`: Data validation and types +- `asgiref>=3.0.0`: Async utilities +- `json-logic>=0.7.0a0`: Runtime rules evaluation + +## Version Management + +Version is defined in `mixpanel/__init__.py` as `__version__` and dynamically loaded by setuptools. + +## API Endpoints + +Default: `api.mixpanel.com` +- Events: `/track` +- People: `/engage` +- Groups: `/groups` +- Imports: `/import` +- Feature Flags: `/decide` + +## Important Notes + +- API secret (not API key) is required for `import` and `merge` endpoints +- `alias()` always uses synchronous Consumer regardless of main consumer type +- Feature flags require opt-in via constructor config parameters +- Local flags poll for updates; call `start_polling_for_definitions()` or use context manager +- Retry logic uses urllib3.Retry with exponential backoff