Skip to content

Commit 91132da

Browse files
committed
Readme
1 parent 9ac44b0 commit 91132da

1 file changed

Lines changed: 90 additions & 48 deletions

File tree

README.md

Lines changed: 90 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![PyPI Downloads](https://img.shields.io/pypi/dm/avito-py.svg)](https://pypi.org/project/avito-py/)
66
[![API coverage](https://img.shields.io/badge/API%20coverage-204%2F204-success)](docs/inventory.md)
77

8-
`avito-py` — Python SDK для работы с Avito API через единый объектный фасад `AvitoClient`.
8+
`avito-py`синхронный Python SDK для работы с Avito API через единый объектный фасад `AvitoClient`.
99

1010
Цели SDK:
1111

@@ -14,6 +14,8 @@
1414
- дать единый вход в доменные сценарии вида `avito.ad(...).get()` и `avito.chat(...).send_message(...)`;
1515
- покрыть все swagger-документы из каталога [docs](docs).
1616

17+
SDK является синхронным. Любая асинхронная поддержка, если она появится, будет жить в отдельном namespace `avito.aio` и никогда не будет смешана с sync-классами в одном модуле.
18+
1719
Каталог [docs](docs) рассматривается как upstream API contract. Эти файлы не редактируются вручную при развитии SDK: публичные модели, мапперы и тесты должны подстраиваться под documented shape из `docs/*`.
1820

1921
## Установка
@@ -32,22 +34,46 @@ pip install avito-py
3234

3335
## Быстрый старт
3436

35-
Получение ключей - https://www.avito.ru/professionals/api
37+
Получение ключей https://www.avito.ru/professionals/api
3638

3739
```python
3840
from avito import AvitoClient
3941

40-
with AvitoClient() as avito:
42+
with AvitoClient.from_env() as avito:
4143
profile = avito.account().get_self()
42-
ad = avito.ad(42).get()
44+
ad = avito.ad(item_id=42, user_id=123).get()
4345

4446
print(profile.name)
4547
print(ad.title)
4648
```
4749

4850
По умолчанию настройки читаются из переменных окружения с префиксом `AVITO_`.
4951

50-
Официальный способ конфигурации SDK:
52+
## Инициализация клиента
53+
54+
SDK предоставляет три нормативных способа создания клиента — от самого простого к самому явному.
55+
56+
### 1. Из переменных окружения
57+
58+
```python
59+
from avito import AvitoClient
60+
61+
with AvitoClient.from_env() as avito:
62+
...
63+
```
64+
65+
### 2. Напрямую через `client_id` / `client_secret`
66+
67+
Короткий путь без промежуточных объектов:
68+
69+
```python
70+
from avito import AvitoClient
71+
72+
with AvitoClient(client_id="client-id", client_secret="client-secret") as avito:
73+
...
74+
```
75+
76+
### 3. Полная конфигурация через `AvitoSettings`
5177

5278
```python
5379
from avito import AuthSettings, AvitoClient, AvitoSettings
@@ -60,17 +86,14 @@ settings = AvitoSettings(
6086
client_secret="client-secret",
6187
),
6288
)
63-
client = AvitoClient(settings)
64-
```
6589

66-
Инициализация из окружения и `.env`:
90+
with AvitoClient(settings) as avito:
91+
...
92+
```
6793

68-
```python
69-
from avito import AvitoClient, AvitoSettings
94+
Все опциональные параметры конструктора — keyword-only. `AvitoClient` иммутабелен: `base_url`, таймауты, retry-политика и `auth` не меняются у живого клиента — вместо этого создаётся новый клиент.
7095

71-
settings = AvitoSettings.from_env()
72-
client = AvitoClient.from_env()
73-
```
96+
### Переменные окружения
7497

7598
Поддерживаемые env-переменные и alias-имена:
7699

@@ -91,7 +114,7 @@ client = AvitoClient.from_env()
91114

92115
- значения из process environment имеют приоритет над `.env`;
93116
- `AvitoSettings.from_env()` и `AvitoClient.from_env()` детерминированно читают `.env` из текущей рабочей директории или из переданного `env_file`;
94-
- при отсутствии `client_id` или `client_secret` SDK завершает инициализацию с typed-ошибкой `ConfigurationError`.
117+
- при отсутствии `client_id` или `client_secret` SDK завершает инициализацию с typed-ошибкой `ConfigurationError` до первого HTTP-запроса.
95118

96119
## Примеры по доменам
97120

@@ -100,7 +123,7 @@ client = AvitoClient.from_env()
100123
```python
101124
from avito import AvitoClient
102125

103-
with AvitoClient() as avito:
126+
with AvitoClient.from_env() as avito:
104127
account = avito.account(user_id=123)
105128
balance = account.get_balance()
106129
ad = avito.ad(item_id=42, user_id=123).get()
@@ -112,7 +135,7 @@ with AvitoClient() as avito:
112135
```python
113136
from avito import AvitoClient
114137

115-
with AvitoClient() as avito:
138+
with AvitoClient.from_env() as avito:
116139
profile = avito.autoload_profile(user_id=123).get()
117140
report = avito.autoload_report(report_id=777).get()
118141
```
@@ -123,7 +146,7 @@ with AvitoClient() as avito:
123146
from avito import AvitoClient
124147
from avito.messenger import UploadImageFile
125148

126-
with AvitoClient() as avito:
149+
with AvitoClient.from_env() as avito:
127150
chats = avito.chat(user_id=123).list()
128151
message = avito.chat_message(chat_id="chat-1", user_id=123).send_message(
129152
message="Здравствуйте"
@@ -147,7 +170,7 @@ with AvitoClient() as avito:
147170
from avito import AvitoClient
148171
from datetime import datetime
149172

150-
with AvitoClient() as avito:
173+
with AvitoClient.from_env() as avito:
151174
services = avito.promotion_order().list_orders()
152175
forecast = avito.bbip_promotion(item_id=42).get_forecasts(items=[])
153176
budget = avito.autostrategy_campaign().create_budget(
@@ -170,13 +193,15 @@ print(campaign.campaign.title if campaign.campaign else None)
170193
print(campaigns.total_count)
171194
```
172195

196+
Write-операции продвижения, поддерживающие сухой прогон, принимают `dry_run: bool = False`. При `dry_run=True` SDK валидирует параметры, строит тот же payload, что и в реальном вызове, но не выполняет сетевой запрос и возвращает `PromotionActionResult` со статусом `preview`/`validated`.
197+
173198
### Заказы и доставка
174199

175200
```python
176201
from avito import AvitoClient
177202
from avito.orders import OrderLabelsRequest, StockInfoRequest
178203

179-
with AvitoClient() as avito:
204+
with AvitoClient.from_env() as avito:
180205
orders = avito.order().list()
181206
label_task = avito.order_label().create(request=OrderLabelsRequest(order_ids=["100500"]))
182207
label_pdf = avito.order_label(task_id=label_task.task_id).download()
@@ -189,7 +214,7 @@ with AvitoClient() as avito:
189214
from avito import AvitoClient
190215
from avito.jobs import ApplicationIdsQuery, ResumeSearchQuery
191216

192-
with AvitoClient() as avito:
217+
with AvitoClient.from_env() as avito:
193218
vacancies = avito.vacancy().list()
194219
applications = avito.application().list(
195220
query=ApplicationIdsQuery(updated_at_from="2026-04-18")
@@ -204,7 +229,7 @@ with AvitoClient() as avito:
204229
from avito import AvitoClient
205230
from avito.cpa import CpaCallsByTimeRequest
206231

207-
with AvitoClient() as avito:
232+
with AvitoClient.from_env() as avito:
208233
calls = avito.cpa_call().list(
209234
request=CpaCallsByTimeRequest(
210235
date_time_from="2026-04-18T00:00:00Z",
@@ -215,37 +240,13 @@ with AvitoClient() as avito:
215240
records = avito.call_tracking_call(10).download()
216241
```
217242

218-
## Пагинация
219-
220-
Публичные list-операции, которые поддерживают lazy pagination, возвращают обычные SDK-результаты, а поле `items` в них ведет себя как list-like коллекция `PaginatedList`.
221-
222-
Текущий стабильный контракт:
223-
224-
- первая страница загружается сразу, остальные страницы подгружаются только при чтении элементов за ее пределами;
225-
- доступ к уже загруженным элементам не делает повторных запросов;
226-
- частичная итерация и slicing загружают только необходимые страницы;
227-
- явная полная материализация выполняется через `items.materialize()`.
228-
229-
Пример:
230-
231-
```python
232-
from avito import AvitoClient
233-
234-
with AvitoClient() as avito:
235-
result = avito.ad(user_id=123).list(status="active", limit=50)
236-
237-
first = result.items[0]
238-
preview = result.items[:10]
239-
all_items = result.items.materialize()
240-
```
241-
242243
### Автотека
243244

244245
```python
245246
from avito import AvitoClient
246247
from avito.autoteka import CatalogResolveRequest, PreviewReportRequest, VinRequest
247248

248-
with AvitoClient() as avito:
249+
with AvitoClient.from_env() as avito:
249250
catalog = avito.autoteka_vehicle().resolve_catalog(
250251
request=CatalogResolveRequest(brand_id=1)
251252
)
@@ -264,7 +265,7 @@ with AvitoClient() as avito:
264265
from avito import AvitoClient
265266
from avito.realty import RealtyBookingsUpdateRequest, RealtyPricePeriod, RealtyPricesUpdateRequest
266267

267-
with AvitoClient() as avito:
268+
with AvitoClient.from_env() as avito:
268269
booking = avito.realty_booking(20, user_id=10)
269270
booking.update_bookings_info(
270271
request=RealtyBookingsUpdateRequest(blocked_dates=["2026-05-01"])
@@ -279,14 +280,55 @@ with AvitoClient() as avito:
279280
tariff = avito.tariff().get_tariff_info()
280281
```
281282

283+
## Пагинация
284+
285+
Публичные list-операции, которые поддерживают lazy pagination, возвращают обычные SDK-результаты, а поле `items` в них типизировано как `PaginatedList[T]` и ведёт себя как list-like коллекция.
286+
287+
Стабильный публичный контракт:
288+
289+
- первая страница загружается сразу, остальные подгружаются только при чтении элементов за её пределами;
290+
- доступ к уже загруженным элементам не делает повторных запросов;
291+
- частичная итерация и slicing загружают только необходимые страницы;
292+
- пустая коллекция не приводит к дополнительным запросам;
293+
- ошибка на последующей странице поднимается в момент её чтения;
294+
- явная полная материализация выполняется через `items.materialize()` и загружает всё ровно один раз.
295+
296+
Пример:
297+
298+
```python
299+
from avito import AvitoClient
300+
301+
with AvitoClient.from_env() as avito:
302+
result = avito.ad(user_id=123).list(status="active", limit=50)
303+
304+
first = result.items[0]
305+
preview = result.items[:10]
306+
all_items = result.items.materialize()
307+
```
308+
309+
## Ошибки
310+
311+
Все исключения SDK наследуются от `AvitoError` и импортируются из `avito.core.exceptions`. HTTP-коды отображаются в конкретные типы:
312+
313+
- `400`, `422``ValidationError`
314+
- `401``AuthenticationError`
315+
- `403``AuthorizationError`
316+
- `409``ConflictError`
317+
- `429``RateLimitError`
318+
- прочие `5xx` и нераспознанные ответы → `UpstreamApiError`
319+
- транспортные сбои → `TransportError`
320+
- ошибки маппинга ответа → `ResponseMappingError`
321+
322+
`AuthenticationError` (401) и `AuthorizationError` (403) — семантически разные ошибки и **не** состоят в отношении наследования. Тексты сообщений написаны на русском языке. Секреты (access token, `client_secret`, `Authorization`) автоматически санитайзятся из сообщений и metadata.
323+
282324
## Отладка интеграции
283325

284326
SDK не раскрывает сырой transport в основном API, но даёт безопасный debug snapshot без секретов:
285327

286328
```python
287329
from avito import AvitoClient
288330

289-
client = AvitoClient()
331+
client = AvitoClient.from_env()
290332
info = client.debug_info()
291333

292334
print(info.base_url)

0 commit comments

Comments
 (0)