libentry 1.21__py3-none-any.whl → 1.21.1__py3-none-any.whl
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.
- libentry/__init__.py +0 -1
- libentry/api.py +362 -69
- libentry/test_api.py +2 -2
- {libentry-1.21.dist-info → libentry-1.21.1.dist-info}/METADATA +8 -16
- {libentry-1.21.dist-info → libentry-1.21.1.dist-info}/RECORD +10 -11
- {libentry-1.21.dist-info → libentry-1.21.1.dist-info}/WHEEL +1 -1
- {libentry-1.21.dist-info → libentry-1.21.1.dist-info}/entry_points.txt +1 -0
- libentry/server.py +0 -53
- {libentry-1.21.dist-info → libentry-1.21.1.dist-info}/LICENSE +0 -0
- {libentry-1.21.dist-info → libentry-1.21.1.dist-info}/top_level.txt +0 -0
- {libentry-1.21.dist-info → libentry-1.21.1.dist-info}/zip-safe +0 -0
libentry/__init__.py
CHANGED
libentry/api.py
CHANGED
@@ -12,9 +12,10 @@ __all__ = [
|
|
12
12
|
|
13
13
|
from dataclasses import dataclass, field
|
14
14
|
from time import sleep
|
15
|
-
from typing import Any, Callable, Iterable, List, Literal, Mapping, Optional, Tuple
|
15
|
+
from typing import Any, AsyncIterable, Callable, Iterable, List, Literal, Mapping, Optional, Tuple, Union
|
16
16
|
from urllib.parse import urljoin
|
17
17
|
|
18
|
+
import httpx
|
18
19
|
from urllib3 import PoolManager
|
19
20
|
from urllib3.exceptions import HTTPError, TimeoutError
|
20
21
|
|
@@ -170,7 +171,7 @@ class ServiceError(RuntimeError):
|
|
170
171
|
ErrorCallback = Callable[[Exception], None]
|
171
172
|
|
172
173
|
|
173
|
-
class
|
174
|
+
class BaseClient:
|
174
175
|
|
175
176
|
def __init__(
|
176
177
|
self,
|
@@ -181,6 +182,8 @@ class APIClient:
|
|
181
182
|
user_agent: str = "API Client",
|
182
183
|
connection: str = "close",
|
183
184
|
verify=False,
|
185
|
+
stream_read_size: int = 512,
|
186
|
+
charset: str = "UTF-8",
|
184
187
|
**extra_headers
|
185
188
|
) -> None:
|
186
189
|
self.base_url = base_url
|
@@ -194,43 +197,89 @@ class APIClient:
|
|
194
197
|
if api_key is not None:
|
195
198
|
self.headers["Authorization"] = f"Bearer {api_key}"
|
196
199
|
self.verify = verify
|
197
|
-
self.
|
200
|
+
self.stream_read_size = stream_read_size
|
201
|
+
self.charset = charset
|
198
202
|
|
199
203
|
DEFAULT_CONN_POOL_SIZE = 10
|
200
|
-
|
201
|
-
PoolManager(DEFAULT_CONN_POOL_SIZE),
|
202
|
-
PoolManager(DEFAULT_CONN_POOL_SIZE
|
204
|
+
URLLIB3_POOL = (
|
205
|
+
PoolManager(DEFAULT_CONN_POOL_SIZE, cert_reqs='CERT_NONE'),
|
206
|
+
PoolManager(DEFAULT_CONN_POOL_SIZE)
|
203
207
|
)
|
204
208
|
|
205
|
-
def
|
209
|
+
def _single_request(
|
210
|
+
self,
|
211
|
+
method: str,
|
212
|
+
url: str,
|
213
|
+
body: Optional[Union[bytes, str]],
|
214
|
+
headers: Optional[Mapping[str, str]],
|
215
|
+
timeout: float,
|
216
|
+
stream: bool,
|
217
|
+
verify: bool,
|
218
|
+
) -> Union[bytes, Iterable[bytes]]:
|
219
|
+
response = self.URLLIB3_POOL[int(verify)].request(
|
220
|
+
method=method,
|
221
|
+
url=url,
|
222
|
+
body=body,
|
223
|
+
headers=headers,
|
224
|
+
timeout=timeout,
|
225
|
+
preload_content=not stream
|
226
|
+
)
|
227
|
+
|
228
|
+
if response.status != 200:
|
229
|
+
try:
|
230
|
+
raise ServiceError(response.data.decode(self.charset))
|
231
|
+
finally:
|
232
|
+
response.release_conn()
|
233
|
+
|
234
|
+
if not stream:
|
235
|
+
try:
|
236
|
+
return response.data
|
237
|
+
finally:
|
238
|
+
response.release_conn()
|
239
|
+
else:
|
240
|
+
def iter_content():
|
241
|
+
try:
|
242
|
+
if hasattr(response, "stream"):
|
243
|
+
yield from response.stream(self.stream_read_size, decode_content=True)
|
244
|
+
else:
|
245
|
+
while True:
|
246
|
+
data = response.read(self.stream_read_size)
|
247
|
+
if not data:
|
248
|
+
break
|
249
|
+
yield data
|
250
|
+
finally:
|
251
|
+
response.release_conn()
|
252
|
+
|
253
|
+
return iter_content()
|
254
|
+
|
255
|
+
def request(
|
206
256
|
self,
|
207
257
|
method: Literal["GET", "POST"],
|
208
258
|
url: str,
|
209
|
-
body: Optional[str] = None,
|
259
|
+
body: Optional[Union[bytes, str]] = None,
|
210
260
|
headers: Optional[Mapping[str, str]] = None,
|
211
|
-
stream: bool = False,
|
212
|
-
num_trials: int = 5,
|
213
261
|
timeout: float = 15,
|
262
|
+
num_trials: int = 5,
|
214
263
|
interval: float = 1,
|
215
264
|
retry_factor: float = 0.5,
|
216
265
|
on_error: Optional[ErrorCallback] = None,
|
266
|
+
stream: bool = False,
|
217
267
|
verify: Optional[bool] = None,
|
218
|
-
):
|
268
|
+
) -> Union[bytes, Iterable[bytes]]:
|
219
269
|
headers = self.headers if headers is None else headers
|
220
270
|
verify = self.verify if verify is None else verify
|
221
|
-
preload_content = not stream
|
222
271
|
|
223
|
-
pool = self.CONN_POOL[int(not verify)]
|
224
272
|
err = None
|
225
273
|
for i in range(num_trials):
|
226
274
|
try:
|
227
|
-
return
|
275
|
+
return self._single_request(
|
228
276
|
method=method,
|
229
277
|
url=url,
|
230
278
|
body=body,
|
231
279
|
headers=headers,
|
232
280
|
timeout=timeout * (1 + i * retry_factor),
|
233
|
-
|
281
|
+
stream=stream,
|
282
|
+
verify=verify,
|
234
283
|
)
|
235
284
|
except TimeoutError as e:
|
236
285
|
err = e
|
@@ -243,98 +292,289 @@ class APIClient:
|
|
243
292
|
sleep(interval)
|
244
293
|
raise err
|
245
294
|
|
246
|
-
|
295
|
+
HTTPX_POOL = httpx.Limits(max_keepalive_connections=DEFAULT_CONN_POOL_SIZE)
|
296
|
+
|
297
|
+
async def _single_request_async(
|
247
298
|
self,
|
248
|
-
|
299
|
+
method: str,
|
300
|
+
url: str,
|
301
|
+
body: Optional[Union[bytes, str]],
|
302
|
+
headers: Optional[Mapping[str, str]],
|
303
|
+
timeout: float,
|
304
|
+
stream: bool,
|
305
|
+
verify: bool,
|
306
|
+
):
|
307
|
+
if not stream:
|
308
|
+
async with httpx.AsyncClient(headers=headers, verify=verify) as client:
|
309
|
+
response = await client.request(
|
310
|
+
method=method,
|
311
|
+
url=url,
|
312
|
+
content=body,
|
313
|
+
timeout=timeout
|
314
|
+
)
|
315
|
+
try:
|
316
|
+
if response.status_code != 200:
|
317
|
+
raise ServiceError(response.content.decode(self.charset))
|
318
|
+
return response.content
|
319
|
+
finally:
|
320
|
+
await response.aclose()
|
321
|
+
else:
|
322
|
+
client = httpx.AsyncClient(headers=headers, verify=verify)
|
323
|
+
response = client.stream(
|
324
|
+
method=method,
|
325
|
+
url=url,
|
326
|
+
content=body,
|
327
|
+
timeout=timeout
|
328
|
+
)
|
329
|
+
|
330
|
+
async def iter_content():
|
331
|
+
try:
|
332
|
+
async with response as r:
|
333
|
+
if r.status_code != 200:
|
334
|
+
content = await r.aread()
|
335
|
+
raise ServiceError(content.decode(self.charset))
|
336
|
+
async for data in r.aiter_bytes():
|
337
|
+
yield data
|
338
|
+
finally:
|
339
|
+
await client.aclose()
|
340
|
+
|
341
|
+
return iter_content()
|
342
|
+
|
343
|
+
async def request_async(
|
344
|
+
self,
|
345
|
+
method: Literal["GET", "POST"],
|
346
|
+
url: str,
|
347
|
+
body: Optional[Union[bytes, str]] = None,
|
348
|
+
headers: Optional[Mapping[str, str]] = None,
|
349
|
+
timeout: float = 15,
|
249
350
|
num_trials: int = 5,
|
351
|
+
interval: float = 1,
|
352
|
+
retry_factor: float = 0.5,
|
353
|
+
on_error: Optional[ErrorCallback] = None,
|
354
|
+
stream: bool = False,
|
355
|
+
verify: Optional[bool] = None,
|
356
|
+
):
|
357
|
+
headers = self.headers if headers is None else headers
|
358
|
+
verify = self.verify if verify is None else verify
|
359
|
+
|
360
|
+
err = None
|
361
|
+
for i in range(num_trials):
|
362
|
+
try:
|
363
|
+
return await self._single_request_async(
|
364
|
+
method=method,
|
365
|
+
url=url,
|
366
|
+
body=body,
|
367
|
+
headers=headers,
|
368
|
+
timeout=timeout * (1 + i * retry_factor),
|
369
|
+
stream=stream,
|
370
|
+
verify=verify,
|
371
|
+
)
|
372
|
+
except httpx.Timeout as e:
|
373
|
+
err = e
|
374
|
+
if callable(on_error):
|
375
|
+
on_error(e)
|
376
|
+
except httpx.HTTPError as e:
|
377
|
+
err = e
|
378
|
+
if callable(on_error):
|
379
|
+
on_error(e)
|
380
|
+
sleep(interval)
|
381
|
+
raise err
|
382
|
+
|
383
|
+
|
384
|
+
class APIClient(BaseClient):
|
385
|
+
|
386
|
+
def request(
|
387
|
+
self,
|
388
|
+
method: Literal["GET", "POST"],
|
389
|
+
path: str,
|
390
|
+
json_data: Optional[Mapping] = None,
|
391
|
+
*,
|
392
|
+
headers: Optional[Mapping[str, str]] = None,
|
250
393
|
timeout: float = 15,
|
394
|
+
num_trials: int = 5,
|
251
395
|
interval: float = 1,
|
252
396
|
retry_factor: float = 0.5,
|
253
|
-
on_error: Optional[ErrorCallback] = None
|
397
|
+
on_error: Optional[ErrorCallback] = None,
|
398
|
+
stream: bool = False,
|
399
|
+
chunk_delimiter: str = "\n\n",
|
400
|
+
chunk_prefix: str = None,
|
401
|
+
chunk_suffix: str = None,
|
402
|
+
error_prefix: str = "ERROR: ",
|
403
|
+
exhaust_stream: bool = False,
|
404
|
+
verify: Optional[bool] = None,
|
254
405
|
):
|
255
406
|
full_url = urljoin(self.base_url, path)
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
407
|
+
headers = {**self.headers}
|
408
|
+
headers["Accept"] = headers["Accept"] + f"; stream={int(stream)}"
|
409
|
+
body = json.dumps(json_data) if json_data is not None else None
|
410
|
+
content = super().request(
|
411
|
+
method,
|
412
|
+
full_url,
|
413
|
+
body=body,
|
414
|
+
headers=headers,
|
260
415
|
timeout=timeout,
|
416
|
+
num_trials=num_trials,
|
261
417
|
interval=interval,
|
262
418
|
retry_factor=retry_factor,
|
263
|
-
on_error=on_error
|
419
|
+
on_error=on_error,
|
420
|
+
stream=stream,
|
421
|
+
verify=verify,
|
264
422
|
)
|
423
|
+
if not stream:
|
424
|
+
return _load_json_or_str(content.decode(self.charset))
|
425
|
+
else:
|
426
|
+
def iter_lines(data_list: Iterable[bytes]) -> Iterable[str]:
|
427
|
+
delimiter = chunk_delimiter.encode(self.charset) if chunk_delimiter is not None else None
|
428
|
+
pending = None
|
429
|
+
for data in data_list:
|
430
|
+
if pending is not None:
|
431
|
+
data = pending + data
|
265
432
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
433
|
+
lines = data.split(delimiter) if delimiter else data.splitlines()
|
434
|
+
pending = lines.pop() if lines and lines[-1] and lines[-1][-1] == data[-1] else None
|
435
|
+
|
436
|
+
for line in lines:
|
437
|
+
yield line.decode(self.charset)
|
438
|
+
|
439
|
+
if pending is not None:
|
440
|
+
yield pending.decode(self.charset)
|
441
|
+
|
442
|
+
def iter_chunks(lines: Iterable[str]):
|
443
|
+
error = None
|
444
|
+
for chunk in lines:
|
445
|
+
if error is not None:
|
446
|
+
# error is not None means there is a fatal exception raised from the server side.
|
447
|
+
# The client should just complete the stream and then raise the error to the upper.
|
448
|
+
continue
|
449
|
+
|
450
|
+
if not chunk:
|
451
|
+
continue
|
270
452
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
453
|
+
if error_prefix is not None:
|
454
|
+
if chunk.startswith(error_prefix):
|
455
|
+
chunk = chunk[len(error_prefix):]
|
456
|
+
error = ServiceError(chunk)
|
457
|
+
continue
|
458
|
+
|
459
|
+
if chunk_prefix is not None:
|
460
|
+
if chunk.startswith(chunk_prefix):
|
461
|
+
chunk = chunk[len(chunk_prefix):]
|
462
|
+
else:
|
463
|
+
continue
|
464
|
+
|
465
|
+
if chunk_suffix is not None:
|
466
|
+
if chunk.endswith(chunk_suffix):
|
467
|
+
chunk = chunk[:-len(chunk_suffix)]
|
468
|
+
else:
|
469
|
+
continue
|
470
|
+
|
471
|
+
yield _load_json_or_str(chunk)
|
472
|
+
|
473
|
+
if error is not None:
|
474
|
+
raise error
|
475
|
+
|
476
|
+
gen = iter_lines(content)
|
477
|
+
gen = iter_chunks(gen)
|
478
|
+
return gen if not exhaust_stream else [*gen]
|
479
|
+
|
480
|
+
def get(
|
481
|
+
self,
|
482
|
+
path: Optional[str] = None,
|
483
|
+
*,
|
484
|
+
timeout: float = 15,
|
485
|
+
num_trials: int = 5,
|
486
|
+
interval: float = 1,
|
487
|
+
retry_factor: float = 0.5,
|
488
|
+
on_error: Optional[ErrorCallback] = None
|
489
|
+
):
|
490
|
+
return self.request(
|
491
|
+
"GET",
|
492
|
+
path,
|
493
|
+
timeout=timeout,
|
494
|
+
num_trials=num_trials,
|
495
|
+
interval=interval,
|
496
|
+
retry_factor=retry_factor,
|
497
|
+
on_error=on_error,
|
498
|
+
)
|
275
499
|
|
276
500
|
def post(
|
277
501
|
self,
|
278
502
|
path: Optional[str] = None,
|
279
|
-
json_data: Optional[Mapping] = None,
|
503
|
+
json_data: Optional[Mapping] = None,
|
504
|
+
*,
|
505
|
+
timeout: float = 15,
|
506
|
+
num_trials: int = 5,
|
507
|
+
interval: float = 1,
|
508
|
+
retry_factor: float = 0.5,
|
509
|
+
on_error: Optional[ErrorCallback] = None,
|
280
510
|
stream: bool = False,
|
511
|
+
chunk_delimiter: str = "\n\n",
|
512
|
+
chunk_prefix: str = None,
|
513
|
+
chunk_suffix: str = None,
|
514
|
+
error_prefix: str = "ERROR: ",
|
281
515
|
exhaust_stream: bool = False,
|
282
|
-
|
516
|
+
):
|
517
|
+
return self.request(
|
518
|
+
"POST",
|
519
|
+
path,
|
520
|
+
json_data=json_data,
|
521
|
+
timeout=timeout,
|
522
|
+
num_trials=num_trials,
|
523
|
+
interval=interval,
|
524
|
+
retry_factor=retry_factor,
|
525
|
+
on_error=on_error,
|
526
|
+
stream=stream,
|
527
|
+
chunk_delimiter=chunk_delimiter,
|
528
|
+
chunk_prefix=chunk_prefix,
|
529
|
+
chunk_suffix=chunk_suffix,
|
530
|
+
error_prefix=error_prefix,
|
531
|
+
exhaust_stream=exhaust_stream,
|
532
|
+
)
|
533
|
+
|
534
|
+
async def request_async(
|
535
|
+
self,
|
536
|
+
method: Literal["GET", "POST"],
|
537
|
+
path: str,
|
538
|
+
json_data: Optional[Mapping] = None,
|
539
|
+
*,
|
540
|
+
headers: Optional[Mapping[str, str]] = None,
|
283
541
|
timeout: float = 15,
|
542
|
+
num_trials: int = 5,
|
284
543
|
interval: float = 1,
|
285
544
|
retry_factor: float = 0.5,
|
286
545
|
on_error: Optional[ErrorCallback] = None,
|
546
|
+
stream: bool = False,
|
287
547
|
chunk_delimiter: str = "\n\n",
|
288
548
|
chunk_prefix: str = None,
|
289
549
|
chunk_suffix: str = None,
|
290
550
|
error_prefix: str = "ERROR: ",
|
291
|
-
|
551
|
+
exhaust_stream: bool = False,
|
552
|
+
verify: Optional[bool] = None,
|
292
553
|
):
|
293
554
|
full_url = urljoin(self.base_url, path)
|
294
|
-
|
295
555
|
headers = {**self.headers}
|
296
556
|
headers["Accept"] = headers["Accept"] + f"; stream={int(stream)}"
|
297
557
|
body = json.dumps(json_data) if json_data is not None else None
|
298
|
-
|
299
|
-
|
300
|
-
|
558
|
+
content = await super().request_async(
|
559
|
+
method,
|
560
|
+
full_url,
|
301
561
|
body=body,
|
302
562
|
headers=headers,
|
303
|
-
stream=stream,
|
304
|
-
num_trials=num_trials,
|
305
563
|
timeout=timeout,
|
564
|
+
num_trials=num_trials,
|
306
565
|
interval=interval,
|
307
566
|
retry_factor=retry_factor,
|
308
|
-
on_error=on_error
|
567
|
+
on_error=on_error,
|
568
|
+
stream=stream,
|
569
|
+
verify=verify,
|
309
570
|
)
|
310
|
-
if response.status != 200:
|
311
|
-
text = response.data.decode(self.charset)
|
312
|
-
response.release_conn()
|
313
|
-
raise ServiceError(text)
|
314
|
-
|
315
571
|
if not stream:
|
316
|
-
|
317
|
-
return _load_json_or_str(response.data.decode(self.charset))
|
318
|
-
finally:
|
319
|
-
response.release_conn()
|
572
|
+
return _load_json_or_str(content.decode(self.charset))
|
320
573
|
else:
|
321
|
-
def
|
322
|
-
try:
|
323
|
-
if hasattr(response, "stream"):
|
324
|
-
yield from response.stream(stream_read_size, decode_content=True)
|
325
|
-
else:
|
326
|
-
while True:
|
327
|
-
data = response.read(stream_read_size)
|
328
|
-
if not data:
|
329
|
-
break
|
330
|
-
yield data
|
331
|
-
finally:
|
332
|
-
response.release_conn()
|
333
|
-
|
334
|
-
def iter_lines(contents: Iterable[bytes]) -> Iterable[str]:
|
574
|
+
async def iter_lines(data_list: AsyncIterable[bytes]) -> AsyncIterable[str]:
|
335
575
|
delimiter = chunk_delimiter.encode(self.charset) if chunk_delimiter is not None else None
|
336
576
|
pending = None
|
337
|
-
for data in
|
577
|
+
async for data in data_list:
|
338
578
|
if pending is not None:
|
339
579
|
data = pending + data
|
340
580
|
|
@@ -347,9 +587,9 @@ class APIClient:
|
|
347
587
|
if pending is not None:
|
348
588
|
yield pending.decode(self.charset)
|
349
589
|
|
350
|
-
def iter_chunks(lines:
|
590
|
+
async def iter_chunks(lines: AsyncIterable[str]) -> AsyncIterable:
|
351
591
|
error = None
|
352
|
-
for chunk in lines:
|
592
|
+
async for chunk in lines:
|
353
593
|
if error is not None:
|
354
594
|
# error is not None means there is a fatal exception raised from the server side.
|
355
595
|
# The client should just complete the stream and then raise the error to the upper.
|
@@ -381,7 +621,60 @@ class APIClient:
|
|
381
621
|
if error is not None:
|
382
622
|
raise error
|
383
623
|
|
384
|
-
gen =
|
385
|
-
gen = iter_lines(gen)
|
624
|
+
gen = iter_lines(content)
|
386
625
|
gen = iter_chunks(gen)
|
387
626
|
return gen if not exhaust_stream else [*gen]
|
627
|
+
|
628
|
+
async def get_async(
|
629
|
+
self,
|
630
|
+
path: Optional[str] = None,
|
631
|
+
*,
|
632
|
+
timeout: float = 15,
|
633
|
+
num_trials: int = 5,
|
634
|
+
interval: float = 1,
|
635
|
+
retry_factor: float = 0.5,
|
636
|
+
on_error: Optional[ErrorCallback] = None
|
637
|
+
):
|
638
|
+
return await self.request_async(
|
639
|
+
"GET",
|
640
|
+
path,
|
641
|
+
timeout=timeout,
|
642
|
+
num_trials=num_trials,
|
643
|
+
interval=interval,
|
644
|
+
retry_factor=retry_factor,
|
645
|
+
on_error=on_error,
|
646
|
+
)
|
647
|
+
|
648
|
+
async def post_async(
|
649
|
+
self,
|
650
|
+
path: Optional[str] = None,
|
651
|
+
json_data: Optional[Mapping] = None,
|
652
|
+
*,
|
653
|
+
timeout: float = 15,
|
654
|
+
num_trials: int = 5,
|
655
|
+
interval: float = 1,
|
656
|
+
retry_factor: float = 0.5,
|
657
|
+
on_error: Optional[ErrorCallback] = None,
|
658
|
+
stream: bool = False,
|
659
|
+
chunk_delimiter: str = "\n\n",
|
660
|
+
chunk_prefix: str = None,
|
661
|
+
chunk_suffix: str = None,
|
662
|
+
error_prefix: str = "ERROR: ",
|
663
|
+
exhaust_stream: bool = False,
|
664
|
+
):
|
665
|
+
return await self.request_async(
|
666
|
+
"POST",
|
667
|
+
path,
|
668
|
+
json_data=json_data,
|
669
|
+
timeout=timeout,
|
670
|
+
num_trials=num_trials,
|
671
|
+
interval=interval,
|
672
|
+
retry_factor=retry_factor,
|
673
|
+
on_error=on_error,
|
674
|
+
stream=stream,
|
675
|
+
chunk_delimiter=chunk_delimiter,
|
676
|
+
chunk_prefix=chunk_prefix,
|
677
|
+
chunk_suffix=chunk_suffix,
|
678
|
+
error_prefix=error_prefix,
|
679
|
+
exhaust_stream=exhaust_stream,
|
680
|
+
)
|
libentry/test_api.py
CHANGED
@@ -43,8 +43,7 @@ def test(request: TestRequest):
|
|
43
43
|
try:
|
44
44
|
kwargs = dict(
|
45
45
|
on_error=lambda err: print(f"[{tid}:{cid}:RETRY] {err}"),
|
46
|
-
timeout=request.timeout
|
47
|
-
stream=request.stream
|
46
|
+
timeout=request.timeout
|
48
47
|
)
|
49
48
|
t = time()
|
50
49
|
if request.method == "GET":
|
@@ -56,6 +55,7 @@ def test(request: TestRequest):
|
|
56
55
|
response = APIClient().post(
|
57
56
|
request.url,
|
58
57
|
request.data,
|
58
|
+
stream=request.stream,
|
59
59
|
**kwargs
|
60
60
|
)
|
61
61
|
if not request.stream:
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.1
|
2
2
|
Name: libentry
|
3
|
-
Version: 1.21
|
3
|
+
Version: 1.21.1
|
4
4
|
Summary: Entries for experimental utilities.
|
5
5
|
Home-page: https://github.com/XoriieInpottn/libentry
|
6
6
|
Author: xi
|
@@ -9,23 +9,13 @@ License: Apache-2.0 license
|
|
9
9
|
Platform: any
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
11
11
|
Description-Content-Type: text/markdown
|
12
|
-
|
13
|
-
Requires-Dist: pydantic
|
12
|
+
Requires-Dist: Flask
|
14
13
|
Requires-Dist: PyYAML
|
14
|
+
Requires-Dist: gunicorn
|
15
|
+
Requires-Dist: httpx
|
15
16
|
Requires-Dist: numpy
|
17
|
+
Requires-Dist: pydantic
|
16
18
|
Requires-Dist: urllib3
|
17
|
-
Requires-Dist: Flask
|
18
|
-
Requires-Dist: gunicorn
|
19
|
-
Dynamic: author
|
20
|
-
Dynamic: author-email
|
21
|
-
Dynamic: classifier
|
22
|
-
Dynamic: description
|
23
|
-
Dynamic: description-content-type
|
24
|
-
Dynamic: home-page
|
25
|
-
Dynamic: license
|
26
|
-
Dynamic: platform
|
27
|
-
Dynamic: requires-dist
|
28
|
-
Dynamic: summary
|
29
19
|
|
30
20
|
# libentry
|
31
21
|
|
@@ -39,3 +29,5 @@ Dynamic: summary
|
|
39
29
|
2. Use its post() or get() method to send the request.
|
40
30
|
|
41
31
|
|
32
|
+
|
33
|
+
|
@@ -1,5 +1,5 @@
|
|
1
|
-
libentry/__init__.py,sha256=
|
2
|
-
libentry/api.py,sha256=
|
1
|
+
libentry/__init__.py,sha256=ko2YBIIx5H3dD0tedBkialzJGEDczFaP_PZmT1cIlak,148
|
2
|
+
libentry/api.py,sha256=g7AAZIV7e7i4v4r6ZNkMnMIOPMDG1LKYs9uZp-3R1VI,22101
|
3
3
|
libentry/argparse.py,sha256=NxzXV-jBN51ReZsNs5aeyOfzwYQ5A5nJ95rWoa-FYCs,10415
|
4
4
|
libentry/dataclasses.py,sha256=AQV2PuxplJCwGZ5HKX72U-z-POUhTdy3XtpEK9KNIGQ,4541
|
5
5
|
libentry/executor.py,sha256=cTV0WxJi0nU1TP-cOwmeodN8DD6L1691M2HIQsJtGrU,6582
|
@@ -7,8 +7,7 @@ libentry/experiment.py,sha256=ejgAHDXWIe9x4haUzIFuz1WasLY0_aD1z_vyEVGjTu8,4922
|
|
7
7
|
libentry/json.py,sha256=1-Kv5ZRb5dBrOTU84n6sZtYZV3xE-O6wEt_--ynbSaU,1209
|
8
8
|
libentry/logging.py,sha256=IiYoCUzm8XTK1fduA-NA0FI2Qz_m81NEPV3d3tEfgdI,1349
|
9
9
|
libentry/schema.py,sha256=o6JcdR00Yj4_Qjmlo100OlQpMVnl0PgvvwVVrL9limw,8268
|
10
|
-
libentry/
|
11
|
-
libentry/test_api.py,sha256=GFmirvxfCAQ-nhh6QXyB5EpF4WaENCc3Eoymuk5JxBQ,4448
|
10
|
+
libentry/test_api.py,sha256=Xw7B7sH6g1iCTV5sFzyBF3JAJzeOr9xg0AyezTNsnIk,4452
|
12
11
|
libentry/service/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
|
13
12
|
libentry/service/common.py,sha256=OVaW2afgKA6YqstJmtnprBCqQEUZEWotZ6tHavmJJeU,42
|
14
13
|
libentry/service/flask.py,sha256=Alpsix01ROqI28k-dabD8wJlWAWs-ObNc1nJ-LxOXn4,13349
|
@@ -16,10 +15,10 @@ libentry/service/list.py,sha256=ElHWhTgShGOhaxMUEwVbMXos0NQKjHsODboiQ-3AMwE,1397
|
|
16
15
|
libentry/service/running.py,sha256=FrPJoJX6wYxcHIysoatAxhW3LajCCm0Gx6l7__6sULQ,5105
|
17
16
|
libentry/service/start.py,sha256=mZT7b9rVULvzy9GTZwxWnciCHgv9dbGN2JbxM60OMn4,1270
|
18
17
|
libentry/service/stop.py,sha256=wOpwZgrEJ7QirntfvibGq-XsTC6b3ELhzRW2zezh-0s,1187
|
19
|
-
libentry-1.21.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
20
|
-
libentry-1.21.dist-info/METADATA,sha256=
|
21
|
-
libentry-1.21.dist-info/WHEEL,sha256=
|
22
|
-
libentry-1.21.dist-info/entry_points.txt,sha256=
|
23
|
-
libentry-1.21.dist-info/top_level.txt,sha256=u2uF6-X5fn2Erf9PYXOg_6tntPqTpyT-yzUZrltEd6I,9
|
24
|
-
libentry-1.21.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
25
|
-
libentry-1.21.dist-info/RECORD,,
|
18
|
+
libentry-1.21.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
19
|
+
libentry-1.21.1.dist-info/METADATA,sha256=A9zv4xA6q8s2F8WzkeOYzmk0aNwD8-Z9s12tYRn0SQQ,813
|
20
|
+
libentry-1.21.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
21
|
+
libentry-1.21.1.dist-info/entry_points.txt,sha256=vgHmJZhM-kqM7U9S179UwDD3pM232tpzJ5NntncXi_8,62
|
22
|
+
libentry-1.21.1.dist-info/top_level.txt,sha256=u2uF6-X5fn2Erf9PYXOg_6tntPqTpyT-yzUZrltEd6I,9
|
23
|
+
libentry-1.21.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
24
|
+
libentry-1.21.1.dist-info/RECORD,,
|
libentry/server.py
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
|
3
|
-
import os
|
4
|
-
from socketserver import ThreadingMixIn
|
5
|
-
from threading import Thread
|
6
|
-
from xmlrpc.server import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
|
7
|
-
|
8
|
-
from libentry.logging import logger
|
9
|
-
|
10
|
-
__all__ = [
|
11
|
-
'XMLRPCServerMixIn',
|
12
|
-
]
|
13
|
-
|
14
|
-
|
15
|
-
class ThreadXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
|
16
|
-
pass
|
17
|
-
|
18
|
-
|
19
|
-
class MultRequestHandler(SimpleXMLRPCRequestHandler):
|
20
|
-
rpc_paths = ('/RPC2', '/RPC3')
|
21
|
-
|
22
|
-
def log_message(self, fmt, *args):
|
23
|
-
logger.info(fmt % args)
|
24
|
-
|
25
|
-
|
26
|
-
class XMLRPCServerMixIn:
|
27
|
-
|
28
|
-
def __init__(self, log_request=True):
|
29
|
-
self.log_request = log_request
|
30
|
-
|
31
|
-
def serve_forever(self, addr, daemon=False):
|
32
|
-
if 'RANK' in os.environ:
|
33
|
-
rank = int(os.environ['RANK'])
|
34
|
-
if rank > 0:
|
35
|
-
return
|
36
|
-
|
37
|
-
with ThreadXMLRPCServer(
|
38
|
-
addr,
|
39
|
-
requestHandler=MultRequestHandler,
|
40
|
-
allow_none=True,
|
41
|
-
logRequests=self.log_request
|
42
|
-
) as server:
|
43
|
-
server.register_introspection_functions()
|
44
|
-
server.register_multicall_functions()
|
45
|
-
server.register_instance(self)
|
46
|
-
if daemon:
|
47
|
-
if hasattr(self, 'serve_thread'):
|
48
|
-
raise RuntimeError('Server is already running.')
|
49
|
-
serve_thread = Thread(target=server.serve_forever, daemon=daemon)
|
50
|
-
serve_thread.start()
|
51
|
-
setattr(self, 'serve_thread', serve_thread)
|
52
|
-
else:
|
53
|
-
server.serve_forever()
|
File without changes
|
File without changes
|
File without changes
|