kisa-utils 0.36.2__py3-none-any.whl → 0.36.3__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.
- kisa_utils/functionUtils.py +1 -1
- kisa_utils/servers/flask.py +65 -7
- {kisa_utils-0.36.2.dist-info → kisa_utils-0.36.3.dist-info}/METADATA +1 -1
- {kisa_utils-0.36.2.dist-info → kisa_utils-0.36.3.dist-info}/RECORD +6 -6
- {kisa_utils-0.36.2.dist-info → kisa_utils-0.36.3.dist-info}/WHEEL +0 -0
- {kisa_utils-0.36.2.dist-info → kisa_utils-0.36.3.dist-info}/top_level.txt +0 -0
kisa_utils/functionUtils.py
CHANGED
kisa_utils/servers/flask.py
CHANGED
|
@@ -5,7 +5,7 @@ Kisa Server Utilities
|
|
|
5
5
|
import kisa_utils as kutils
|
|
6
6
|
from kisa_utils.storage import Path
|
|
7
7
|
from kisa_utils.response import Response, Ok, Error
|
|
8
|
-
from flask import Flask, request, jsonify, wrappers, render_template_string
|
|
8
|
+
from flask import Flask, request, jsonify, wrappers, render_template_string, current_app, g as app_ctx
|
|
9
9
|
from flask_cors import CORS
|
|
10
10
|
from functools import wraps
|
|
11
11
|
import copy
|
|
@@ -25,6 +25,7 @@ import types
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
_VALID_HTTP_METHODS = {'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'}
|
|
28
|
+
_WHITE_LISTED_PARAMS = ["headers"]
|
|
28
29
|
|
|
29
30
|
from typing import Optional, Callable
|
|
30
31
|
from functools import wraps
|
|
@@ -42,6 +43,27 @@ def __init():
|
|
|
42
43
|
globals()['__SERVER_APP'] = __app
|
|
43
44
|
globals()['__BASIC_AUTH'] = __basic_auth
|
|
44
45
|
|
|
46
|
+
# change logger to include time spend on a request
|
|
47
|
+
import logging, logging.handlers, time
|
|
48
|
+
logging.getLogger('werkzeug').setLevel(logging.ERROR)
|
|
49
|
+
logging.basicConfig(level=logging.INFO)
|
|
50
|
+
__app.logger.addHandler(logging.handlers.SysLogHandler(address="/dev/log")) # Address for journald
|
|
51
|
+
|
|
52
|
+
@__app.before_request
|
|
53
|
+
def beforeRequest():
|
|
54
|
+
app_ctx.start_time = time.perf_counter()
|
|
55
|
+
app_ctx.start_timestamp = kutils.dates.currentTimestamp()
|
|
56
|
+
|
|
57
|
+
@__app.after_request
|
|
58
|
+
def logging_after(response):
|
|
59
|
+
# Get total time in milliseconds
|
|
60
|
+
total_time = round(time.perf_counter() - app_ctx.start_time, 4)
|
|
61
|
+
# Log the time taken for the endpoint
|
|
62
|
+
|
|
63
|
+
current_app.logger.info('[%s %s %s %d] %s', request.method, app_ctx.start_timestamp, total_time, response.status_code, request.path)
|
|
64
|
+
|
|
65
|
+
return response
|
|
66
|
+
|
|
45
67
|
|
|
46
68
|
__init()
|
|
47
69
|
|
|
@@ -238,11 +260,13 @@ def endpoint(
|
|
|
238
260
|
|
|
239
261
|
signature = inspect.signature(func)
|
|
240
262
|
typeHints = func.__annotations__
|
|
241
|
-
parameters = signature.parameters
|
|
263
|
+
# parameters = signature.parameters
|
|
242
264
|
|
|
243
|
-
|
|
265
|
+
headers_type = typeHints.get("headers")
|
|
266
|
+
if headers_type and not (headers_type is dict):
|
|
267
|
+
raise TypeError("arg::headers type must be 'dict'")
|
|
244
268
|
|
|
245
|
-
if not handler.__doc__:
|
|
269
|
+
if not handler.__doc__ and not (handler.__doc__.strip()):
|
|
246
270
|
raise ValueError(
|
|
247
271
|
f"handler function {func.__name__} has no docString!")
|
|
248
272
|
|
|
@@ -265,6 +289,9 @@ def endpoint(
|
|
|
265
289
|
f"function `{handler.__name__}` should take either positional-only or keyword-only parameters")
|
|
266
290
|
|
|
267
291
|
if value.kind == inspect.Parameter.POSITIONAL_ONLY:
|
|
292
|
+
if key == "headers":
|
|
293
|
+
raise ValueError(f"arg: {key}, should ony be passed as keyword_only argument!")
|
|
294
|
+
|
|
268
295
|
if value.default is not inspect.Parameter.empty:
|
|
269
296
|
if not (resp := validateWithResponse(value.default, typeHints[key])):
|
|
270
297
|
raise ValueError(f'arg `{key}` default value: {resp.log}')
|
|
@@ -273,8 +300,12 @@ def endpoint(
|
|
|
273
300
|
args.append((key,))
|
|
274
301
|
elif value.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
275
302
|
if value.default is not inspect.Parameter.empty:
|
|
303
|
+
if key == "headers":
|
|
304
|
+
raise ValueError(f'arg `{key}` should have no default value')
|
|
305
|
+
|
|
276
306
|
if not (resp := validateWithResponse(value.default, typeHints[key])):
|
|
277
307
|
raise ValueError(f'arg `{key}` default value: {resp.log}')
|
|
308
|
+
|
|
278
309
|
kwargs.append((key, value.default))
|
|
279
310
|
else:
|
|
280
311
|
kwargs.append((key,))
|
|
@@ -287,6 +318,10 @@ def endpoint(
|
|
|
287
318
|
if not hint:
|
|
288
319
|
raise TypeError(f"parameter {key} has no type hint")
|
|
289
320
|
|
|
321
|
+
# print(typeHints)
|
|
322
|
+
if "headers" in typeHints and "headers" in _WHITE_LISTED_PARAMS:
|
|
323
|
+
del typeHints["headers"]
|
|
324
|
+
|
|
290
325
|
validationStructure = copy.deepcopy(typeHints)
|
|
291
326
|
del validationStructure["return"]
|
|
292
327
|
|
|
@@ -308,7 +343,6 @@ def endpoint(
|
|
|
308
343
|
return resp
|
|
309
344
|
|
|
310
345
|
payload = resp.data
|
|
311
|
-
# print("pyload====>", payload)
|
|
312
346
|
|
|
313
347
|
# Handle text payloads differently
|
|
314
348
|
if isinstance(payload, (str, bytes)):
|
|
@@ -317,7 +351,6 @@ def endpoint(
|
|
|
317
351
|
raise BadRequest("Text input requires exactly one positional parameter")
|
|
318
352
|
|
|
319
353
|
nonlocal validationStructure
|
|
320
|
-
|
|
321
354
|
_args = []
|
|
322
355
|
_kwargs = {}
|
|
323
356
|
|
|
@@ -334,10 +367,15 @@ def endpoint(
|
|
|
334
367
|
|
|
335
368
|
for item in kwargs:
|
|
336
369
|
kwarg = item[0]
|
|
370
|
+
_headers_present = True if kwarg == "headers" else False
|
|
337
371
|
|
|
338
372
|
if kwarg in payload:
|
|
339
373
|
_kwargs[kwarg] = payload[kwarg]
|
|
340
374
|
else:
|
|
375
|
+
if _headers_present and kwarg == "headers":
|
|
376
|
+
_kwargs[kwarg] = dict(request.headers)
|
|
377
|
+
continue
|
|
378
|
+
|
|
341
379
|
if len(item) == 2:
|
|
342
380
|
default = item[1]
|
|
343
381
|
_kwargs[kwarg] = default
|
|
@@ -785,8 +823,28 @@ def _convert_single_type(value:Any, target_type:type)->Any:
|
|
|
785
823
|
except (ValueError, TypeError) as e:
|
|
786
824
|
raise ValueError(f"Cannot convert {value} to {target_type.__name__}: {str(e)}")
|
|
787
825
|
|
|
826
|
+
@enforceRequirements
|
|
827
|
+
def parse_headers(headers_str: str, /) -> dict:
|
|
828
|
+
"""Parses a raw HTTP headers string into a dictionary.
|
|
829
|
+
|
|
830
|
+
Args:
|
|
831
|
+
headers_str: Raw headers string (
|
|
832
|
+
e.g., "Content-Type: application/json\nAuthorization: Bearer token"
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
Returns:
|
|
836
|
+
Dictionary with header names as keys and header values as values.
|
|
837
|
+
"""
|
|
838
|
+
headers = {}
|
|
839
|
+
|
|
840
|
+
for line in headers_str.strip().splitlines():
|
|
841
|
+
if ':' in line:
|
|
842
|
+
key, value = line.split(':', 1)
|
|
843
|
+
headers[key.strip()] = value.strip()
|
|
844
|
+
|
|
845
|
+
return headers
|
|
788
846
|
|
|
789
847
|
# startServer()
|
|
790
848
|
if __name__ == "__main__":
|
|
791
849
|
# print('======> app:', getAppInstance())
|
|
792
|
-
startServer()
|
|
850
|
+
startServer()
|
|
@@ -7,7 +7,7 @@ kisa_utils/db.py,sha256=qMOPfBHz9qJIKUwGvSk32EyyhvEFqOpDv0MX4QseXl0,40788
|
|
|
7
7
|
kisa_utils/encryption.py,sha256=KwSUtjZj6m2JqEeeg0GW3bx93PCpEwJlcBzLZrnReyE,3522
|
|
8
8
|
kisa_utils/enqueue.py,sha256=RbImgoPNFFCQHT1ow9zJEM-tHwWE1bNnHznJqEVXL9k,11290
|
|
9
9
|
kisa_utils/figures.py,sha256=ossQHBR7T9rOV1yhQJLDbwrY23xf0RIGOmjcFH7P0ss,1860
|
|
10
|
-
kisa_utils/functionUtils.py,sha256=
|
|
10
|
+
kisa_utils/functionUtils.py,sha256=v8ShUZ56yxdYLMTsxRdj4vQfk6wTo0ATqB1wD1dJYYw,5420
|
|
11
11
|
kisa_utils/log.py,sha256=EKBAVvDpY_hgALDCC6i-ARdqQzZwxBxxeHR4NsYgs9U,2120
|
|
12
12
|
kisa_utils/queues.py,sha256=D0bCtI95VEg-xLuzf-Wp0Pfjc5hoEwlmzEJHuokx-i0,5418
|
|
13
13
|
kisa_utils/remote.py,sha256=2EMG2kJudCYqpNPsACe3riQCqTsg-MzviVSZPbjCtxk,1793
|
|
@@ -19,11 +19,11 @@ kisa_utils/token.py,sha256=ReCIBsq95RMYCrDCyHgU1y_Eq-xp_PBpZiHxwsY6Fj4,8015
|
|
|
19
19
|
kisa_utils/permissions/__config__.py,sha256=i3ELkOydDnjKx2ozQTxLZdZ8DXSeUncnl2kRxANjFmM,613
|
|
20
20
|
kisa_utils/permissions/__init__.py,sha256=k7WbNlE8i9Vyf_SdbXbTh8D3gt4obDe3f8rONVVmNH4,36291
|
|
21
21
|
kisa_utils/servers/__init__.py,sha256=lPqDyGTrFo0qwPZ2WA9Xtcpc5D8AIU4huqgFx1iZf68,19
|
|
22
|
-
kisa_utils/servers/flask.py,sha256=
|
|
22
|
+
kisa_utils/servers/flask.py,sha256=niD6Cv04cs6YVMXB7MVjOQ4__78UfLLia7qHdz2FgUs,30354
|
|
23
23
|
kisa_utils/structures/__init__.py,sha256=JBU1j3A42jQ62ALKnsS1Hav9YXcYwjDw1wQJtohXPbU,83
|
|
24
24
|
kisa_utils/structures/utils.py,sha256=doZnnrKT5qGWZIOhXqBnD7mBBc7r-lhwcfpRKcK95Is,2237
|
|
25
25
|
kisa_utils/structures/validator.py,sha256=2cKaVuY6ia6-pt6o73A6L11qInDz_tygFZdsbZU-RbA,3328
|
|
26
|
-
kisa_utils-0.36.
|
|
27
|
-
kisa_utils-0.36.
|
|
28
|
-
kisa_utils-0.36.
|
|
29
|
-
kisa_utils-0.36.
|
|
26
|
+
kisa_utils-0.36.3.dist-info/METADATA,sha256=q8DInAIFbutxylvRMLc-_VibGckjlnXKlwDJv7baeBI,477
|
|
27
|
+
kisa_utils-0.36.3.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
28
|
+
kisa_utils-0.36.3.dist-info/top_level.txt,sha256=URxY4sRuqmirOxWtztpVmPoGQdksEMYO6hmYsEDGz2Y,75
|
|
29
|
+
kisa_utils-0.36.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|