REST2JSON 0.1.3__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.
@@ -0,0 +1 @@
1
+ recursive-include src/REST2JSON *.py
@@ -0,0 +1,348 @@
1
+ Metadata-Version: 2.4
2
+ Name: REST2JSON
3
+ Version: 0.1.3
4
+ Summary: metadriven-адаптер для RESTAPI запросов
5
+ Home-page: https://github.com/Siinthd/REST2JSON
6
+ Author: Denis Kodolich
7
+ Author-email: d.kodolich@concept-software.ru
8
+ Project-URL: Bug Reports, https://github.com/yourusername/REST2JSON/issues
9
+ Project-URL: Source, https://github.com/yourusername/REST2JSON
10
+ Project-URL: Documentation, https://REST2JSON.readthedocs.io
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Operating System :: OS Independent
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: httpx>=0.24.0
24
+ Requires-Dist: omegaconf>=2.3.0
25
+ Requires-Dist: pyyaml>=6.0
26
+ Requires-Dist: requests>=2.28.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
29
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
30
+ Requires-Dist: black>=23.0.0; extra == "dev"
31
+ Requires-Dist: isort>=5.0.0; extra == "dev"
32
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
33
+ Provides-Extra: docs
34
+ Requires-Dist: sphinx>=6.0.0; extra == "docs"
35
+ Requires-Dist: sphinx-rtd-theme>=1.0.0; extra == "docs"
36
+ Dynamic: author
37
+ Dynamic: author-email
38
+ Dynamic: classifier
39
+ Dynamic: description
40
+ Dynamic: description-content-type
41
+ Dynamic: home-page
42
+ Dynamic: project-url
43
+ Dynamic: provides-extra
44
+ Dynamic: requires-dist
45
+ Dynamic: requires-python
46
+ Dynamic: summary
47
+
48
+ # REST2JSON
49
+
50
+
51
+ Конфигурируемый адаптер, способный выполнять чтение данных с использованием внешнего REST-сервиса без написания клиента под каждый конкретный API на основании описания метаданных этого сервиса.
52
+
53
+
54
+
55
+ ## Особенности
56
+
57
+ - Гибкая конфигурация через OmegaConf
58
+ - Загрузка OpenAPI спецификаций из файлов (YAML/JSON) или по URL
59
+ - Пакетная обработка запросов с подготовкой payload(Возможно,придется отказаться в пользу явного построения запроса)
60
+ - Поддержка контекстного менеджера для безопасного управления ресурсами
61
+ - Генерация JSON Schema из OpenAPI спецификации
62
+
63
+
64
+ ## Установка
65
+
66
+ ```bash
67
+ pip install git+https://github.com/Siinthd/Rest2JSON.git
68
+ pip install REST2JSON --index-url {mirror}
69
+ ```
70
+
71
+
72
+ ## Быстрый старт
73
+
74
+
75
+ ### Простой запрос
76
+ ```Python
77
+ from rest2json import REST2JSON
78
+
79
+ config_file = 'C:/Users/kdenis/Documents/Work/configs/config_WorldBank.yaml'
80
+
81
+ import yaml
82
+
83
+ with open(config_file) as stream:
84
+ try:
85
+ config = yaml.safe_load(stream)
86
+ except yaml.YAMLError as exc:
87
+ print(exc)
88
+
89
+ #Иницализация адаптера, в этот момент происходит чтение/конфигурации,скачивание спецификации и ее парсинг
90
+ adapter = REST2JSON(config)
91
+ # get_schema() возвращает схему данных в <class 'dict'> - формате
92
+ # по умолчанию (raw = False) вернет Spark dataframe ddl
93
+ # (raw = True) возвращает структуру ответа без обработки (не подходит,чтобы создать dataframe)
94
+ schema = adapter.get_schema(raw = False)
95
+ # get_data() возвращает данные ответов с сервера в формате [<class 'dict'>]
96
+ # по умолчанию (пустые скобки), payload берется из конфигурации
97
+ data = adapter.get_data()
98
+ # При наличии payload (get_data(payload)) у сервера запрашиваются конкретные в payload данные.
99
+ payload = [{"query": 123}, {"query": 456}, {"query": 789}]
100
+ results = adapter.get_response(payload)
101
+
102
+ ```
103
+
104
+
105
+ ### Использование с контекстным менеджером
106
+ ```Python
107
+ # возможность докачки данных вне конфигурации
108
+ with REST2JSON(config) as adapter:
109
+ # Работа с адаптером
110
+     response = adapter.get_data({})
111
+     print(response)
112
+     # Автоматическое закрытие соединений
113
+ ```
114
+
115
+ ### Пакетная обработка
116
+ ```Python
117
+ payload = [{"query": 123}, {"query": 456}, {"query": 789}] # API-сервис ожидает параметр c именем query
118
+
119
+ with REST2JSON(config) as adapter:
120
+     results = adapter.get_response(payload)
121
+     for result in results:
122
+         print(result)
123
+
124
+ #или
125
+ adapter = REST2JSON(config)
126
+ response = adapter.get_response(payload)
127
+ for result in response:
128
+         print(result)
129
+ ```
130
+
131
+
132
+
133
+ ## Конфигурация
134
+
135
+ ### Структура конфигурации
136
+ ```yaml
137
+ # обязательный: параметры процесса
138
+ # (основные настройки)
139
+ proc:
140
+ # обязательный: конфиг источника
141
+
142
+ src:
143
+ # обязательный: наименование источника
144
+ name: "getEverything" #
145
+ # обязательный: тип подключения
146
+ # определяет, как мы читаем источник
147
+ conn_type: 'rest2json'
148
+ # обязательный: список параметров подключения
149
+ conn_params:
150
+ # опциональный: количество ретраев и таймаут
151
+ # если не указать -- 1 ретрай и какой-нибудь таймаут
152
+ retries: 3
153
+ timeout: 30
154
+
155
+ # обязательный: спецификация сервиса
156
+ # (хотя бы один из двух должен быть указан и заполнен)
157
+ # отсюда берём схемы реплаев,
158
+ # + url для запроса, если возможно
159
+
160
+ spec_url: 'https://dadata.ru/files/openapi/suggestions.yml'
161
+ spec_data:
162
+ # опциональный: адрес для запроса
163
+
164
+ base_url: "https://suggestions.dadata.ru/suggestions"
165
+ # опциональный: ep+method для случаев, когда сервис не использует operation_id
166
+ endpoint_url: "/api/4_1/rs/suggest/bank"
167
+ method: 'post'
168
+ # опциональный: перебор страниц на сервере
169
+ # игнорим, если параметра нет,
170
+ pagination:
171
+ # обязательный: включение
172
+ enabled: false
173
+ # обязательный: название параметра с номером страницы
174
+ page_param: 'page'
175
+ # обязательный: название параметра с размером страницы
176
+ pagesize_param: 'per_page'
177
+ # обязательный: запрашиваемый размер страницы
178
+ pagesize_val: 100
179
+ # обязательный: название параметра с общим числом записей
180
+ pagecnt_param: "total_results"
181
+
182
+ # обязательный: конфиг данных (схема, фильтры, etc)
183
+ data:
184
+ # TODO: использовать динамическую генерацию запросов по спеке,
185
+ # и как-то угадывать, куда какие параметры писать -- нецелесообразно
186
+ payload:
187
+ ['SABRRUMM', 'VTBRRUMM',]
188
+ # опциональный: свой маппинг типов
189
+ json_mapping_override:
190
+ "null": "null"
191
+
192
+ # обязательный: данные для авторизации
193
+ # содержат только логины, токены, пароли
194
+ auth:
195
+ # источник (extract)
196
+ src:
197
+ header: # авторизация через хедер (как в dadata)
198
+ "Authorization": "Token "
199
+ "X-Secret": ""
200
+ #X-Secret: "64545645"
201
+ body: # авторизация через параметр в теле (как в random.org)
202
+ #- "API_KEY: 12434547985675"
203
+ env:
204
+ # опциональный: маппинг типов данных (при конвертация в StrucType-json)
205
+ json:
206
+ type_mapping:
207
+ int32: integer
208
+ int64: long
209
+ float: float
210
+ double: double
211
+ date: string
212
+ date-time: string
213
+ binary: binary
214
+ ```
215
+
216
+
217
+ ## Class Reference
218
+
219
+ ### Класс `REST2JSON`
220
+
221
+ Основной класс для работы с API.
222
+
223
+ #### Методы
224
+
225
+ ##### `get_data(data=None)`
226
+
227
+ Основной метод для выполнения запросов. Автоматически управляет контекстом.
228
+
229
+ **Параметры:**
230
+
231
+ - `data` - Данные для запроса. Может быть:
232
+
233
+ - `None` - запрос без параметров,в таком случае данные для запроса берутся из конфигурации (раздел proc.src.data.payload)
234
+ - `dict` - одиночный запрос (может быть пустым - {})
235
+ - `list[dict]` - пакет запросов (может быть пустым - [])
236
+ - `str/int` - одиночное значение (будет преобразовано в параметр required)(может быть пустым - '')
237
+
238
+
239
+ **Возвращает:** JSON ответ от API или список ответов при пакетной обработке.
240
+
241
+ ##### `get_schema(raw = False)`
242
+
243
+ **Возвращает:**  схема структуры данных из OpenAPI спецификации при raw = False возвращает схему в Spark-формате.
244
+
245
+
246
+ ## Обработка payload
247
+
248
+ Для получения ответа от API сервиса необходимо передать в запрос параметры, которые он ожидает — обычно это идентификаторы, фильтры или данные для создания/обновления объектов.
249
+ Они могут быть переданы как часть URL (например, /users/123), в строке запроса (?page=2) или в теле запроса (JSON с полями).
250
+
251
+ Такие параметры указаны в разделе "requestBody". Часто, это один required-параметр и можно передать в REST2JSON список значения без указания имени параметра - Сервис сам подставить имя параметра.
252
+
253
+ В противном случае, требуется указать все параметры явно:
254
+
255
+ ```python
256
+ data = {"query": 123,"status":["ACTIVE"],"type":["BANK","BANK_BRANCH","OTHER"]}
257
+ ```
258
+
259
+ #### Пример requestBody в спецификации
260
+
261
+ ```yaml
262
+ requestBody:
263
+     content:
264
+        application/json:
265
+          schema:
266
+ required:
267
+ - query
268
+ type: object
269
+ properties:
270
+ count:
271
+ type: integer
272
+ format: int32
273
+ nullable: true
274
+ default: 10
275
+ locations:
276
+ type: array
277
+ nullable: true
278
+ items:
279
+ $ref: "#/components/schemas/LocationCode"
280
+ locations_boost:
281
+ type: array
282
+ nullable: true
283
+ items:
284
+ $ref: "#/components/schemas/LocationCode"
285
+ query:
286
+ type: string
287
+ status:
288
+ type: array
289
+ nullable: true
290
+ items:
291
+ type: string
292
+ enum:
293
+ - ACTIVE
294
+ - LIQUIDATING
295
+ - LIQUIDATED
296
+ - REORGANIZING
297
+ - BANKRUPT
298
+ type:
299
+ type: array
300
+ nullable: true
301
+ items:
302
+ type: string
303
+ enum:
304
+ - BANK
305
+ - NKO
306
+ - BANK_BRANCH
307
+ - NKO_BRANCH
308
+ - RKC
309
+ - CBR
310
+ - TREASURY
311
+ - OTHER
312
+ ```
313
+
314
+
315
+ | Тип входных данных | Результат |
316
+ | -------------------- | -------------------------------------------------- |
317
+ | `{}`,`[]`,`''` | `[dict]` - одиночный запрос(сервер не ждет данных) |
318
+ | `dict` | `[dict]` - одиночный запрос |
319
+ | `list[dict]` | `list[dict]` - пакет запросов |
320
+ | `list` (не словарей) | Если required имеет n параметров: `[{query: value_1}...{query: value_n}]` |
321
+ | Одиночное значение | Если required имеет 1 параметр: `[{query: value}]` |
322
+
323
+ ### Примеры преобразования данных
324
+
325
+ ```python
326
+ # Одиночный словарь
327
+ data = {"query": 123}
328
+ # → [{"query": 123}]
329
+ # Список словарей
330
+ data = [{"query": 123}, {"query": 456}]
331
+ # → [{"query": 123}, {"query": 456}]
332
+ # Список значений (если required = ["id"])
333
+ data = [123, 456, 789]
334
+ # [{"query": 123}, {"query": 456}, {"query": 789}]
335
+ # Одиночное значение (если required = ["query"])
336
+ data = 123
337
+ # [{"query": 123}]
338
+ data = {"query": 123,"status":["ACTIVE"],"type":["BANK","BANK_BRANCH","OTHER"]}
339
+ #Явное указание дополнительных фильтров
340
+ ```
341
+
342
+ ### TODO
343
+
344
+ - [ ] Проверка наличия ключей словаря в спецификации
345
+ - [ ] Формирование очереди загрузок
346
+ - [ ] Реализация пагинации
347
+ - [ ] Улучшенная валидация ответов
348
+ - [ ] Потокобезопасность
@@ -0,0 +1,301 @@
1
+ # REST2JSON
2
+
3
+
4
+ Конфигурируемый адаптер, способный выполнять чтение данных с использованием внешнего REST-сервиса без написания клиента под каждый конкретный API на основании описания метаданных этого сервиса.
5
+
6
+
7
+
8
+ ## Особенности
9
+
10
+ - Гибкая конфигурация через OmegaConf
11
+ - Загрузка OpenAPI спецификаций из файлов (YAML/JSON) или по URL
12
+ - Пакетная обработка запросов с подготовкой payload(Возможно,придется отказаться в пользу явного построения запроса)
13
+ - Поддержка контекстного менеджера для безопасного управления ресурсами
14
+ - Генерация JSON Schema из OpenAPI спецификации
15
+
16
+
17
+ ## Установка
18
+
19
+ ```bash
20
+ pip install git+https://github.com/Siinthd/Rest2JSON.git
21
+ pip install REST2JSON --index-url {mirror}
22
+ ```
23
+
24
+
25
+ ## Быстрый старт
26
+
27
+
28
+ ### Простой запрос
29
+ ```Python
30
+ from rest2json import REST2JSON
31
+
32
+ config_file = 'C:/Users/kdenis/Documents/Work/configs/config_WorldBank.yaml'
33
+
34
+ import yaml
35
+
36
+ with open(config_file) as stream:
37
+ try:
38
+ config = yaml.safe_load(stream)
39
+ except yaml.YAMLError as exc:
40
+ print(exc)
41
+
42
+ #Иницализация адаптера, в этот момент происходит чтение/конфигурации,скачивание спецификации и ее парсинг
43
+ adapter = REST2JSON(config)
44
+ # get_schema() возвращает схему данных в <class 'dict'> - формате
45
+ # по умолчанию (raw = False) вернет Spark dataframe ddl
46
+ # (raw = True) возвращает структуру ответа без обработки (не подходит,чтобы создать dataframe)
47
+ schema = adapter.get_schema(raw = False)
48
+ # get_data() возвращает данные ответов с сервера в формате [<class 'dict'>]
49
+ # по умолчанию (пустые скобки), payload берется из конфигурации
50
+ data = adapter.get_data()
51
+ # При наличии payload (get_data(payload)) у сервера запрашиваются конкретные в payload данные.
52
+ payload = [{"query": 123}, {"query": 456}, {"query": 789}]
53
+ results = adapter.get_response(payload)
54
+
55
+ ```
56
+
57
+
58
+ ### Использование с контекстным менеджером
59
+ ```Python
60
+ # возможность докачки данных вне конфигурации
61
+ with REST2JSON(config) as adapter:
62
+ # Работа с адаптером
63
+     response = adapter.get_data({})
64
+     print(response)
65
+     # Автоматическое закрытие соединений
66
+ ```
67
+
68
+ ### Пакетная обработка
69
+ ```Python
70
+ payload = [{"query": 123}, {"query": 456}, {"query": 789}] # API-сервис ожидает параметр c именем query
71
+
72
+ with REST2JSON(config) as adapter:
73
+     results = adapter.get_response(payload)
74
+     for result in results:
75
+         print(result)
76
+
77
+ #или
78
+ adapter = REST2JSON(config)
79
+ response = adapter.get_response(payload)
80
+ for result in response:
81
+         print(result)
82
+ ```
83
+
84
+
85
+
86
+ ## Конфигурация
87
+
88
+ ### Структура конфигурации
89
+ ```yaml
90
+ # обязательный: параметры процесса
91
+ # (основные настройки)
92
+ proc:
93
+ # обязательный: конфиг источника
94
+
95
+ src:
96
+ # обязательный: наименование источника
97
+ name: "getEverything" #
98
+ # обязательный: тип подключения
99
+ # определяет, как мы читаем источник
100
+ conn_type: 'rest2json'
101
+ # обязательный: список параметров подключения
102
+ conn_params:
103
+ # опциональный: количество ретраев и таймаут
104
+ # если не указать -- 1 ретрай и какой-нибудь таймаут
105
+ retries: 3
106
+ timeout: 30
107
+
108
+ # обязательный: спецификация сервиса
109
+ # (хотя бы один из двух должен быть указан и заполнен)
110
+ # отсюда берём схемы реплаев,
111
+ # + url для запроса, если возможно
112
+
113
+ spec_url: 'https://dadata.ru/files/openapi/suggestions.yml'
114
+ spec_data:
115
+ # опциональный: адрес для запроса
116
+
117
+ base_url: "https://suggestions.dadata.ru/suggestions"
118
+ # опциональный: ep+method для случаев, когда сервис не использует operation_id
119
+ endpoint_url: "/api/4_1/rs/suggest/bank"
120
+ method: 'post'
121
+ # опциональный: перебор страниц на сервере
122
+ # игнорим, если параметра нет,
123
+ pagination:
124
+ # обязательный: включение
125
+ enabled: false
126
+ # обязательный: название параметра с номером страницы
127
+ page_param: 'page'
128
+ # обязательный: название параметра с размером страницы
129
+ pagesize_param: 'per_page'
130
+ # обязательный: запрашиваемый размер страницы
131
+ pagesize_val: 100
132
+ # обязательный: название параметра с общим числом записей
133
+ pagecnt_param: "total_results"
134
+
135
+ # обязательный: конфиг данных (схема, фильтры, etc)
136
+ data:
137
+ # TODO: использовать динамическую генерацию запросов по спеке,
138
+ # и как-то угадывать, куда какие параметры писать -- нецелесообразно
139
+ payload:
140
+ ['SABRRUMM', 'VTBRRUMM',]
141
+ # опциональный: свой маппинг типов
142
+ json_mapping_override:
143
+ "null": "null"
144
+
145
+ # обязательный: данные для авторизации
146
+ # содержат только логины, токены, пароли
147
+ auth:
148
+ # источник (extract)
149
+ src:
150
+ header: # авторизация через хедер (как в dadata)
151
+ "Authorization": "Token "
152
+ "X-Secret": ""
153
+ #X-Secret: "64545645"
154
+ body: # авторизация через параметр в теле (как в random.org)
155
+ #- "API_KEY: 12434547985675"
156
+ env:
157
+ # опциональный: маппинг типов данных (при конвертация в StrucType-json)
158
+ json:
159
+ type_mapping:
160
+ int32: integer
161
+ int64: long
162
+ float: float
163
+ double: double
164
+ date: string
165
+ date-time: string
166
+ binary: binary
167
+ ```
168
+
169
+
170
+ ## Class Reference
171
+
172
+ ### Класс `REST2JSON`
173
+
174
+ Основной класс для работы с API.
175
+
176
+ #### Методы
177
+
178
+ ##### `get_data(data=None)`
179
+
180
+ Основной метод для выполнения запросов. Автоматически управляет контекстом.
181
+
182
+ **Параметры:**
183
+
184
+ - `data` - Данные для запроса. Может быть:
185
+
186
+ - `None` - запрос без параметров,в таком случае данные для запроса берутся из конфигурации (раздел proc.src.data.payload)
187
+ - `dict` - одиночный запрос (может быть пустым - {})
188
+ - `list[dict]` - пакет запросов (может быть пустым - [])
189
+ - `str/int` - одиночное значение (будет преобразовано в параметр required)(может быть пустым - '')
190
+
191
+
192
+ **Возвращает:** JSON ответ от API или список ответов при пакетной обработке.
193
+
194
+ ##### `get_schema(raw = False)`
195
+
196
+ **Возвращает:**  схема структуры данных из OpenAPI спецификации при raw = False возвращает схему в Spark-формате.
197
+
198
+
199
+ ## Обработка payload
200
+
201
+ Для получения ответа от API сервиса необходимо передать в запрос параметры, которые он ожидает — обычно это идентификаторы, фильтры или данные для создания/обновления объектов.
202
+ Они могут быть переданы как часть URL (например, /users/123), в строке запроса (?page=2) или в теле запроса (JSON с полями).
203
+
204
+ Такие параметры указаны в разделе "requestBody". Часто, это один required-параметр и можно передать в REST2JSON список значения без указания имени параметра - Сервис сам подставить имя параметра.
205
+
206
+ В противном случае, требуется указать все параметры явно:
207
+
208
+ ```python
209
+ data = {"query": 123,"status":["ACTIVE"],"type":["BANK","BANK_BRANCH","OTHER"]}
210
+ ```
211
+
212
+ #### Пример requestBody в спецификации
213
+
214
+ ```yaml
215
+ requestBody:
216
+     content:
217
+        application/json:
218
+          schema:
219
+ required:
220
+ - query
221
+ type: object
222
+ properties:
223
+ count:
224
+ type: integer
225
+ format: int32
226
+ nullable: true
227
+ default: 10
228
+ locations:
229
+ type: array
230
+ nullable: true
231
+ items:
232
+ $ref: "#/components/schemas/LocationCode"
233
+ locations_boost:
234
+ type: array
235
+ nullable: true
236
+ items:
237
+ $ref: "#/components/schemas/LocationCode"
238
+ query:
239
+ type: string
240
+ status:
241
+ type: array
242
+ nullable: true
243
+ items:
244
+ type: string
245
+ enum:
246
+ - ACTIVE
247
+ - LIQUIDATING
248
+ - LIQUIDATED
249
+ - REORGANIZING
250
+ - BANKRUPT
251
+ type:
252
+ type: array
253
+ nullable: true
254
+ items:
255
+ type: string
256
+ enum:
257
+ - BANK
258
+ - NKO
259
+ - BANK_BRANCH
260
+ - NKO_BRANCH
261
+ - RKC
262
+ - CBR
263
+ - TREASURY
264
+ - OTHER
265
+ ```
266
+
267
+
268
+ | Тип входных данных | Результат |
269
+ | -------------------- | -------------------------------------------------- |
270
+ | `{}`,`[]`,`''` | `[dict]` - одиночный запрос(сервер не ждет данных) |
271
+ | `dict` | `[dict]` - одиночный запрос |
272
+ | `list[dict]` | `list[dict]` - пакет запросов |
273
+ | `list` (не словарей) | Если required имеет n параметров: `[{query: value_1}...{query: value_n}]` |
274
+ | Одиночное значение | Если required имеет 1 параметр: `[{query: value}]` |
275
+
276
+ ### Примеры преобразования данных
277
+
278
+ ```python
279
+ # Одиночный словарь
280
+ data = {"query": 123}
281
+ # → [{"query": 123}]
282
+ # Список словарей
283
+ data = [{"query": 123}, {"query": 456}]
284
+ # → [{"query": 123}, {"query": 456}]
285
+ # Список значений (если required = ["id"])
286
+ data = [123, 456, 789]
287
+ # [{"query": 123}, {"query": 456}, {"query": 789}]
288
+ # Одиночное значение (если required = ["query"])
289
+ data = 123
290
+ # [{"query": 123}]
291
+ data = {"query": 123,"status":["ACTIVE"],"type":["BANK","BANK_BRANCH","OTHER"]}
292
+ #Явное указание дополнительных фильтров
293
+ ```
294
+
295
+ ### TODO
296
+
297
+ - [ ] Проверка наличия ключей словаря в спецификации
298
+ - [ ] Формирование очереди загрузок
299
+ - [ ] Реализация пагинации
300
+ - [ ] Улучшенная валидация ответов
301
+ - [ ] Потокобезопасность
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+