39 lines
1.1 KiB
Markdown
39 lines
1.1 KiB
Markdown
# Project Rules
|
|
|
|
## Python Async Generator Safety
|
|
|
|
When writing async generator functions (async def with yield), NEVER use early `return` before the first `yield`. This causes Python to treat the function as a coroutine instead of an async generator, resulting in `'async for' requires an object with __aiter__ method, got coroutine` errors at runtime.
|
|
|
|
**Wrong:**
|
|
```python
|
|
async def subscribe(self):
|
|
if self._closed:
|
|
return # BUG: makes this a coroutine when _closed=True
|
|
...
|
|
yield event
|
|
```
|
|
|
|
**Correct — use `return; yield` pattern:**
|
|
```python
|
|
async def subscribe(self):
|
|
if self._closed:
|
|
return
|
|
yield # Makes this always an async generator
|
|
...
|
|
yield event
|
|
```
|
|
|
|
**Correct — restructure to avoid early return:**
|
|
```python
|
|
async def subscribe(self):
|
|
if not self._closed:
|
|
...
|
|
yield event
|
|
```
|
|
|
|
This applies to ALL async generator functions in the codebase. When adding an early exit path to any `async def` that contains `yield`, always ensure the `yield` is reachable or add a `return; yield` guard.
|
|
|
|
## Testing
|
|
|
|
- Run `python3 -m pytest tests/unit/ -x -q` before committing
|