|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Commands |
| 6 | + |
| 7 | +```bash |
| 8 | +make test # run all tests |
| 9 | +make typecheck # mypy strict check on avito/ |
| 10 | +make lint # ruff check |
| 11 | +make fmt # ruff format |
| 12 | +make check # test → typecheck → lint → build (full gate) |
| 13 | +make build # poetry build |
| 14 | + |
| 15 | +# single test |
| 16 | +poetry run pytest tests/test_facade.py::test_name |
| 17 | +``` |
| 18 | + |
| 19 | +## Architecture |
| 20 | + |
| 21 | +**Entry point**: `avito/client.py` — `AvitoClient` is the single public facade. It exposes factory methods (`account()`, `ad()`, `chat()`, etc.) that return domain objects. |
| 22 | + |
| 23 | +**Layers** (strict separation, no mixing): |
| 24 | + |
| 25 | +| Layer | Location | Responsibility | |
| 26 | +|---|---|---| |
| 27 | +| `AvitoClient` | `avito/client.py` | Public facade, factory methods | |
| 28 | +| `SectionClient` | `avito/<domain>/client.py` | HTTP calls for one API section | |
| 29 | +| `Transport` | `avito/core/transport.py` | httpx, retries, error mapping, token injection | |
| 30 | +| `AuthProvider` | `avito/auth/provider.py` | Token cache, refresh, 401 handling | |
| 31 | +| `Mapper` | `avito/<domain>/mappers.py` | JSON → typed dataclass | |
| 32 | +| Config | `avito/config.py`, `avito/auth/settings.py` | `AvitoSettings`, `AuthSettings` | |
| 33 | + |
| 34 | +**Domain packages** follow a uniform structure: `__init__.py`, `domain.py` (DomainObject subclass), `client.py` (SectionClient), `models.py` (frozen dataclasses), `mappers.py`, optional `enums.py`. |
| 35 | + |
| 36 | +**Public models** are `@dataclass(slots=True, frozen=True)`, inherit `SerializableModel` (provides `to_dict()` / `model_dump()`), and never expose transport fields. |
| 37 | + |
| 38 | +**Exceptions** live in `avito/core/exceptions.py`. `AvitoError` is the base. HTTP codes map to specific types: 401→`AuthenticationError`, 403→`AuthorizationError`, 429→`RateLimitError`, etc. These two are siblings, not parent/child. |
| 39 | + |
| 40 | +**Pagination**: `PaginatedList[T]` is lazy. First page loads on creation; subsequent pages load on iteration. `materialize()` loads all pages. |
| 41 | + |
| 42 | +**Testing**: `tests/fake_transport.py` provides `FakeTransport` — inject it instead of real HTTP. Tests are Arrange/Act/Assert, one scenario per test. Test names describe behavior, not the method under test. |
| 43 | + |
| 44 | +## API coverage and inventory |
| 45 | + |
| 46 | +`docs/` contains Swagger/OpenAPI specs (23 documents, 204 operations) — the authoritative source of truth for all API contracts. |
| 47 | + |
| 48 | +`docs/inventory.md` is the canonical mapping of every API operation to its SDK domain object and public method. Before implementing any new method, check the inventory to find: |
| 49 | +- which `пакет_sdk` and `доменный_объект` it belongs to |
| 50 | +- the expected `публичный_метод_sdk`, request/response type names |
| 51 | +- whether the operation is deprecated (`deprecated: да` → wrap in a legacy domain object) |
| 52 | + |
| 53 | +**When adding a new API method**: add it to the `## Операции` table in `docs/inventory.md` (between the `operations-table:start/end` markers) following the existing format. |
| 54 | + |
| 55 | +All 204 operations from the specs must be covered. A missing method is a defect. |
| 56 | + |
| 57 | +## STYLEGUIDE.md — strict compliance is mandatory |
| 58 | + |
| 59 | +`STYLEGUIDE.md` is a normative document. All code changes **must** comply with it. When there is a conflict between any consideration and the STYLEGUIDE, the STYLEGUIDE takes priority. |
| 60 | + |
| 61 | +The most critical prohibitions that must never be violated: |
| 62 | + |
| 63 | +- Mixing layers: transport/auth/parsing/domain logic in one class. |
| 64 | +- Returning `dict` or `Any` from public methods. |
| 65 | +- Using `resource_id` instead of concrete names (`item_id`, `order_id`). |
| 66 | +- Annotating `list[T]` where `PaginatedList[T]` is returned at runtime. |
| 67 | +- Making `AuthenticationError` a subclass of `AuthorizationError` (or vice versa). |
| 68 | +- Writing error messages in mixed languages (Russian only). |
| 69 | +- Injecting methods via `setattr`/`globals()` at runtime. |
| 70 | +- Duplicating behavior through two different public methods without deprecation. |
| 71 | +- Leaking internal-layer request-DTOs into public signatures. |
| 72 | +- Adding dead code: unused imports, type aliases, TypeVars. |
| 73 | + |
| 74 | +## Key conventions (from STYLEGUIDE.md) |
| 75 | + |
| 76 | +- All public methods return typed SDK models, never raw `dict`. |
| 77 | +- Field names are concrete: `item_id`, `user_id` — never `resource_id`. |
| 78 | +- Public method arguments are primitives or domain models — internal request-DTOs must not leak out. |
| 79 | +- Write-operations that accept `dry_run: bool = False` must build the same payload in both modes; with `dry_run=True` transport must not be called. |
| 80 | +- `Any` is forbidden except at JSON boundary layers (with a local comment). |
| 81 | +- Error messages are written in Russian only — no mixed languages. |
| 82 | +- No dynamic method injection (`setattr`, `globals()` patching). |
| 83 | +- `PaginatedList[T]` annotation must match runtime — never annotate as `list[T]` if you return `PaginatedList[T]`. |
| 84 | +- `AuthenticationError` (401) and `AuthorizationError` (403) must not be in an inheritance relation. |
| 85 | +- Dead code (unused imports, aliases, TypeVars) must be removed. |
| 86 | +- Request-objects of the internal layer must not appear in public domain-method signatures. |
0 commit comments