|
| 1 | +# PYTHON CLIENT - AI AGENT INSTRUCTIONS |
| 2 | + |
| 3 | +## ⚠️ CRITICAL: CHECK YOUR REPOSITORY FIRST |
| 4 | + |
| 5 | +Before making ANY changes, verify you're in the correct repository: |
| 6 | + |
| 7 | +```bash |
| 8 | +git remote -v |
| 9 | +``` |
| 10 | + |
| 11 | +- ✅ **CORRECT**: `origin .../algolia/api-clients-automation.git` → You may proceed |
| 12 | +- ❌ **WRONG**: `origin .../algolia/algoliasearch-client-python.git` → STOP! This is the PUBLIC repository |
| 13 | + |
| 14 | +**If you're in `algoliasearch-client-python`**: Do NOT make changes here. All changes must go through `api-clients-automation`. PRs and commits made directly to the public repo will be discarded on next release. |
| 15 | + |
| 16 | +## ⚠️ BEFORE ANY EDIT: Check If File Is Generated |
| 17 | + |
| 18 | +Before editing ANY file, verify it's hand-written by checking `config/generation.config.mjs`: |
| 19 | + |
| 20 | +```javascript |
| 21 | +// In generation.config.mjs - patterns WITHOUT '!' are GENERATED (do not edit) |
| 22 | +'clients/algoliasearch-client-python/algoliasearch/**', // Generated |
| 23 | +'!clients/algoliasearch-client-python/algoliasearch/http/**', // Hand-written ✓ |
| 24 | +``` |
| 25 | + |
| 26 | +**Hand-written (safe to edit):** |
| 27 | + |
| 28 | +- `algoliasearch/http/**` - HTTP transport, retry logic, exceptions |
| 29 | +- `algoliasearch/py.typed` - Type marker |
| 30 | + |
| 31 | +**Generated (DO NOT EDIT):** |
| 32 | + |
| 33 | +- `algoliasearch/{client}/` directories (search, insights, etc.) |
| 34 | +- `algoliasearch/{client}/models/` - API models |
| 35 | +- `algoliasearch/{client}/client.py` - API clients |
| 36 | +- `pyproject.toml`, `poetry.lock`, `requirements.txt` |
| 37 | + |
| 38 | +## Language Conventions |
| 39 | + |
| 40 | +### Naming |
| 41 | + |
| 42 | +- **Files**: `snake_case.py` |
| 43 | +- **Variables/Functions**: `snake_case` |
| 44 | +- **Classes**: `PascalCase` |
| 45 | +- **Constants**: `UPPER_SNAKE_CASE` |
| 46 | +- **Private**: `_leading_underscore` |
| 47 | + |
| 48 | +### Formatting |
| 49 | + |
| 50 | +- Ruff for linting (line-length: 88) |
| 51 | +- Pyright for type checking |
| 52 | +- Run formatting via CLI: `yarn cli format python clients/algoliasearch-client-python` |
| 53 | + |
| 54 | +### Type Hints |
| 55 | + |
| 56 | +- Full type hints required (Python 3.8+ style) |
| 57 | +- Use `Optional[T]` for nullable, `Union[A, B]` for unions |
| 58 | +- Use `List`, `Dict` from `typing` module (not built-in generics for 3.8 compat) |
| 59 | + |
| 60 | +### Dependencies |
| 61 | + |
| 62 | +- **HTTP**: `aiohttp` for async requests |
| 63 | +- **Async timeout**: `async-timeout` |
| 64 | +- **Build**: Poetry |
| 65 | +- **Types**: Pydantic for models |
| 66 | + |
| 67 | +## Client Patterns |
| 68 | + |
| 69 | +### Transporter Architecture |
| 70 | + |
| 71 | +```python |
| 72 | +# Core transport in algoliasearch/http/ |
| 73 | +class Transporter(BaseTransporter): |
| 74 | + def __init__(self, config: BaseConfig): |
| 75 | + self._session: Optional[ClientSession] = None |
| 76 | + self._retry_strategy = RetryStrategy() |
| 77 | + |
| 78 | + async def request(self, verb, path, request_options, use_read_transporter): |
| 79 | + # Retry logic with host failover |
| 80 | +``` |
| 81 | + |
| 82 | +### Async-First Design |
| 83 | + |
| 84 | +- All API methods are `async def` |
| 85 | +- Must run in asyncio event loop |
| 86 | +- Use `async with` for client lifecycle |
| 87 | + |
| 88 | +```python |
| 89 | +async with SearchClient("APP_ID", "API_KEY") as client: |
| 90 | + response = await client.search(...) |
| 91 | +``` |
| 92 | + |
| 93 | +### Retry Strategy |
| 94 | + |
| 95 | +- `RetryOutcome`: `SUCCESS`, `RETRY`, `FAILURE` |
| 96 | +- Host states: `UP`, `DOWN`, `TIMED_OUT` |
| 97 | +- Exponential backoff on timeouts |
| 98 | + |
| 99 | +### Exception Hierarchy |
| 100 | + |
| 101 | +```python |
| 102 | +# algoliasearch/http/exceptions.py |
| 103 | +AlgoliaException # Base |
| 104 | +├── RequestException # Request failed |
| 105 | +├── AlgoliaUnreachableHostException # All hosts failed |
| 106 | +└── AlgoliaApiException # API error response |
| 107 | +``` |
| 108 | + |
| 109 | +## Common Gotchas |
| 110 | + |
| 111 | +### Async Context Required |
| 112 | + |
| 113 | +```python |
| 114 | +# WRONG - won't work outside async context |
| 115 | +response = client.search(...) |
| 116 | + |
| 117 | +# CORRECT |
| 118 | +response = await client.search(...) |
| 119 | + |
| 120 | +# Or use asyncio.run() for scripts |
| 121 | +import asyncio |
| 122 | +asyncio.run(main()) |
| 123 | +``` |
| 124 | + |
| 125 | +### Session Lifecycle |
| 126 | + |
| 127 | +```python |
| 128 | +# Always close the client or use context manager |
| 129 | +client = SearchClient("APP_ID", "API_KEY") |
| 130 | +try: |
| 131 | + await client.search(...) |
| 132 | +finally: |
| 133 | + await client.close() |
| 134 | + |
| 135 | +# Or prefer context manager |
| 136 | +async with SearchClient("APP_ID", "API_KEY") as client: |
| 137 | + await client.search(...) |
| 138 | +``` |
| 139 | + |
| 140 | +### Type Narrowing |
| 141 | + |
| 142 | +```python |
| 143 | +# Use isinstance for union types |
| 144 | +from algoliasearch.search.models import Hit |
| 145 | + |
| 146 | +if isinstance(result, Hit): |
| 147 | + print(result.object_id) |
| 148 | +``` |
| 149 | + |
| 150 | +### Python Version Compatibility |
| 151 | + |
| 152 | +- Support Python 3.8 - 3.12 |
| 153 | +- Avoid walrus operator `:=` (3.8 has issues) |
| 154 | +- Use `typing` module generics, not built-in `list[T]` |
| 155 | + |
| 156 | +## Build & Test Commands |
| 157 | + |
| 158 | +```bash |
| 159 | +# From repo root (api-clients-automation) |
| 160 | +yarn cli build clients python # Build Python client |
| 161 | +yarn cli cts generate python # Generate CTS tests |
| 162 | +yarn cli cts run python # Run CTS tests |
| 163 | +yarn cli playground python search # Interactive playground |
| 164 | +yarn cli format python clients/algoliasearch-client-python |
| 165 | + |
| 166 | +# From client directory (requires Poetry) |
| 167 | +cd clients/algoliasearch-client-python |
| 168 | +poetry install # Install dependencies |
| 169 | +poetry run pytest # Run tests |
| 170 | +poetry run pyright # Type check |
| 171 | +``` |
0 commit comments