py4writers 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py4writers-0.8.0/PKG-INFO +516 -0
- py4writers-0.8.0/README.md +496 -0
- py4writers-0.8.0/pyproject.toml +22 -0
- py4writers-0.8.0/src/py4writers/__init__.py +31 -0
- py4writers-0.8.0/src/py4writers/api/__init__.py +3 -0
- py4writers-0.8.0/src/py4writers/api/api.py +626 -0
- py4writers-0.8.0/src/py4writers/client/__init__.py +3 -0
- py4writers-0.8.0/src/py4writers/client/aiohttp.py +146 -0
- py4writers-0.8.0/src/py4writers/const.py +30 -0
- py4writers-0.8.0/src/py4writers/exceptions.py +41 -0
- py4writers-0.8.0/src/py4writers/parsers/__init__.py +3 -0
- py4writers-0.8.0/src/py4writers/parsers/order_parser.py +275 -0
- py4writers-0.8.0/src/py4writers/tools/__init__.py +0 -0
- py4writers-0.8.0/src/py4writers/types/__init__.py +3 -0
- py4writers-0.8.0/src/py4writers/types/models.py +38 -0
- py4writers-0.8.0/src/py4writers/utils/__init__.py +4 -0
- py4writers-0.8.0/src/py4writers/utils/rate_limiter.py +37 -0
- py4writers-0.8.0/src/py4writers/utils/retry.py +54 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: py4writers
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: Python library for 4writers.net automation
|
|
5
|
+
Author: socalmy
|
|
6
|
+
Author-email: sergeitoropov2003@gmail.com
|
|
7
|
+
Requires-Python: >=3.12,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Requires-Dist: aiohttp (>=3.11.10,<4.0.0)
|
|
12
|
+
Requires-Dist: bs4 (>=0.0.2,<0.0.3)
|
|
13
|
+
Requires-Dist: build (>=1.2.2.post1,<2.0.0)
|
|
14
|
+
Requires-Dist: certifi (>=2024.8.30,<2025.0.0)
|
|
15
|
+
Requires-Dist: envparse (>=0.2.0,<0.3.0)
|
|
16
|
+
Requires-Dist: lxml (>=5.3.0,<6.0.0)
|
|
17
|
+
Requires-Dist: setuptools (>=75.7.0,<76.0.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# py4writers
|
|
21
|
+
|
|
22
|
+
[](https://badge.fury.io/py/py4writers)
|
|
23
|
+
[](https://www.python.org/downloads/)
|
|
24
|
+
|
|
25
|
+
Python библиотека для автоматизации работы с [4writers.net](https://4writers.net) - асинхронный API для получения заказов, управления файлами и взаимодействия с платформой.
|
|
26
|
+
|
|
27
|
+
## ✨ Основные возможности
|
|
28
|
+
|
|
29
|
+
- 🔐 **Авторизация** - автоматическая авторизация и управление сессией
|
|
30
|
+
- 📋 **Получение заказов** - доступные заказы (free orders) и выполненные (completed orders)
|
|
31
|
+
- 📄 **Работа с файлами** - получение списка файлов и скачивание
|
|
32
|
+
- 🔄 **Retry механизм** - автоматические повторные попытки при ошибках сети
|
|
33
|
+
- ⚡ **Rate limiting** - контроль параллельных запросов (до 10 одновременно)
|
|
34
|
+
- 🎯 **Context manager** - автоматическое управление ресурсами через `async with`
|
|
35
|
+
- 🛡️ **Type hints** - полная типизация для IDE autocomplete
|
|
36
|
+
- 📊 **Детальное логирование** - отслеживание всех операций
|
|
37
|
+
|
|
38
|
+
## 📦 Установка
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install py4writers
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Или через poetry:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
poetry add py4writers
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 🚀 Быстрый старт
|
|
51
|
+
|
|
52
|
+
### Базовый пример
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import asyncio
|
|
56
|
+
from py4writers import API
|
|
57
|
+
|
|
58
|
+
async def main():
|
|
59
|
+
# Используем context manager для автоматического закрытия ресурсов
|
|
60
|
+
async with API(login="your_login", password="your_password") as api:
|
|
61
|
+
# Авторизация
|
|
62
|
+
await api.login()
|
|
63
|
+
|
|
64
|
+
# Получаем доступные заказы
|
|
65
|
+
orders = await api.get_orders(page=1, page_size=50)
|
|
66
|
+
|
|
67
|
+
for order in orders:
|
|
68
|
+
print(f"📦 {order.order_id}: {order.title}")
|
|
69
|
+
print(f"💰 ${order.total:.2f} | ⏰ {order.deadline}")
|
|
70
|
+
if order.description:
|
|
71
|
+
print(f"📝 {order.description[:100]}...")
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
asyncio.run(main())
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Использование с .env файлом
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import asyncio
|
|
81
|
+
from py4writers import API
|
|
82
|
+
from envparse import env
|
|
83
|
+
|
|
84
|
+
# Загружаем переменные окружения
|
|
85
|
+
env.read_envfile(".env")
|
|
86
|
+
LOGIN = env.str("LOGIN")
|
|
87
|
+
PASSWORD = env.str("PASSWORD")
|
|
88
|
+
|
|
89
|
+
async def main():
|
|
90
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
91
|
+
await api.login()
|
|
92
|
+
orders = await api.get_orders()
|
|
93
|
+
print(f"Найдено заказов: {len(orders)}")
|
|
94
|
+
|
|
95
|
+
asyncio.run(main())
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 📚 Примеры использования
|
|
99
|
+
|
|
100
|
+
### 1. Получение доступных заказов
|
|
101
|
+
|
|
102
|
+
#### Вариант A: Получить все заказы сразу (в память)
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
106
|
+
await api.login()
|
|
107
|
+
|
|
108
|
+
# Получить заказы с пагинацией
|
|
109
|
+
orders = await api.get_orders(
|
|
110
|
+
page=1,
|
|
111
|
+
page_size=50,
|
|
112
|
+
category="essay"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
for order in orders:
|
|
116
|
+
print(f"Order #{order.order_id}")
|
|
117
|
+
print(f" Название: {order.title}")
|
|
118
|
+
print(f" Всего: ${order.total:.2f}")
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### Вариант B: Streaming через async generator (рекомендуется)
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
125
|
+
await api.login()
|
|
126
|
+
|
|
127
|
+
# Получаем заказы по одному (не загружает все в память)
|
|
128
|
+
async for order in api.iter_orders(page=1, page_size=50, max_pages=3):
|
|
129
|
+
print(f"📦 Order #{order.order_id}")
|
|
130
|
+
print(f" Название: {order.title}")
|
|
131
|
+
print(f" Тема: {order.subject}")
|
|
132
|
+
print(f" Тип: {order.order_type}")
|
|
133
|
+
print(f" Уровень: {order.academic_level}")
|
|
134
|
+
print(f" Страниц: {order.pages}")
|
|
135
|
+
print(f" Источников: {order.sources}")
|
|
136
|
+
print(f" Стиль: {order.style}")
|
|
137
|
+
print(f" Язык: {order.language}")
|
|
138
|
+
print(f" Дедлайн: {order.deadline}")
|
|
139
|
+
print(f" Осталось времени: {order.remaining}")
|
|
140
|
+
print(f" Зарплата: ${order.salary:.2f}")
|
|
141
|
+
print(f" Бонус: ${order.bonus:.2f}")
|
|
142
|
+
print(f" Всего: ${order.total:.2f}")
|
|
143
|
+
print(f" Описание: {order.description}")
|
|
144
|
+
|
|
145
|
+
# Можно прервать в любой момент
|
|
146
|
+
if order.total > 50.0:
|
|
147
|
+
print("Нашли дорогой заказ, останавливаемся!")
|
|
148
|
+
break
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 2. Получение выполненных заказов
|
|
152
|
+
|
|
153
|
+
#### Вариант A: Получить все заказы сразу
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
157
|
+
await api.login()
|
|
158
|
+
|
|
159
|
+
# Получаем выполненные заказы
|
|
160
|
+
completed = await api.get_completed_orders(page=1)
|
|
161
|
+
|
|
162
|
+
for order in completed:
|
|
163
|
+
print(f"✅ Order #{order.order_id}")
|
|
164
|
+
print(f" Название: {order.title}")
|
|
165
|
+
print(f" Ваша выплата: ${order.your_payment:.2f}")
|
|
166
|
+
print(f" Работа редактора: ${order.editor_work:.2f}")
|
|
167
|
+
print(f" Страниц: {order.pages}")
|
|
168
|
+
print(f" Описание: {order.description}")
|
|
169
|
+
print(f" Файлов: {len(order.files) if order.files else 0}")
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Вариант B: Streaming (рекомендуется)
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
176
|
+
await api.login()
|
|
177
|
+
|
|
178
|
+
# Получаем заказы по одному
|
|
179
|
+
async for order in api.iter_completed_orders():
|
|
180
|
+
print(f"✅ Order #{order.order_id}: {order.title}")
|
|
181
|
+
print(f" 💰 Ваша выплата: ${order.your_payment:.2f}")
|
|
182
|
+
print(f" 📝 Описание: {order.description[:100]}...")
|
|
183
|
+
|
|
184
|
+
# Обрабатываем файлы сразу
|
|
185
|
+
if order.files:
|
|
186
|
+
for file in order.files:
|
|
187
|
+
print(f" 📁 {file.name}")
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 3. Работа с файлами
|
|
191
|
+
|
|
192
|
+
#### Вариант A: Получить все файлы сразу
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
196
|
+
await api.login()
|
|
197
|
+
|
|
198
|
+
# Получаем список файлов заказа
|
|
199
|
+
files = await api.get_order_files(order_index=2569038)
|
|
200
|
+
|
|
201
|
+
for file in files:
|
|
202
|
+
print(f"📁 {file.name}")
|
|
203
|
+
print(f" ID: {file.id}")
|
|
204
|
+
print(f" Автор: {file.author}")
|
|
205
|
+
print(f" Дата: {file.date}")
|
|
206
|
+
|
|
207
|
+
# Скачиваем файл
|
|
208
|
+
file_bytes = await api.download_file(file.id)
|
|
209
|
+
|
|
210
|
+
if file_bytes:
|
|
211
|
+
# Сохраняем на диск
|
|
212
|
+
with open(f"downloaded_{file.name}", "wb") as f:
|
|
213
|
+
f.write(file_bytes)
|
|
214
|
+
print(f" ✅ Скачан: {len(file_bytes):,} байт")
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### Вариант B: Streaming файлов (рекомендуется)
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
221
|
+
await api.login()
|
|
222
|
+
|
|
223
|
+
# Скачиваем файлы по одному (экономит память)
|
|
224
|
+
async for file in api.iter_order_files(order_index=2569038):
|
|
225
|
+
print(f"📁 Скачиваем: {file.name}")
|
|
226
|
+
|
|
227
|
+
# Скачиваем и сразу сохраняем
|
|
228
|
+
file_bytes = await api.download_file(file.id)
|
|
229
|
+
if file_bytes:
|
|
230
|
+
with open(f"downloads/{file.name}", "wb") as f:
|
|
231
|
+
f.write(file_bytes)
|
|
232
|
+
print(f" ✅ Сохранен: {len(file_bytes):,} байт")
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 4. Получение деталей заказа
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
239
|
+
await api.login()
|
|
240
|
+
|
|
241
|
+
# Для обычного заказа
|
|
242
|
+
description = await api.fetch_order_details(
|
|
243
|
+
order_index=2569038,
|
|
244
|
+
is_completed=False
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# Для выполненного заказа
|
|
248
|
+
description = await api.fetch_order_details(
|
|
249
|
+
order_index=2569038,
|
|
250
|
+
is_completed=True
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
print(f"Описание: {description}")
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### 5. Взятие заказа в работу
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
260
|
+
await api.login()
|
|
261
|
+
|
|
262
|
+
# Берем заказ по индексу
|
|
263
|
+
success = await api.take_order(order_index=2569038)
|
|
264
|
+
|
|
265
|
+
if success:
|
|
266
|
+
print("✅ Заказ успешно взят!")
|
|
267
|
+
else:
|
|
268
|
+
print("❌ Не удалось взять заказ")
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## 📊 Модели данных
|
|
272
|
+
|
|
273
|
+
### Order
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
@dataclass
|
|
277
|
+
class Order:
|
|
278
|
+
order_id: str # ID заказа
|
|
279
|
+
order_index: int # Индекс заказа (для запросов)
|
|
280
|
+
title: str # Название
|
|
281
|
+
subject: str # Тема
|
|
282
|
+
deadline: str # Дедлайн
|
|
283
|
+
remaining: str # Оставшееся время
|
|
284
|
+
order_type: str # Тип работы (Essay, Research Paper, etc.)
|
|
285
|
+
academic_level: str # Академический уровень
|
|
286
|
+
style: str # Стиль оформления (APA, MLA, etc.)
|
|
287
|
+
language: str # Язык
|
|
288
|
+
pages: Optional[int] # Количество страниц
|
|
289
|
+
sources: Optional[int] # Количество источников
|
|
290
|
+
salary: Optional[float] # Зарплата
|
|
291
|
+
bonus: Optional[float] # Бонус
|
|
292
|
+
total: Optional[float] # Итого
|
|
293
|
+
description: Optional[str] # Описание заказа
|
|
294
|
+
files: Optional[List[File]] # Файлы заказа
|
|
295
|
+
editor_work: Optional[float] # Работа редактора (completed)
|
|
296
|
+
your_payment: Optional[float] # Ваша выплата (completed)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### File
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
@dataclass
|
|
303
|
+
class File:
|
|
304
|
+
id: int # ID файла
|
|
305
|
+
name: str # Название файла
|
|
306
|
+
author: str # Автор загрузки
|
|
307
|
+
date: str # Дата загрузки
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## ⚙️ Конфигурация
|
|
311
|
+
|
|
312
|
+
### Создание API клиента
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
from py4writers import API
|
|
316
|
+
|
|
317
|
+
# С credentials
|
|
318
|
+
api = API(login="user", password="pass")
|
|
319
|
+
|
|
320
|
+
# С кастомным HTTP клиентом
|
|
321
|
+
from py4writers.client.aiohttp import AiohttpClient
|
|
322
|
+
|
|
323
|
+
http_client = AiohttpClient(timeout=60)
|
|
324
|
+
api = API(
|
|
325
|
+
login="user",
|
|
326
|
+
password="pass",
|
|
327
|
+
http_client=http_client
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
# Context manager (рекомендуется)
|
|
331
|
+
async with API(login="user", password="pass") as api:
|
|
332
|
+
await api.login()
|
|
333
|
+
# ваш код
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Настройка логирования
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
import logging
|
|
340
|
+
|
|
341
|
+
# Базовая настройка
|
|
342
|
+
logging.basicConfig(
|
|
343
|
+
level=logging.INFO,
|
|
344
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# Детальное логирование
|
|
348
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## 🛡️ Обработка ошибок
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
from py4writers import API
|
|
355
|
+
from py4writers.exceptions import (
|
|
356
|
+
AuthenticationError,
|
|
357
|
+
SessionExpiredError,
|
|
358
|
+
NetworkError,
|
|
359
|
+
ParsingError
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
363
|
+
try:
|
|
364
|
+
await api.login()
|
|
365
|
+
orders = await api.get_orders()
|
|
366
|
+
|
|
367
|
+
except AuthenticationError as e:
|
|
368
|
+
print(f"❌ Ошибка авторизации: {e}")
|
|
369
|
+
|
|
370
|
+
except SessionExpiredError as e:
|
|
371
|
+
print(f"⏰ Сессия истекла: {e}")
|
|
372
|
+
# API автоматически попытается переавторизоваться
|
|
373
|
+
|
|
374
|
+
except NetworkError as e:
|
|
375
|
+
print(f"🌐 Сетевая ошибка: {e}")
|
|
376
|
+
# Retry декоратор автоматически повторит запрос
|
|
377
|
+
|
|
378
|
+
except ParsingError as e:
|
|
379
|
+
print(f"📄 Ошибка парсинга: {e}")
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## 🔧 Продвинутые возможности
|
|
383
|
+
|
|
384
|
+
### Async Generators (Streaming)
|
|
385
|
+
|
|
386
|
+
**Преимущества использования `iter_*` методов:**
|
|
387
|
+
|
|
388
|
+
1. 🚀 **Меньше памяти** - заказы не загружаются все сразу
|
|
389
|
+
2. ⚡ **Быстрее начало** - первый заказ доступен моментально
|
|
390
|
+
3. 🔄 **Прерывание** - можно остановить в любой момент через `break`
|
|
391
|
+
4. 📊 **Пагинация** - автоматически обходит все страницы
|
|
392
|
+
|
|
393
|
+
```python
|
|
394
|
+
async with API(login=LOGIN, password=PASSWORD) as api:
|
|
395
|
+
await api.login()
|
|
396
|
+
|
|
397
|
+
# Обрабатываем первые 100 заказов из всех страниц
|
|
398
|
+
count = 0
|
|
399
|
+
async for order in api.iter_orders(max_pages=None): # None = все страницы
|
|
400
|
+
print(f"{order.order_id}: ${order.total}")
|
|
401
|
+
|
|
402
|
+
count += 1
|
|
403
|
+
if count >= 100:
|
|
404
|
+
break # Останавливаемся после 100 заказов
|
|
405
|
+
|
|
406
|
+
# Скачиваем все файлы выполненного заказа
|
|
407
|
+
async for order in api.iter_completed_orders():
|
|
408
|
+
print(f"\n📦 {order.title}")
|
|
409
|
+
|
|
410
|
+
async for file in api.iter_order_files(order.order_index):
|
|
411
|
+
print(f" 📁 {file.name}")
|
|
412
|
+
file_bytes = await api.download_file(file.id)
|
|
413
|
+
# Сразу обрабатываем без хранения в памяти
|
|
414
|
+
process_file(file.name, file_bytes)
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Rate Limiting
|
|
418
|
+
|
|
419
|
+
API автоматически ограничивает количество параллельных запросов (по умолчанию 10):
|
|
420
|
+
|
|
421
|
+
```python
|
|
422
|
+
from py4writers.utils.rate_limiter import RateLimiter
|
|
423
|
+
|
|
424
|
+
# Кастомный лимит
|
|
425
|
+
rate_limiter = RateLimiter(max_concurrent=5)
|
|
426
|
+
api = API(
|
|
427
|
+
login=LOGIN,
|
|
428
|
+
password=PASSWORD,
|
|
429
|
+
rate_limiter=rate_limiter
|
|
430
|
+
)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Retry механизм
|
|
434
|
+
|
|
435
|
+
Используйте декоратор для автоматических повторных попыток:
|
|
436
|
+
|
|
437
|
+
```python
|
|
438
|
+
from py4writers.utils.retry import async_retry
|
|
439
|
+
from py4writers.exceptions import NetworkError
|
|
440
|
+
|
|
441
|
+
@async_retry(
|
|
442
|
+
max_attempts=3,
|
|
443
|
+
delay=1.0,
|
|
444
|
+
backoff=2.0,
|
|
445
|
+
exceptions=(NetworkError,)
|
|
446
|
+
)
|
|
447
|
+
async def my_function():
|
|
448
|
+
# ваш код
|
|
449
|
+
pass
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## 📝 Требования
|
|
453
|
+
|
|
454
|
+
- Python 3.12+
|
|
455
|
+
- aiohttp
|
|
456
|
+
- beautifulsoup4
|
|
457
|
+
- lxml
|
|
458
|
+
- certifi
|
|
459
|
+
|
|
460
|
+
## 🔄 Changelog
|
|
461
|
+
|
|
462
|
+
### [0.8.0] - 2026-01-17
|
|
463
|
+
|
|
464
|
+
#### ✨ Added
|
|
465
|
+
- **Async Generators**: Новые методы для streaming
|
|
466
|
+
- `iter_orders()` - получение заказов по одному с автоматической пагинацией
|
|
467
|
+
- `iter_completed_orders()` - получение выполненных заказов по одному
|
|
468
|
+
- `iter_order_files()` - получение файлов по одному
|
|
469
|
+
- **Преимущества**: меньше памяти, быстрее начало, можно прервать через `break`
|
|
470
|
+
|
|
471
|
+
### [0.7.0] - 2026-01-17
|
|
472
|
+
|
|
473
|
+
#### 🔄 Breaking Changes
|
|
474
|
+
- Переименован модуль `fourwritersapi` → `py4writers`
|
|
475
|
+
|
|
476
|
+
### [0.6.1] - 2026-01-17
|
|
477
|
+
|
|
478
|
+
#### 🔧 Fixed
|
|
479
|
+
- Исправлен парсинг описаний для completed orders
|
|
480
|
+
- Добавлен отдельный endpoint для completed order details
|
|
481
|
+
|
|
482
|
+
### [0.6.0] - 2026-01-17
|
|
483
|
+
|
|
484
|
+
#### ✨ Added
|
|
485
|
+
- Context Manager Support
|
|
486
|
+
- Custom Exceptions
|
|
487
|
+
- Retry Logic
|
|
488
|
+
- Rate Limiting
|
|
489
|
+
- OrderParser Class
|
|
490
|
+
- Pagination Support
|
|
491
|
+
- Better Logging
|
|
492
|
+
|
|
493
|
+
#### 🔧 Fixed
|
|
494
|
+
- Resource Leak в AiohttpClient
|
|
495
|
+
- Session Expiration handling
|
|
496
|
+
- BeautifulSoup warnings
|
|
497
|
+
- Code duplication (200+ строк)
|
|
498
|
+
|
|
499
|
+
## 📄 Лицензия
|
|
500
|
+
|
|
501
|
+
MIT License
|
|
502
|
+
|
|
503
|
+
## 🤝 Контрибьюция
|
|
504
|
+
|
|
505
|
+
Pull requests приветствуются! Для больших изменений сначала откройте issue для обсуждения.
|
|
506
|
+
|
|
507
|
+
## 📧 Контакты
|
|
508
|
+
|
|
509
|
+
- Author: socalmy
|
|
510
|
+
- Email: sergeitoropov2003@gmail.com
|
|
511
|
+
- PyPI: https://pypi.org/project/py4writers/
|
|
512
|
+
|
|
513
|
+
## ⭐ Поддержка
|
|
514
|
+
|
|
515
|
+
Если библиотека была полезна, поставьте звезду на GitHub!
|
|
516
|
+
|