|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +axiom-py is the official Python client library for Axiom, a serverless log management and analytics platform. The library provides bindings for ingesting events, querying data, and managing datasets via the Axiom REST API. |
| 8 | + |
| 9 | +## Development Commands |
| 10 | + |
| 11 | +### Running Tests |
| 12 | +```bash |
| 13 | +# Run all tests (requires AXIOM_TOKEN, AXIOM_ORG_ID, and optionally AXIOM_URL env vars) |
| 14 | +uv run pytest |
| 15 | + |
| 16 | +# Run a specific test file |
| 17 | +uv run pytest tests/test_client.py |
| 18 | + |
| 19 | +# Run a specific test |
| 20 | +uv run pytest tests/test_client.py::TestClient::test_step001_ingest |
| 21 | +``` |
| 22 | + |
| 23 | +### Linting and Formatting |
| 24 | +```bash |
| 25 | +# Check code with ruff |
| 26 | +uv run ruff check |
| 27 | + |
| 28 | +# Auto-fix issues |
| 29 | +uv run ruff check --fix |
| 30 | + |
| 31 | +# Check formatting |
| 32 | +uv run ruff format --check |
| 33 | + |
| 34 | +# Format code |
| 35 | +uv run ruff format |
| 36 | +``` |
| 37 | + |
| 38 | +### Pre-commit Hooks |
| 39 | +```bash |
| 40 | +# Install pre-commit hooks |
| 41 | +uv run pre-commit install |
| 42 | + |
| 43 | +# Run hooks manually |
| 44 | +uv run pre-commit run --all-files |
| 45 | +``` |
| 46 | + |
| 47 | +### Building |
| 48 | +```bash |
| 49 | +# Build the package for distribution |
| 50 | +uv build |
| 51 | +``` |
| 52 | + |
| 53 | +## Architecture |
| 54 | + |
| 55 | +### Core Components |
| 56 | + |
| 57 | +**Client (`src/axiom_py/client.py`)**: The main `Client` class is the entry point for all Axiom operations. It manages: |
| 58 | +- HTTP session with automatic retries (3 retries with exponential backoff for 5xx errors) |
| 59 | +- Authentication via Bearer token (from constructor or `AXIOM_TOKEN` env var) |
| 60 | +- Organization ID handling (from constructor or `AXIOM_ORG_ID` env var) |
| 61 | +- Shutdown hooks registered with `atexit` to flush buffered logs |
| 62 | + |
| 63 | +The Client exposes specialized sub-clients as properties: |
| 64 | +- `client.datasets` - Dataset CRUD operations |
| 65 | +- `client.annotations` - Annotation management |
| 66 | +- `client.users` - User information |
| 67 | +- `client.tokens` - API token management |
| 68 | + |
| 69 | +**DatasetsClient (`src/axiom_py/datasets.py`)**: Handles dataset operations (create, get, list, update, delete, trim). Takes a requests.Session object and makes API calls to `/v1/datasets` endpoints. |
| 70 | + |
| 71 | +**Query System (`src/axiom_py/query/`)**: Supports two query types: |
| 72 | +- **APL queries** (recommended): Uses Axiom Processing Language via `client.query()` or `client.apl_query()`. Supports both "legacy" and "tabular" result formats. |
| 73 | +- **Legacy structured queries**: Uses `QueryLegacy` dataclass with filters, aggregations, groupBy, etc. via `client.query_legacy()`. |
| 74 | + |
| 75 | +**Logging Integration (`src/axiom_py/logging.py`, `src/axiom_py/structlog.py`)**: |
| 76 | +- `AxiomHandler`: Standard Python logging handler that buffers log records and flushes to Axiom every 1 second or when buffer reaches 1000 events. |
| 77 | +- `AxiomProcessor`: Structlog processor with similar buffering behavior. |
| 78 | +- Both use threading.Timer (AxiomHandler) or time checks to periodically flush. |
| 79 | + |
| 80 | +### Key Design Patterns |
| 81 | + |
| 82 | +**Ingestion**: Events are encoded as NDJSON and gzip-compressed before sending. The `ingest_events()` method is a convenience wrapper around the lower-level `ingest()` method that handles this automatically. |
| 83 | + |
| 84 | +**Error Handling**: All HTTP responses are checked via a response hook that raises `AxiomError` for status codes >= 400. This exception includes the status code and error message from the API. |
| 85 | + |
| 86 | +**Serialization**: Uses `dacite` for deserializing API responses to dataclasses, and custom JSON handling (`handle_json_serialization` in `util.py`) for datetime objects during serialization. |
| 87 | + |
| 88 | +**Type Safety**: The codebase heavily uses dataclasses and type hints (Python 3.8+). Field names use snake_case in Python but are automatically converted to/from camelCase for API communication using the `pyhumps` library. |
| 89 | + |
| 90 | +## Testing Guidelines |
| 91 | + |
| 92 | +- Tests are integration tests that run against live Axiom environments (dev and staging) |
| 93 | +- Test methods should be prefixed with `test_step` and numbered (e.g., `test_step001_ingest`) to control execution order when needed |
| 94 | +- Use `get_random_name()` helper from `tests/helpers.py` to generate unique dataset names |
| 95 | +- Clean up resources in `tearDownClass` even on test failures to avoid zombie datasets |
| 96 | +- Tests use the `responses` library for mocking HTTP requests when testing client behavior (retries, error handling) |
| 97 | + |
| 98 | +## Code Style |
| 99 | + |
| 100 | +- Line length: 79 characters (PEP 8) |
| 101 | +- Uses Ruff for both linting and formatting |
| 102 | +- Python 3.8+ compatible (check `classifiers` in pyproject.toml before using newer syntax) |
| 103 | + |
| 104 | +## Important Notes |
| 105 | + |
| 106 | +- Version 0.9.0 removed the aggregation operation enum (see #158). Use string literals instead. |
| 107 | +- When using APL queries with `limit`, pass it via `AplOptions.limit` parameter (not in the APL query string). |
| 108 | +- Personal tokens don't require an org_id, but organization tokens do. The `is_personal_token()` utility in `tokens.py` detects token type. |
0 commit comments