libentry 1.22.3__py3-none-any.whl → 1.23__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/schema.py CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  __author__ = "xi"
4
4
  __all__ = [
5
+ "APISignature",
6
+ "get_api_signature",
5
7
  "SchemaField",
6
8
  "Schema",
7
- "ParseContext",
8
9
  "parse_type",
9
10
  "QueryAPIOutput",
10
11
  "query_api",
@@ -13,14 +14,73 @@ __all__ = [
13
14
  import enum
14
15
  from dataclasses import asdict, dataclass, is_dataclass
15
16
  from inspect import signature
16
- from typing import Any, Dict, Iterable, List, Literal, Mapping, MutableMapping, NoReturn, Optional, Sequence, Union, \
17
- get_args, \
18
- get_origin
17
+ from typing import Any, Dict, Iterable, List, Literal, Mapping, MutableMapping, NoReturn, Optional, Sequence, Type, \
18
+ Union, get_args, get_origin
19
19
 
20
- from pydantic import BaseModel, Field, create_model
20
+ from pydantic import BaseModel, ConfigDict, Field, RootModel, create_model
21
21
  from pydantic_core import PydanticUndefined
22
22
 
23
23
 
24
+ class APISignature(BaseModel):
25
+ input_types: List[Any]
26
+ input_model: Optional[Type[BaseModel]] = None
27
+ bundled_model: Optional[Type[BaseModel]] = None
28
+ output_type: Optional[Any] = None
29
+ output_model: Optional[Type[BaseModel]] = None
30
+
31
+
32
+ def get_api_signature(fn, ignores: List[str] = ("self", "cls")) -> APISignature:
33
+ sig = signature(fn)
34
+
35
+ input_types = []
36
+ fields = {}
37
+ for name, param in sig.parameters.items():
38
+ if name in ignores:
39
+ continue
40
+
41
+ annotation = param.annotation
42
+ if annotation is sig.empty:
43
+ annotation = Any
44
+
45
+ input_types.append(annotation)
46
+
47
+ default = param.default
48
+ field = Field() if default is sig.empty else Field(default=default)
49
+ fields[name] = (annotation, field)
50
+
51
+ input_model = None
52
+ bundled_model = None
53
+ if len(input_types) == 1:
54
+ for annotation in input_types:
55
+ origin = get_origin(annotation) or annotation
56
+ if isinstance(origin, type) and issubclass(origin, BaseModel):
57
+ input_model = origin
58
+ if input_model is None:
59
+ name = "".join(word.capitalize() for word in fn.__name__.split("_"))
60
+ bundled_model = create_model(
61
+ f"{name}Request*",
62
+ __config__=ConfigDict(extra="forbid"),
63
+ **fields
64
+ )
65
+
66
+ output_type = None
67
+ output_model = None
68
+ output_annotation = sig.return_annotation
69
+ if output_annotation is not None and output_annotation is not NoReturn:
70
+ if output_annotation is sig.empty:
71
+ output_annotation = Any
72
+ output_type = output_annotation
73
+ output_model = RootModel[output_annotation]
74
+
75
+ return APISignature(
76
+ input_types=input_types,
77
+ input_model=input_model,
78
+ bundled_model=bundled_model,
79
+ output_type=output_type,
80
+ output_model=output_model
81
+ )
82
+
83
+
24
84
  class SchemaField(BaseModel):
25
85
  name: str = Field()
26
86
  type: Union[str, List[str]] = Field("Any")
@@ -75,13 +135,14 @@ def type_parser(fn):
75
135
 
76
136
 
77
137
  @type_parser
78
- def _parse_basic_types(context: ParseContext):
138
+ def _parse_basic_types(context: ParseContext) -> Optional[str]:
79
139
  if context.origin in {int, float, str, bool}:
80
140
  return context.origin.__name__
141
+ return None
81
142
 
82
143
 
83
144
  @type_parser
84
- def _parse_dict(context: ParseContext):
145
+ def _parse_dict(context: ParseContext) -> Optional[str]:
85
146
  if issubclass(context.origin, Mapping):
86
147
  dict_args = get_args(context.annotation)
87
148
  if dict_args:
@@ -95,10 +156,11 @@ def _parse_dict(context: ParseContext):
95
156
  return f"Dict[{key_type},{value_type}]"
96
157
  else:
97
158
  return "Dict"
159
+ return None
98
160
 
99
161
 
100
162
  @type_parser
101
- def _parse_list(context: ParseContext):
163
+ def _parse_list(context: ParseContext) -> Optional[str]:
102
164
  if issubclass(context.origin, Sequence):
103
165
  list_args = get_args(context.annotation)
104
166
  if list_args:
@@ -110,16 +172,18 @@ def _parse_list(context: ParseContext):
110
172
  return f"List[{elem_type}]"
111
173
  else:
112
174
  return "List"
175
+ return None
113
176
 
114
177
 
115
178
  @type_parser
116
- def _parse_enum(context: ParseContext):
179
+ def _parse_enum(context: ParseContext) -> Optional[str]:
117
180
  if issubclass(context.origin, enum.Enum):
118
181
  return f"Enum[{','.join(e.name for e in context.origin)}]"
182
+ return None
119
183
 
120
184
 
121
185
  @type_parser
122
- def _parse_base_model(context: ParseContext):
186
+ def _parse_base_model(context: ParseContext) -> Optional[str]:
123
187
  origin = context.origin
124
188
  if issubclass(origin, BaseModel):
125
189
  _module = origin.__module__
@@ -155,10 +219,11 @@ def _parse_base_model(context: ParseContext):
155
219
  context.schemas[model_name] = schema
156
220
 
157
221
  return model_name
222
+ return None
158
223
 
159
224
 
160
225
  @type_parser
161
- def _parse_iterable(context: ParseContext):
226
+ def _parse_iterable(context: ParseContext) -> Optional[str]:
162
227
  if context.origin.__name__ in {"Iterable", "Generator", "range"} and issubclass(context.origin, Iterable):
163
228
  iter_args = get_args(context.annotation)
164
229
  if len(iter_args) != 1:
@@ -167,20 +232,23 @@ def _parse_iterable(context: ParseContext):
167
232
  if isinstance(iter_type, list):
168
233
  raise TypeError("\"Union\" cannot be used as the type of iterable elements.")
169
234
  return f"Iter[{iter_type}]"
235
+ return None
170
236
 
171
237
 
172
238
  @type_parser
173
- def _parse_none_type(context: ParseContext):
239
+ def _parse_none_type(context: ParseContext) -> Optional[str]:
174
240
  origin = context.origin
175
241
  if origin.__module__ == "builtins" and origin.__name__ == "NoneType":
176
242
  return "NoneType"
243
+ return None
177
244
 
178
245
 
179
246
  @type_parser
180
- def _parse_ndarray(context: ParseContext):
247
+ def _parse_ndarray(context: ParseContext) -> Optional[str]:
181
248
  origin = context.origin
182
249
  if origin.__module__ == "numpy" and origin.__name__ == "ndarray":
183
250
  return "numpy.ndarray"
251
+ return None
184
252
 
185
253
 
186
254
  def generic_parser(fn):
@@ -189,25 +257,28 @@ def generic_parser(fn):
189
257
 
190
258
 
191
259
  @generic_parser
192
- def _parse_any(context: ParseContext):
260
+ def _parse_any(context: ParseContext) -> Optional[str]:
193
261
  if context.origin is Any or str(context.origin) == str(Any):
194
262
  return "Any"
263
+ return None
195
264
 
196
265
 
197
266
  @generic_parser
198
- def _parse_union(context: ParseContext):
267
+ def _parse_union(context: ParseContext) -> Optional[List[str]]:
199
268
  if context.origin is Union or str(context.origin) == str(Union):
200
269
  return [
201
270
  parse_type(arg, context.schemas)
202
271
  for arg in get_args(context.annotation)
203
272
  ]
273
+ return None
204
274
 
205
275
 
206
276
  @generic_parser
207
- def _parse_literal(context: ParseContext):
277
+ def _parse_literal(context: ParseContext) -> Optional[str]:
208
278
  if context.origin is Literal or str(context.origin) == str(Literal):
209
279
  enum_args = get_args(context.annotation)
210
280
  return f"Enum[{','.join(map(str, enum_args))}]"
281
+ return None
211
282
 
212
283
 
213
284
  class QueryAPIOutput(BaseModel):
@@ -217,49 +288,33 @@ class QueryAPIOutput(BaseModel):
217
288
  bundled_input: bool
218
289
 
219
290
 
220
- def query_api(fn) -> QueryAPIOutput:
221
- sig = signature(fn)
222
-
223
- fields = {}
224
- for name, param in sig.parameters.items():
225
- if name in ["self", "cls"]:
226
- continue
227
-
228
- annotation = param.annotation
229
- if annotation is sig.empty:
230
- annotation = Any
231
-
232
- default = param.default
233
- field = Field() if default is sig.empty else Field(default)
234
- fields[name] = (annotation, field)
235
-
236
- args_model = None
237
- if len(fields) == 1:
238
- for annotation, _ in fields.values():
239
- origin = get_origin(annotation)
240
- if origin is None:
241
- origin = annotation
242
- if isinstance(origin, type) and issubclass(origin, BaseModel):
243
- args_model = origin
244
- bundle = args_model is None
245
- if bundle:
246
- name = "".join(word.capitalize() for word in fn.__name__.split("_"))
247
- args_model = create_model(f"{name}Request*", **fields)
291
+ def query_api(obj) -> QueryAPIOutput:
292
+ api_models = obj if isinstance(obj, APISignature) else get_api_signature(obj)
248
293
 
249
294
  context = {}
295
+
296
+ args_model = api_models.input_model or api_models.bundled_model
250
297
  input_schema = parse_type(args_model, context)
298
+
251
299
  output_schema = None
252
- return_annotation = sig.return_annotation
253
- if return_annotation is not None and return_annotation is not NoReturn:
254
- if return_annotation is sig.empty:
255
- return_annotation = Any
256
- output_schema = parse_type(return_annotation, context)
300
+ if api_models.output_type is not None:
301
+ output_schema = parse_type(api_models.output_type, context)
257
302
  if isinstance(output_schema, list):
258
303
  output_schema = output_schema[0]
259
304
 
305
+ # output_schema = None
306
+ # sig = signature(fn)
307
+ # return_annotation = sig.return_annotation
308
+ # if return_annotation is not None and return_annotation is not NoReturn:
309
+ # if return_annotation is sig.empty:
310
+ # return_annotation = Any
311
+ # output_schema = parse_type(return_annotation, context)
312
+ # if isinstance(output_schema, list):
313
+ # output_schema = output_schema[0]
314
+
260
315
  return QueryAPIOutput(
261
316
  input_schema=input_schema,
262
317
  output_schema=output_schema,
263
318
  context=context,
264
- bundled_input=bundle,
319
+ bundled_input=api_models.bundled_model is not None,
265
320
  )
libentry/service/flask.py CHANGED
@@ -371,6 +371,7 @@ def run_service(
371
371
  worker_class: str = "gthread",
372
372
  timeout: int = 60,
373
373
  keyfile: Optional[str] = None,
374
+ keyfile_password: Optional[str] = None,
374
375
  certfile: Optional[str] = None
375
376
  ):
376
377
  logger.info("Starting gunicorn server.")
@@ -378,6 +379,18 @@ def run_service(
378
379
  num_connections = num_threads * 2
379
380
  if backlog is None or backlog < num_threads * 2:
380
381
  backlog = num_threads * 2
382
+
383
+ def ssl_context(config, _default_ssl_context_factory):
384
+ import ssl
385
+ context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
386
+ context.load_cert_chain(
387
+ certfile=config.certfile,
388
+ keyfile=config.keyfile,
389
+ password=keyfile_password
390
+ )
391
+ context.minimum_version = ssl.TLSVersion.TLSv1_3
392
+ return context
393
+
381
394
  options = {
382
395
  "bind": f"{host}:{port}",
383
396
  "workers": num_workers,
@@ -387,9 +400,9 @@ def run_service(
387
400
  "backlog": backlog,
388
401
  "keyfile": keyfile,
389
402
  "certfile": certfile,
390
- "worker_class": worker_class
403
+ "worker_class": worker_class,
404
+ "ssl_context": ssl_context
391
405
  }
392
406
  for name, value in options.items():
393
407
  logger.info(f"Option {name}: {value}")
394
408
  GunicornApplication(service_type, service_config, options).run()
395
-
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: libentry
3
- Version: 1.22.3
3
+ Version: 1.23
4
4
  Summary: Entries for experimental utilities.
5
5
  Home-page: https://github.com/XoriieInpottn/libentry
6
6
  Author: xi
@@ -9,15 +9,27 @@ License: Apache-2.0 license
9
9
  Platform: any
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Description-Content-Type: text/markdown
12
- Requires-Dist: Flask
12
+ License-File: LICENSE
13
+ Requires-Dist: pydantic
13
14
  Requires-Dist: PyYAML
14
- Requires-Dist: gunicorn
15
- Requires-Dist: httpx
16
15
  Requires-Dist: numpy
17
- Requires-Dist: pydantic
18
16
  Requires-Dist: urllib3
17
+ Requires-Dist: httpx
18
+ Requires-Dist: Flask
19
+ Requires-Dist: gunicorn
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: license
27
+ Dynamic: platform
28
+ Dynamic: requires-dist
29
+ Dynamic: summary
19
30
 
20
31
  # libentry
32
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/XoriieInpottn/libentry)
21
33
 
22
34
  ## Define a Service Class
23
35
  1. Define a normal python class.
@@ -29,5 +41,3 @@ Requires-Dist: urllib3
29
41
  2. Use its post() or get() method to send the request.
30
42
 
31
43
 
32
-
33
-
@@ -0,0 +1,30 @@
1
+ libentry/__init__.py,sha256=ko2YBIIx5H3dD0tedBkialzJGEDczFaP_PZmT1cIlak,148
2
+ libentry/api.py,sha256=lw_XH7GMWxwX7OzN4K5SxBHobjNIT-FNiTcRK60v7Fg,24151
3
+ libentry/argparse.py,sha256=NxzXV-jBN51ReZsNs5aeyOfzwYQ5A5nJ95rWoa-FYCs,10415
4
+ libentry/dataclasses.py,sha256=AQV2PuxplJCwGZ5HKX72U-z-POUhTdy3XtpEK9KNIGQ,4541
5
+ libentry/executor.py,sha256=cTV0WxJi0nU1TP-cOwmeodN8DD6L1691M2HIQsJtGrU,6582
6
+ libentry/experiment.py,sha256=ejgAHDXWIe9x4haUzIFuz1WasLY0_aD1z_vyEVGjTu8,4922
7
+ libentry/json.py,sha256=CubUUu29h7idLaC4d66vKhjBgVHKN1rZOv-Tw2qM17k,1916
8
+ libentry/logging.py,sha256=IiYoCUzm8XTK1fduA-NA0FI2Qz_m81NEPV3d3tEfgdI,1349
9
+ libentry/schema.py,sha256=i-TswY-R7Lw-WY1pFFYBMoEUCPN_tpUMpezlS7nQgIw,10107
10
+ libentry/test_api.py,sha256=Xw7B7sH6g1iCTV5sFzyBF3JAJzeOr9xg0AyezTNsnIk,4452
11
+ libentry/utils.py,sha256=O7P6GadtUIjq0N2IZH7PhHZDUM3NebzcqyDqytet7CM,683
12
+ libentry/mcp/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
13
+ libentry/mcp/api.py,sha256=uoGBYCesMj6umlJpRulKZNS3trm9oG3LUSg1otPDS_8,2362
14
+ libentry/mcp/client.py,sha256=lM_bTF40pbdYdBrMmoOqUDRzlNgjqEKh5d4IVkpI6D8,21512
15
+ libentry/mcp/service.py,sha256=KDpEUhHuyVXjc_J5Z9_aciJbTcEy9dYA44rpdgAAwGE,32322
16
+ libentry/mcp/types.py,sha256=xTQCnKAgeJNss4klJ33MrWHGCzG_LeR3urizO_Z9q9U,12239
17
+ libentry/service/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
18
+ libentry/service/common.py,sha256=OVaW2afgKA6YqstJmtnprBCqQEUZEWotZ6tHavmJJeU,42
19
+ libentry/service/flask.py,sha256=2egCFFhRAfLpmSyibgaJ-3oexI-j27P1bmaPEn-hSlc,13817
20
+ libentry/service/list.py,sha256=ElHWhTgShGOhaxMUEwVbMXos0NQKjHsODboiQ-3AMwE,1397
21
+ libentry/service/running.py,sha256=FrPJoJX6wYxcHIysoatAxhW3LajCCm0Gx6l7__6sULQ,5105
22
+ libentry/service/start.py,sha256=mZT7b9rVULvzy9GTZwxWnciCHgv9dbGN2JbxM60OMn4,1270
23
+ libentry/service/stop.py,sha256=wOpwZgrEJ7QirntfvibGq-XsTC6b3ELhzRW2zezh-0s,1187
24
+ libentry-1.23.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
25
+ libentry-1.23.dist-info/METADATA,sha256=_2SdH3WYDWIH-bs_FzKN3NHzViQKtYFDf7BKVs19WcM,1133
26
+ libentry-1.23.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
27
+ libentry-1.23.dist-info/entry_points.txt,sha256=1v_nLVDsjvVJp9SWhl4ef2zZrsLTBtFWgrYFgqvQBgc,61
28
+ libentry-1.23.dist-info/top_level.txt,sha256=u2uF6-X5fn2Erf9PYXOg_6tntPqTpyT-yzUZrltEd6I,9
29
+ libentry-1.23.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
30
+ libentry-1.23.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.45.1)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
2
  libentry_test_api = libentry.test_api:main
3
-
@@ -1,25 +0,0 @@
1
- libentry/__init__.py,sha256=ko2YBIIx5H3dD0tedBkialzJGEDczFaP_PZmT1cIlak,148
2
- libentry/api.py,sha256=UkXdBv9oqQhaSESRReLWEPj8venBUoCBppIg-FAXqKA,24146
3
- libentry/argparse.py,sha256=NxzXV-jBN51ReZsNs5aeyOfzwYQ5A5nJ95rWoa-FYCs,10415
4
- libentry/dataclasses.py,sha256=AQV2PuxplJCwGZ5HKX72U-z-POUhTdy3XtpEK9KNIGQ,4541
5
- libentry/executor.py,sha256=cTV0WxJi0nU1TP-cOwmeodN8DD6L1691M2HIQsJtGrU,6582
6
- libentry/experiment.py,sha256=ejgAHDXWIe9x4haUzIFuz1WasLY0_aD1z_vyEVGjTu8,4922
7
- libentry/json.py,sha256=Rigg8H3oWooI9bNfIpx9mSOcoc3aTigy_-BHiPIv8kY,1821
8
- libentry/logging.py,sha256=IiYoCUzm8XTK1fduA-NA0FI2Qz_m81NEPV3d3tEfgdI,1349
9
- libentry/schema.py,sha256=o6JcdR00Yj4_Qjmlo100OlQpMVnl0PgvvwVVrL9limw,8268
10
- libentry/test_api.py,sha256=Xw7B7sH6g1iCTV5sFzyBF3JAJzeOr9xg0AyezTNsnIk,4452
11
- libentry/utils.py,sha256=O7P6GadtUIjq0N2IZH7PhHZDUM3NebzcqyDqytet7CM,683
12
- libentry/service/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
13
- libentry/service/common.py,sha256=OVaW2afgKA6YqstJmtnprBCqQEUZEWotZ6tHavmJJeU,42
14
- libentry/service/flask.py,sha256=Alpsix01ROqI28k-dabD8wJlWAWs-ObNc1nJ-LxOXn4,13349
15
- libentry/service/list.py,sha256=ElHWhTgShGOhaxMUEwVbMXos0NQKjHsODboiQ-3AMwE,1397
16
- libentry/service/running.py,sha256=FrPJoJX6wYxcHIysoatAxhW3LajCCm0Gx6l7__6sULQ,5105
17
- libentry/service/start.py,sha256=mZT7b9rVULvzy9GTZwxWnciCHgv9dbGN2JbxM60OMn4,1270
18
- libentry/service/stop.py,sha256=wOpwZgrEJ7QirntfvibGq-XsTC6b3ELhzRW2zezh-0s,1187
19
- libentry-1.22.3.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
20
- libentry-1.22.3.dist-info/METADATA,sha256=xpYfLdij0X7LmPtj1jHxb7RmeL3p2zrYA5fPJiwLnEY,813
21
- libentry-1.22.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
22
- libentry-1.22.3.dist-info/entry_points.txt,sha256=vgHmJZhM-kqM7U9S179UwDD3pM232tpzJ5NntncXi_8,62
23
- libentry-1.22.3.dist-info/top_level.txt,sha256=u2uF6-X5fn2Erf9PYXOg_6tntPqTpyT-yzUZrltEd6I,9
24
- libentry-1.22.3.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
25
- libentry-1.22.3.dist-info/RECORD,,