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 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 get(self, path: str, timeout=60):
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 = requests.get(api_url, headers=self.headers, verify=self.verify, timeout=timeout)
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
- timeout=60,
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 = requests.post(
220
- full_url,
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
- for item in response:
37
- text = self.dump(item)
36
+ try:
37
+ for item in response:
38
+ text = self.dump(item)
38
39
 
39
- if self.api_info.chunk_prefix is not None:
40
- yield self.api_info.chunk_prefix
40
+ if self.api_info.chunk_prefix is not None:
41
+ yield self.api_info.chunk_prefix
41
42
 
42
- yield text
43
+ yield text
43
44
 
44
- if self.api_info.chunk_suffix is not None:
45
- yield self.api_info.chunk_suffix
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.make_err(e))
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.make_err(e))
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 = 10,
264
- num_connections: int = 2000,
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: libentry
3
- Version: 1.11.4
3
+ Version: 1.11.6
4
4
  Summary: Entries for experimental utilities.
5
5
  Home-page: https://github.com/XoriieInpottn/libentry
6
6
  Author: xi
@@ -1,5 +1,5 @@
1
1
  libentry/__init__.py,sha256=rDBip9M1Xb1N4wMKE1ni_DldrQbkRjp8DxPkTp3K2qo,170
2
- libentry/api.py,sha256=2I52bVwJPxr_FTHCn4fCm08lGpNofcp9JGWz8JJJq4A,7934
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=TZGyDXWh5r93uxklSpP7cKmD2r0ZswplMUK91Hxo4Do,9169
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.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
18
- libentry-1.11.4.dist-info/METADATA,sha256=bWMkqIhQ4ro2pebBCS7tRNdvXEiOwWy7g2mmVSB1098,500
19
- libentry-1.11.4.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
20
- libentry-1.11.4.dist-info/top_level.txt,sha256=u2uF6-X5fn2Erf9PYXOg_6tntPqTpyT-yzUZrltEd6I,9
21
- libentry-1.11.4.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
22
- libentry-1.11.4.dist-info/RECORD,,
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,,