libentry 1.11.4__py3-none-any.whl → 1.11.6__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/api.py +73 -7
- libentry/service/flask.py +40 -25
- {libentry-1.11.4.dist-info → libentry-1.11.6.dist-info}/METADATA +1 -1
- {libentry-1.11.4.dist-info → libentry-1.11.6.dist-info}/RECORD +8 -8
- {libentry-1.11.4.dist-info → libentry-1.11.6.dist-info}/LICENSE +0 -0
- {libentry-1.11.4.dist-info → libentry-1.11.6.dist-info}/WHEEL +0 -0
- {libentry-1.11.4.dist-info → libentry-1.11.6.dist-info}/top_level.txt +0 -0
- {libentry-1.11.4.dist-info → libentry-1.11.6.dist-info}/zip-safe +0 -0
libentry/api.py
CHANGED
@@ -11,6 +11,7 @@ __all__ = [
|
|
11
11
|
]
|
12
12
|
|
13
13
|
from dataclasses import dataclass, field
|
14
|
+
from time import sleep
|
14
15
|
from typing import Any, Callable, Iterable, List, Literal, Mapping, Optional, Tuple
|
15
16
|
from urllib.parse import urljoin
|
16
17
|
|
@@ -31,6 +32,7 @@ class APIInfo:
|
|
31
32
|
chunk_suffix: str = field(default=None)
|
32
33
|
stream_prefix: str = field(default=None)
|
33
34
|
stream_suffix: str = field(default=None)
|
35
|
+
error_prefix: str = field(default="ERROR: ")
|
34
36
|
extra_info: Mapping[str, Any] = field(default_factory=dict)
|
35
37
|
|
36
38
|
|
@@ -189,9 +191,48 @@ class APIClient:
|
|
189
191
|
self.headers["Authorization"] = f"Bearer {api_key}"
|
190
192
|
self.verify = verify
|
191
193
|
|
192
|
-
def
|
194
|
+
def _request(
|
195
|
+
self,
|
196
|
+
method: str,
|
197
|
+
url: str,
|
198
|
+
num_trials: int = 5,
|
199
|
+
retry_interval: int = 5,
|
200
|
+
retry_factor: float = 2,
|
201
|
+
timeout: float = 5,
|
202
|
+
**kwargs
|
203
|
+
):
|
204
|
+
err = None
|
205
|
+
for _ in range(num_trials):
|
206
|
+
try:
|
207
|
+
return requests.request(method, url, timeout=timeout, **kwargs)
|
208
|
+
except requests.Timeout as e:
|
209
|
+
err = e
|
210
|
+
except requests.ConnectionError as e:
|
211
|
+
err = e
|
212
|
+
sleep(retry_interval)
|
213
|
+
timeout *= retry_factor
|
214
|
+
retry_interval *= retry_factor
|
215
|
+
raise err
|
216
|
+
|
217
|
+
def get(
|
218
|
+
self,
|
219
|
+
path: str,
|
220
|
+
num_trials: int = 5,
|
221
|
+
retry_interval: int = 5,
|
222
|
+
retry_factor: float = 2,
|
223
|
+
timeout: float = 5
|
224
|
+
):
|
193
225
|
api_url = urljoin(self.base_url, path)
|
194
|
-
response =
|
226
|
+
response = self._request(
|
227
|
+
"get",
|
228
|
+
url=api_url,
|
229
|
+
headers=self.headers,
|
230
|
+
verify=self.verify,
|
231
|
+
num_trials=num_trials,
|
232
|
+
retry_interval=retry_interval,
|
233
|
+
retry_factor=retry_factor,
|
234
|
+
timeout=timeout
|
235
|
+
)
|
195
236
|
|
196
237
|
if response.status_code != 200:
|
197
238
|
text = response.text
|
@@ -207,21 +248,29 @@ class APIClient:
|
|
207
248
|
self,
|
208
249
|
path: str,
|
209
250
|
json_data: Optional[Mapping] = None,
|
210
|
-
stream=False,
|
211
|
-
|
251
|
+
stream: bool = False,
|
252
|
+
num_trials: int = 5,
|
253
|
+
retry_interval: int = 5,
|
254
|
+
retry_factor: float = 2,
|
255
|
+
timeout: float = 5,
|
212
256
|
chunk_delimiter: str = "\n\n",
|
213
257
|
chunk_prefix: str = None,
|
214
258
|
chunk_suffix: str = None,
|
259
|
+
error_prefix: str = "ERROR: "
|
215
260
|
):
|
216
261
|
full_url = urljoin(self.base_url, path)
|
217
262
|
|
218
263
|
data = json.dumps(json_data) if json_data is not None else None
|
219
|
-
response =
|
220
|
-
|
264
|
+
response = self._request(
|
265
|
+
"post",
|
266
|
+
url=full_url,
|
221
267
|
headers=self.headers,
|
222
268
|
data=data,
|
223
269
|
verify=self.verify,
|
224
270
|
stream=stream,
|
271
|
+
num_trials=num_trials,
|
272
|
+
retry_interval=retry_interval,
|
273
|
+
retry_factor=retry_factor,
|
225
274
|
timeout=timeout
|
226
275
|
)
|
227
276
|
if response.status_code != 200:
|
@@ -239,6 +288,7 @@ class APIClient:
|
|
239
288
|
chunk_delimiter=chunk_delimiter.encode() if chunk_delimiter else None,
|
240
289
|
chunk_prefix=chunk_prefix.encode() if chunk_prefix else None,
|
241
290
|
chunk_suffix=chunk_suffix.encode() if chunk_suffix else None,
|
291
|
+
error_prefix=error_prefix.encode() if error_prefix else None,
|
242
292
|
)
|
243
293
|
else:
|
244
294
|
try:
|
@@ -251,13 +301,26 @@ class APIClient:
|
|
251
301
|
response: requests.Response,
|
252
302
|
chunk_delimiter: bytes,
|
253
303
|
chunk_prefix: bytes,
|
254
|
-
chunk_suffix: bytes
|
304
|
+
chunk_suffix: bytes,
|
305
|
+
error_prefix: bytes
|
255
306
|
) -> Iterable:
|
256
307
|
try:
|
308
|
+
error = None
|
257
309
|
for chunk in response.iter_lines(decode_unicode=False, delimiter=chunk_delimiter):
|
310
|
+
if error is not None:
|
311
|
+
# error is not None means there is a fatal exception raised from the server side.
|
312
|
+
# The client should just complete the stream and then raise the error to the upper.
|
313
|
+
continue
|
314
|
+
|
258
315
|
if not chunk:
|
259
316
|
continue
|
260
317
|
|
318
|
+
if error_prefix is not None:
|
319
|
+
if chunk.startswith(error_prefix):
|
320
|
+
chunk = chunk[len(error_prefix):]
|
321
|
+
error = ServiceError(chunk)
|
322
|
+
continue
|
323
|
+
|
261
324
|
if chunk_prefix is not None:
|
262
325
|
if chunk.startswith(chunk_prefix):
|
263
326
|
chunk = chunk[len(chunk_prefix):]
|
@@ -271,5 +334,8 @@ class APIClient:
|
|
271
334
|
continue
|
272
335
|
|
273
336
|
yield _load_json_or_str(chunk)
|
337
|
+
|
338
|
+
if error is not None:
|
339
|
+
raise error
|
274
340
|
finally:
|
275
341
|
response.close()
|
libentry/service/flask.py
CHANGED
@@ -33,16 +33,27 @@ class JSONDumper:
|
|
33
33
|
if self.api_info.chunk_delimiter is not None:
|
34
34
|
yield self.api_info.chunk_delimiter
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
try:
|
37
|
+
for item in response:
|
38
|
+
text = self.dump(item)
|
38
39
|
|
39
|
-
|
40
|
-
|
40
|
+
if self.api_info.chunk_prefix is not None:
|
41
|
+
yield self.api_info.chunk_prefix
|
41
42
|
|
42
|
-
|
43
|
+
yield text
|
43
44
|
|
44
|
-
|
45
|
-
|
45
|
+
if self.api_info.chunk_suffix is not None:
|
46
|
+
yield self.api_info.chunk_suffix
|
47
|
+
|
48
|
+
if self.api_info.chunk_delimiter is not None:
|
49
|
+
yield self.api_info.chunk_delimiter
|
50
|
+
except Exception as e:
|
51
|
+
if isinstance(e, (SystemExit, KeyboardInterrupt)):
|
52
|
+
raise e
|
53
|
+
if self.api_info.error_prefix is not None:
|
54
|
+
yield self.api_info.error_prefix
|
55
|
+
|
56
|
+
yield self.dump_error(e)
|
46
57
|
|
47
58
|
if self.api_info.chunk_delimiter is not None:
|
48
59
|
yield self.api_info.chunk_delimiter
|
@@ -65,6 +76,19 @@ class JSONDumper:
|
|
65
76
|
except TypeError:
|
66
77
|
return repr(response)
|
67
78
|
|
79
|
+
@staticmethod
|
80
|
+
def dump_error(e: Exception) -> str:
|
81
|
+
err_cls = e.__class__
|
82
|
+
err_name = err_cls.__name__
|
83
|
+
module = err_cls.__module__
|
84
|
+
if module != "builtins":
|
85
|
+
err_name = f"{module}.{err_name}"
|
86
|
+
return json.dumps({
|
87
|
+
"error": err_name,
|
88
|
+
"message": str(e),
|
89
|
+
"traceback": traceback.format_exc()
|
90
|
+
}, indent=2)
|
91
|
+
|
68
92
|
|
69
93
|
def create_model_from_signature(fn):
|
70
94
|
sig = signature(fn)
|
@@ -122,14 +146,14 @@ class FlaskWrapper:
|
|
122
146
|
except Exception as e:
|
123
147
|
if isinstance(e, (SystemExit, KeyboardInterrupt)):
|
124
148
|
raise e
|
125
|
-
return self.app.error(self.
|
149
|
+
return self.app.error(self.dumper.dump_error(e))
|
126
150
|
else:
|
127
151
|
try:
|
128
152
|
response = self.fn(**input_json)
|
129
153
|
except Exception as e:
|
130
154
|
if isinstance(e, (SystemExit, KeyboardInterrupt)):
|
131
155
|
raise e
|
132
|
-
return self.app.error(self.
|
156
|
+
return self.app.error(self.dumper.dump_error(e))
|
133
157
|
|
134
158
|
if isinstance(response, (GeneratorType, range)):
|
135
159
|
return self.app.response_class(
|
@@ -142,19 +166,6 @@ class FlaskWrapper:
|
|
142
166
|
mimetype=self.api_info.mime_type
|
143
167
|
)
|
144
168
|
|
145
|
-
@staticmethod
|
146
|
-
def make_err(e):
|
147
|
-
err_cls = e.__class__
|
148
|
-
err_name = err_cls.__name__
|
149
|
-
module = err_cls.__module__
|
150
|
-
if module != "builtins":
|
151
|
-
err_name = f"{module}.{err_name}"
|
152
|
-
return json.dumps({
|
153
|
-
"error": err_name,
|
154
|
-
"message": str(e),
|
155
|
-
"traceback": traceback.format_exc()
|
156
|
-
}, indent=2)
|
157
|
-
|
158
169
|
|
159
170
|
class CustomGenerateJsonSchema(GenerateJsonSchema):
|
160
171
|
|
@@ -260,8 +271,10 @@ def run_service(
|
|
260
271
|
host: str = "0.0.0.0",
|
261
272
|
port: int = 8888,
|
262
273
|
num_workers: int = 1,
|
263
|
-
num_threads: int =
|
264
|
-
num_connections: int =
|
274
|
+
num_threads: int = 20,
|
275
|
+
num_connections: Optional[int] = 100,
|
276
|
+
backlog: Optional[int] = 100,
|
277
|
+
worker_class: str = "gthread",
|
265
278
|
timeout: int = 60,
|
266
279
|
keyfile: Optional[str] = None,
|
267
280
|
certfile: Optional[str] = None
|
@@ -272,9 +285,11 @@ def run_service(
|
|
272
285
|
"workers": num_workers,
|
273
286
|
"threads": num_threads,
|
274
287
|
"timeout": timeout,
|
275
|
-
"worker_connections": num_connections,
|
288
|
+
"worker_connections": num_connections if num_connections else num_threads,
|
289
|
+
"backlog": backlog if backlog else num_threads,
|
276
290
|
"keyfile": keyfile,
|
277
291
|
"certfile": certfile,
|
292
|
+
"worker_class": worker_class
|
278
293
|
}
|
279
294
|
for name, value in options.items():
|
280
295
|
logger.info(f"Option {name}: {value}")
|
@@ -1,5 +1,5 @@
|
|
1
1
|
libentry/__init__.py,sha256=rDBip9M1Xb1N4wMKE1ni_DldrQbkRjp8DxPkTp3K2qo,170
|
2
|
-
libentry/api.py,sha256=
|
2
|
+
libentry/api.py,sha256=IajZ1oSSsFfB7Gc4QUCGy8uUqxrtFov1LTsMg06ck0Y,10067
|
3
3
|
libentry/argparse.py,sha256=Bk11H4WRKxcjMlSd0mjWj1T4NWh0JW5eA7TX3C21IoE,10116
|
4
4
|
libentry/dataclasses.py,sha256=AQV2PuxplJCwGZ5HKX72U-z-POUhTdy3XtpEK9KNIGQ,4541
|
5
5
|
libentry/executor.py,sha256=cTV0WxJi0nU1TP-cOwmeodN8DD6L1691M2HIQsJtGrU,6582
|
@@ -9,14 +9,14 @@ libentry/logging.py,sha256=IiYoCUzm8XTK1fduA-NA0FI2Qz_m81NEPV3d3tEfgdI,1349
|
|
9
9
|
libentry/server.py,sha256=gYPoZXd0umlDYZf-6ZV0_vJadg3YQvnLDc6JFDJh9jc,1503
|
10
10
|
libentry/service/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
|
11
11
|
libentry/service/common.py,sha256=OVaW2afgKA6YqstJmtnprBCqQEUZEWotZ6tHavmJJeU,42
|
12
|
-
libentry/service/flask.py,sha256=
|
12
|
+
libentry/service/flask.py,sha256=71FcjawjfoNG4p0hUWyPpiQ8X5Ao4zsTqt_UEbnSMRc,9842
|
13
13
|
libentry/service/list.py,sha256=ElHWhTgShGOhaxMUEwVbMXos0NQKjHsODboiQ-3AMwE,1397
|
14
14
|
libentry/service/running.py,sha256=FrPJoJX6wYxcHIysoatAxhW3LajCCm0Gx6l7__6sULQ,5105
|
15
15
|
libentry/service/start.py,sha256=mZT7b9rVULvzy9GTZwxWnciCHgv9dbGN2JbxM60OMn4,1270
|
16
16
|
libentry/service/stop.py,sha256=wOpwZgrEJ7QirntfvibGq-XsTC6b3ELhzRW2zezh-0s,1187
|
17
|
-
libentry-1.11.
|
18
|
-
libentry-1.11.
|
19
|
-
libentry-1.11.
|
20
|
-
libentry-1.11.
|
21
|
-
libentry-1.11.
|
22
|
-
libentry-1.11.
|
17
|
+
libentry-1.11.6.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
18
|
+
libentry-1.11.6.dist-info/METADATA,sha256=pQ_nA2SJrcmSEX-_y-ljBsTP3C7j70onmqtU8IWXFeY,500
|
19
|
+
libentry-1.11.6.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
|
20
|
+
libentry-1.11.6.dist-info/top_level.txt,sha256=u2uF6-X5fn2Erf9PYXOg_6tntPqTpyT-yzUZrltEd6I,9
|
21
|
+
libentry-1.11.6.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
22
|
+
libentry-1.11.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|