This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
make test # run all tests
make typecheck # mypy strict check on avito/
make lint # ruff check
make swagger-lint # strict Swagger binding coverage check
make fmt # ruff format
make check # test → typecheck → lint → swagger-lint → build (full gate)
make build # poetry build
# single test
poetry run pytest tests/test_facade.py::test_nameEntry point: avito/client.py — AvitoClient is the single public facade. It exposes factory methods (account(), ad(), chat(), etc.) that return domain objects.
Layers (strict separation, no mixing):
| Layer | Location | Responsibility |
|---|---|---|
AvitoClient |
avito/client.py |
Public facade, factory methods |
DomainObject |
avito/<domain>/domain.py |
Explicit public methods, validation, docstrings, Swagger bindings |
OperationSpec |
avito/<domain>/operations.py or operations/ |
Internal HTTP method/path/context/retry/request/response metadata |
OperationExecutor |
avito/core/operations.py |
Executes operation specs through transport |
Transport |
avito/core/transport.py |
httpx, retries, error mapping, token injection |
AuthProvider |
avito/auth/provider.py |
Token cache, refresh, 401 handling |
| Models | avito/<domain>/models.py or models/ |
dataclasses, enums, from_payload(), to_payload(), to_params() |
| Config | avito/config.py, avito/auth/settings.py |
AvitoSettings, AuthSettings |
Target domain packages follow the v2 architecture in docs/site/explanations/domain-architecture-v2.md: __init__.py, domain.py (DomainObject subclasses), operations.py or operations/ (OperationSpec definitions), and models.py or models/ (frozen dataclasses, colocated enums, payload parsing/serialization). Do not add new client.py, mappers.py, or standalone enums.py without an explicit architecture reason. Existing legacy modules may remain during migration; compatibility mappers should delegate to Model.from_payload().
Public models are @dataclass(slots=True, frozen=True), inherit ApiModel or another approved SerializableModel subclass (provides to_dict() / model_dump()), implement from_payload() for API JSON, and never expose transport fields. Request/query dataclasses use to_payload() / to_params() and stay out of public method signatures unless explicitly documented as public input models.
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.
Pagination: PaginatedList[T] is lazy. First page loads on creation; subsequent pages load on iteration. materialize() loads all pages.
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.
docs/avito/api/ contains Swagger/OpenAPI specs (23 documents, 204 operations) — the authoritative source of truth for all API contracts.
The canonical SDK coverage map is built from Swagger operation bindings discovered on public domain methods, not from markdown inventory files.
Public SDK methods are documented in docs/site/reference/ and generated by the MkDocs reference builder from the actual package surface. All 204 operations from the specs must be covered. A missing method is a defect.
The persistent subsystem context is documented in docs/site/explanations/swagger-binding-subsystem.md.
Core invariant:
each Swagger operation -> exactly one discovered binding
each discovered SDK method -> exactly one Swagger operation
Multiple Swagger bindings on one public SDK method are forbidden. If one public scenario covers different upstream modes, expose separate documented SDK methods and keep compatibility wrappers unbound.
When adding or changing a public method that corresponds to Avito API:
- consult
docs/avito/api/*.jsonfirst; - add or update the public domain method, operation spec, typed models, request/query dataclasses, and
from_payload()/to_payload()/to_params()mapping; - add
@swagger_operation(...)on the public domain method; - do not put schemas, statuses, content types, request models, response models, error models, path params, or query params into the decorator;
- add or update class-level Swagger metadata when introducing a domain class;
- write a reference-ready docstring for the public method: business action, arguments, return model, pagination/dry-run/idempotency behavior when relevant, and common SDK exceptions;
- update
docs/site/how-to/ordocs/site/explanations/if the method introduces a workflow or a non-obvious contract; - update
docs/site/explanations/swagger-binding-subsystem.mdwhen changing discovery, linter, JSON report,SwaggerFakeTransport, deprecated/legacy policy, or multi-operation binding policy.
Minimum verification for API-related changes:
make swagger-lint
poetry run pytest tests/core/test_swagger*.py tests/contracts/test_swagger_contracts.py
poetry run pytest tests/domains/<domain>/
poetry run mypy avito
poetry run ruff check .Before completing an API-surface change, run make check. If generated docs, docs snippets, coverage pages, or reference output changed, also run make docs-strict.
.ai/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.
The most critical prohibitions that must never be violated:
- Mixing layers: transport/auth/parsing/domain logic in one class.
- Returning
dictorAnyfrom public methods. - Using
resource_idinstead of concrete names (item_id,order_id). - Annotating
list[T]wherePaginatedList[T]is returned at runtime. - Adding or changing an Avito API public method without a
@swagger_operation(...)binding. - Adding or changing an Avito API public method without a matching
OperationSpecor documented legacy adapter. - Adding or changing an Avito API public method without a reference-ready docstring.
- Duplicating Swagger contract data inside binding decorators.
- Making
AuthenticationErrora subclass ofAuthorizationError(or vice versa). - Writing error messages in mixed languages (Russian only).
- Injecting methods via
setattr/globals()at runtime. - Duplicating behavior through two different public methods without deprecation.
- Leaking internal-layer request-DTOs into public signatures.
- Adding new
client.py,mappers.py, or standaloneenums.pyin a new/refactored domain without an explicit architecture note. - Adding dead code: unused imports, type aliases, TypeVars.
- All public methods return typed SDK models, never raw
dict. - New/refactored domains use v2 layout:
domain.py,operations.py/operations/,models.py/models/. - API response parsing belongs in
Model.from_payload(); request/query serialization belongs in dataclasses viato_payload()/to_params(). - Enums live next to the models that use them; legacy
enums.pymodules are compatibility re-exports only. - Field names are concrete:
item_id,user_id— neverresource_id. - Public method arguments are primitives or domain models — internal request-DTOs must not leak out.
- Write-operations that accept
dry_run: bool = Falsemust build the same payload in both modes; withdry_run=Truetransport must not be called. Anyis forbidden except at JSON boundary layers (with a local comment).- Error messages are written in Russian only — no mixed languages.
- No dynamic method injection (
setattr,globals()patching). PaginatedList[T]annotation must match runtime — never annotate aslist[T]if you returnPaginatedList[T].AuthenticationError(401) andAuthorizationError(403) must not be in an inheritance relation.- Dead code (unused imports, aliases, TypeVars) must be removed.
- Request-objects of the internal layer must not appear in public domain-method signatures.