libentry 1.11.10__py3-none-any.whl → 1.11.12__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 +5 -5
- libentry/service/flask.py +34 -12
- {libentry-1.11.10.dist-info → libentry-1.11.12.dist-info}/METADATA +17 -6
- {libentry-1.11.10.dist-info → libentry-1.11.12.dist-info}/RECORD +8 -9
- {libentry-1.11.10.dist-info → libentry-1.11.12.dist-info}/WHEEL +1 -1
- libentry/start_service.py +0 -74
- {libentry-1.11.10.dist-info → libentry-1.11.12.dist-info}/LICENSE +0 -0
- {libentry-1.11.10.dist-info → libentry-1.11.12.dist-info}/top_level.txt +0 -0
- {libentry-1.11.10.dist-info → libentry-1.11.12.dist-info}/zip-safe +0 -0
libentry/api.py
CHANGED
@@ -170,8 +170,8 @@ class APIClient:
|
|
170
170
|
|
171
171
|
def __init__(
|
172
172
|
self,
|
173
|
-
base_url: str,
|
174
|
-
api_key: str = None,
|
173
|
+
base_url: Optional[str] = None,
|
174
|
+
api_key: Optional[str] = None,
|
175
175
|
accept: str = "application/json",
|
176
176
|
content_type: str = "application/json",
|
177
177
|
user_agent: str = "API Client",
|
@@ -219,10 +219,10 @@ class APIClient:
|
|
219
219
|
retry_factor: float = 2,
|
220
220
|
timeout: float = 15
|
221
221
|
):
|
222
|
-
|
222
|
+
full_url = urljoin(self.base_url, path) if self.base_url else path
|
223
223
|
response = self._request(
|
224
224
|
"get",
|
225
|
-
url=
|
225
|
+
url=full_url,
|
226
226
|
headers=self.headers,
|
227
227
|
verify=self.verify,
|
228
228
|
num_trials=num_trials,
|
@@ -254,7 +254,7 @@ class APIClient:
|
|
254
254
|
chunk_suffix: str = None,
|
255
255
|
error_prefix: str = "ERROR: "
|
256
256
|
):
|
257
|
-
full_url = urljoin(self.base_url, path)
|
257
|
+
full_url = urljoin(self.base_url, path) if self.base_url else path
|
258
258
|
|
259
259
|
headers = {**self.headers}
|
260
260
|
headers["Accept"] = headers["Accept"] + f"; stream={int(stream)}"
|
libentry/service/flask.py
CHANGED
@@ -10,14 +10,14 @@ import re
|
|
10
10
|
import traceback
|
11
11
|
from inspect import signature
|
12
12
|
from types import GeneratorType
|
13
|
-
from typing import Callable, Iterable, Optional, Type, Union
|
13
|
+
from typing import Any, Callable, Iterable, Optional, Type, Union
|
14
14
|
|
15
15
|
from flask import Flask, request
|
16
16
|
from gunicorn.app.base import BaseApplication
|
17
17
|
from pydantic import BaseModel, Field, create_model
|
18
18
|
from pydantic.json_schema import GenerateJsonSchema
|
19
19
|
|
20
|
-
from libentry import json
|
20
|
+
from libentry import api, json
|
21
21
|
from libentry.api import APIInfo, list_api_info
|
22
22
|
from libentry.logging import logger
|
23
23
|
|
@@ -120,7 +120,11 @@ def create_model_from_signature(fn):
|
|
120
120
|
fields[name] = (param.annotation, None)
|
121
121
|
else:
|
122
122
|
fields[name] = (param.annotation, Field())
|
123
|
-
|
123
|
+
|
124
|
+
return_annotation = sig.return_annotation
|
125
|
+
if return_annotation is sig.empty:
|
126
|
+
return_annotation = Any
|
127
|
+
fields["return"] = (return_annotation, None)
|
124
128
|
return create_model(f"__{fn.__name__}_signature", **fields)
|
125
129
|
|
126
130
|
|
@@ -253,27 +257,45 @@ class FlaskServer(Flask):
|
|
253
257
|
self.post(path)(wrapped_fn)
|
254
258
|
else:
|
255
259
|
raise RuntimeError(f"Unsupported method \"{method}\" for ")
|
256
|
-
logger.info("Flask application initialized.")
|
257
260
|
|
258
|
-
|
261
|
+
for fn, api_info in list_api_info(self):
|
262
|
+
method = api_info.method
|
263
|
+
path = api_info.path
|
264
|
+
if asyncio.iscoroutinefunction(fn):
|
265
|
+
logger.error(f"Async function \"{fn.__name__}\" is not supported.")
|
266
|
+
continue
|
267
|
+
logger.info(f"Serving {method}-API for {path}")
|
259
268
|
|
260
|
-
|
261
|
-
|
262
|
-
|
269
|
+
wrapped_fn = FlaskWrapper(self, fn, api_info)
|
270
|
+
if method == "GET":
|
271
|
+
self.get(path)(wrapped_fn)
|
272
|
+
elif method == "POST":
|
273
|
+
self.post(path)(wrapped_fn)
|
274
|
+
else:
|
275
|
+
raise RuntimeError(f"Unsupported method \"{method}\" for ")
|
276
|
+
|
277
|
+
logger.info("Flask application initialized.")
|
278
|
+
|
279
|
+
@api.get("/")
|
280
|
+
def index(self, name: str = None):
|
281
|
+
if name is None:
|
263
282
|
all_api = []
|
264
283
|
for _, api_info in self.api_info_list:
|
265
284
|
all_api.append({"path": api_info.path})
|
266
|
-
return
|
285
|
+
return all_api
|
267
286
|
|
268
|
-
name = args["name"]
|
269
287
|
for fn, api_info in self.api_info_list:
|
270
288
|
if api_info.path == "/" + name:
|
271
289
|
# noinspection PyTypeChecker
|
272
290
|
dynamic_model = create_model_from_signature(fn)
|
273
291
|
schema = dynamic_model.model_json_schema(schema_generator=CustomGenerateJsonSchema)
|
274
|
-
return
|
292
|
+
return schema
|
293
|
+
|
294
|
+
return f"No API named \"{name}\""
|
275
295
|
|
276
|
-
|
296
|
+
@api.get()
|
297
|
+
def live(self):
|
298
|
+
return "OK"
|
277
299
|
|
278
300
|
def ok(self, body: Union[str, Iterable[str]], mimetype="application/json"):
|
279
301
|
return self.response_class(body, status=200, mimetype=mimetype)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: libentry
|
3
|
-
Version: 1.11.
|
3
|
+
Version: 1.11.12
|
4
4
|
Summary: Entries for experimental utilities.
|
5
5
|
Home-page: https://github.com/XoriieInpottn/libentry
|
6
6
|
Author: xi
|
@@ -9,13 +9,24 @@ License: Apache-2.0 license
|
|
9
9
|
Platform: any
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
11
11
|
Description-Content-Type: text/markdown
|
12
|
-
License-File: LICENSE
|
13
|
-
Requires-Dist: requests
|
14
|
-
Requires-Dist: pydantic
|
15
|
-
Requires-Dist: json5
|
16
|
-
Requires-Dist: PyYAML
|
17
12
|
Requires-Dist: Flask
|
13
|
+
Requires-Dist: PyYAML
|
18
14
|
Requires-Dist: gunicorn
|
15
|
+
Requires-Dist: json5
|
16
|
+
Requires-Dist: pydantic
|
17
|
+
Requires-Dist: requests
|
19
18
|
|
20
19
|
# libentry
|
21
20
|
|
21
|
+
## Define a Service Class
|
22
|
+
1. Define a normal python class.
|
23
|
+
2. Use @api.post() or @api.get() to tag a method as API method.
|
24
|
+
3. Ensure every API method has type hint for both arguments and return value.
|
25
|
+
|
26
|
+
## Access a Service
|
27
|
+
1. Create an APIClient instance.
|
28
|
+
2. Use its post() or get() method to send the request.
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
@@ -1,5 +1,5 @@
|
|
1
1
|
libentry/__init__.py,sha256=rDBip9M1Xb1N4wMKE1ni_DldrQbkRjp8DxPkTp3K2qo,170
|
2
|
-
libentry/api.py,sha256=
|
2
|
+
libentry/api.py,sha256=OZ-KZvSV2sWHshYB6vgS-qIu1P6bdfqcMCrOQs-_RwA,10114
|
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,17 +7,16 @@ 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/server.py,sha256=gYPoZXd0umlDYZf-6ZV0_vJadg3YQvnLDc6JFDJh9jc,1503
|
10
|
-
libentry/start_service.py,sha256=Mm0HRwikW1KcDsnkK_Jo2QlNqe5BBBgMqtGd9jZxX1o,1902
|
11
10
|
libentry/service/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
|
12
11
|
libentry/service/common.py,sha256=OVaW2afgKA6YqstJmtnprBCqQEUZEWotZ6tHavmJJeU,42
|
13
|
-
libentry/service/flask.py,sha256=
|
12
|
+
libentry/service/flask.py,sha256=IWA9GKyuLCcdpuwZxjKXMOy8MCnEWympMyNjHxRXu-Q,12333
|
14
13
|
libentry/service/list.py,sha256=ElHWhTgShGOhaxMUEwVbMXos0NQKjHsODboiQ-3AMwE,1397
|
15
14
|
libentry/service/running.py,sha256=FrPJoJX6wYxcHIysoatAxhW3LajCCm0Gx6l7__6sULQ,5105
|
16
15
|
libentry/service/start.py,sha256=mZT7b9rVULvzy9GTZwxWnciCHgv9dbGN2JbxM60OMn4,1270
|
17
16
|
libentry/service/stop.py,sha256=wOpwZgrEJ7QirntfvibGq-XsTC6b3ELhzRW2zezh-0s,1187
|
18
|
-
libentry-1.11.
|
19
|
-
libentry-1.11.
|
20
|
-
libentry-1.11.
|
21
|
-
libentry-1.11.
|
22
|
-
libentry-1.11.
|
23
|
-
libentry-1.11.
|
17
|
+
libentry-1.11.12.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
18
|
+
libentry-1.11.12.dist-info/METADATA,sha256=GLIiUoxHfO_byVbTFbyBMar-S8ywTkGzZR_tQpRBmII,794
|
19
|
+
libentry-1.11.12.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
20
|
+
libentry-1.11.12.dist-info/top_level.txt,sha256=u2uF6-X5fn2Erf9PYXOg_6tntPqTpyT-yzUZrltEd6I,9
|
21
|
+
libentry-1.11.12.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
22
|
+
libentry-1.11.12.dist-info/RECORD,,
|
libentry/start_service.py
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
|
3
|
-
__author__ = "xi"
|
4
|
-
|
5
|
-
import os
|
6
|
-
import subprocess
|
7
|
-
from cgi import parse
|
8
|
-
from typing import Dict, List, Optional
|
9
|
-
|
10
|
-
import yaml
|
11
|
-
from pydantic import BaseModel, Field
|
12
|
-
|
13
|
-
from libentry import ArgumentParser
|
14
|
-
|
15
|
-
|
16
|
-
class Config(BaseModel):
|
17
|
-
exec: str = Field()
|
18
|
-
envs: Dict[str, str] = Field(default_factory=dict)
|
19
|
-
stdout: Optional[str] = Field(default="-")
|
20
|
-
stderr: Optional[str] = Field(default="-")
|
21
|
-
|
22
|
-
|
23
|
-
class Status(BaseModel):
|
24
|
-
pid: int = Field()
|
25
|
-
|
26
|
-
|
27
|
-
def main():
|
28
|
-
parser = ArgumentParser()
|
29
|
-
parser.add_argument("--config_dir", "-d")
|
30
|
-
parser.add_argument("--config_filename", "-f", default="config.json")
|
31
|
-
parser.add_argument("--status_filename", default="status.json")
|
32
|
-
args = parser.parse_args()
|
33
|
-
|
34
|
-
config_dir = args.config_dir
|
35
|
-
if config_dir is None:
|
36
|
-
config_dir = os.getcwd()
|
37
|
-
config_dir = os.path.abspath(config_dir)
|
38
|
-
os.chdir(config_dir)
|
39
|
-
|
40
|
-
if not os.path.exists(args.config_filename):
|
41
|
-
raise FileNotFoundError(f"Cannot find \"{args.config_filename}\".")
|
42
|
-
|
43
|
-
with open(args.config_filename) as f:
|
44
|
-
config = Config.model_validate(yaml.safe_load(f))
|
45
|
-
|
46
|
-
if config.stdout == "-":
|
47
|
-
stdout = None
|
48
|
-
elif config.stdout is None:
|
49
|
-
stdout = subprocess.DEVNULL
|
50
|
-
else:
|
51
|
-
stdout = open(config.stdout, "a")
|
52
|
-
if config.stderr == "-":
|
53
|
-
stderr = None
|
54
|
-
elif config.stderr is None:
|
55
|
-
stderr = subprocess.DEVNULL
|
56
|
-
else:
|
57
|
-
stderr = open(config.stderr, "a")
|
58
|
-
|
59
|
-
process = subprocess.Popen(
|
60
|
-
["/bin/bash", "-c", config["exec"]],
|
61
|
-
cwd=os.getcwd(),
|
62
|
-
env={**os.environ, **config.envs} if len(config.envs) > 0 else None,
|
63
|
-
preexec_fn=os.setpgrp,
|
64
|
-
stdout=stdout,
|
65
|
-
stderr=stderr
|
66
|
-
)
|
67
|
-
pgid = os.getpgid(process.pid)
|
68
|
-
with open(PID_FILENAME, "w") as f:
|
69
|
-
f.write(str(pgid))
|
70
|
-
return 0
|
71
|
-
|
72
|
-
|
73
|
-
if __name__ == "__main__":
|
74
|
-
raise SystemExit(main())
|
File without changes
|
File without changes
|
File without changes
|