CheeseAPI 1.6.24__tar.gz → 1.7.0__tar.gz
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.
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/app.py +5 -2
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/file.py +1 -1
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/handle.py +1 -1
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/request.py +1 -1
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/response.py +3 -2
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/schedule.py +14 -6
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/text.py +8 -8
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/validator.py +11 -9
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/PKG-INFO +3 -1
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/pyproject.toml +4 -2
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/.gitignore +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/__init__.py +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/cors.py +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/exception.py +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/protocol.py +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/route.py +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/server.py +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/signal.py +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/websocket.py +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/CheeseAPI/workspace.py +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/LICENSE +0 -0
- {cheeseapi-1.6.24 → cheeseapi-1.7.0}/README.md +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import multiprocessing, os, setproctitle, sys, multiprocessing.managers
|
|
2
2
|
from typing import Dict, Any, List
|
|
3
3
|
|
|
4
|
+
from dill import settings
|
|
5
|
+
|
|
4
6
|
from CheeseAPI.text import Text
|
|
5
7
|
from CheeseAPI.server import Server
|
|
6
8
|
from CheeseAPI.workspace import Workspace
|
|
@@ -10,6 +12,9 @@ from CheeseAPI.route import Route, RouteBus
|
|
|
10
12
|
from CheeseAPI.cors import Cors
|
|
11
13
|
from CheeseAPI.schedule import Scheduler
|
|
12
14
|
|
|
15
|
+
multiprocessing.set_start_method('fork', True)
|
|
16
|
+
settings['protocol'] = 5
|
|
17
|
+
|
|
13
18
|
class App:
|
|
14
19
|
def __init__(self):
|
|
15
20
|
self.manager: multiprocessing.managers.SyncManager = multiprocessing.Manager()
|
|
@@ -59,8 +64,6 @@ class App:
|
|
|
59
64
|
self.localModules.append(foldername)
|
|
60
65
|
|
|
61
66
|
def run(self):
|
|
62
|
-
multiprocessing.set_start_method('fork', True)
|
|
63
|
-
|
|
64
67
|
self._handle.server_start()
|
|
65
68
|
|
|
66
69
|
@property
|
|
@@ -48,5 +48,5 @@ class File:
|
|
|
48
48
|
|
|
49
49
|
from CheeseAPI.app import app
|
|
50
50
|
|
|
51
|
-
with open(filePath if filePath[0] == '/' else
|
|
51
|
+
with open(filePath if filePath[0] == '/' else join(app.workspace.base, filePath), 'w' if isinstance(self.data, str) else 'wb') as f:
|
|
52
52
|
f.write(self.data)
|
|
@@ -40,7 +40,7 @@ HTTP_STATUS_NOT_FOUND = HTTPStatus.NOT_FOUND
|
|
|
40
40
|
HTTP_STATUS_METHOD_NOT_ALLOWED = HTTPStatus.METHOD_NOT_ALLOWED
|
|
41
41
|
HTTP_STATUS_INTERNAL_SERVER_ERROR = HTTPStatus.INTERNAL_SERVER_ERROR
|
|
42
42
|
HTTP_STATUS_BAD_REQUEST = HTTPStatus.BAD_REQUEST
|
|
43
|
-
STATIC_INDEX_FILE_NAMES = ('', '.html', 'index.html', '/index.html')
|
|
43
|
+
STATIC_INDEX_FILE_NAMES = frozenset(('', '.html', 'index.html', '/index.html'))
|
|
44
44
|
STATIC_EXCEPTIONS = (FileNotFoundError, NotADirectoryError, IsADirectoryError)
|
|
45
45
|
logger_danger = logger.danger
|
|
46
46
|
WEBSOCKET_EXCEPTIONS = (ConnectionClosed, TimeoutError)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import http
|
|
2
2
|
from typing import Literal, Dict, List
|
|
3
3
|
from urllib.parse import unquote
|
|
4
|
-
from json import loads
|
|
5
4
|
from re import findall
|
|
6
5
|
|
|
7
6
|
from CheeseAPI.file import File
|
|
8
7
|
from xmltodict import parse
|
|
8
|
+
from orjson import loads
|
|
9
9
|
|
|
10
10
|
class Request:
|
|
11
11
|
def __init__(self, method: http.HTTPMethod | None, url: str):
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
from json import dumps
|
|
2
1
|
from typing import Callable, Dict, AsyncIterator, Tuple, overload, Literal
|
|
3
2
|
from email.utils import formatdate
|
|
4
3
|
from http import HTTPStatus
|
|
5
4
|
from time import time
|
|
6
5
|
from datetime import datetime, timedelta
|
|
7
6
|
|
|
7
|
+
from orjson import dumps
|
|
8
|
+
|
|
8
9
|
from CheeseAPI.file import File
|
|
9
10
|
|
|
10
11
|
HTTPStatus_OK = HTTPStatus.OK
|
|
@@ -465,7 +466,7 @@ class JsonResponse(BaseResponse):
|
|
|
465
466
|
'''
|
|
466
467
|
|
|
467
468
|
def __init__(self, body: dict | list = {}, status: HTTPStatus | int = HTTPStatus_OK, headers: Dict[str, str] = {}):
|
|
468
|
-
super().__init__(dumps(body), status, {
|
|
469
|
+
super().__init__(dumps(body).encode(), status, {
|
|
469
470
|
'Content-Type': 'application/json',
|
|
470
471
|
**headers
|
|
471
472
|
})
|
|
@@ -5,6 +5,7 @@ from asyncio import sleep as asyncio_sleep
|
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
from traceback import format_exc
|
|
7
7
|
|
|
8
|
+
from snappy import compress, uncompress
|
|
8
9
|
from CheeseLog import logger
|
|
9
10
|
from dill import loads, dumps
|
|
10
11
|
|
|
@@ -14,6 +15,7 @@ if TYPE_CHECKING:
|
|
|
14
15
|
logger_danger = logger.danger
|
|
15
16
|
logger_encode = logger.encode
|
|
16
17
|
datetime_now = datetime.now
|
|
18
|
+
DATA_LENGTH = 1024
|
|
17
19
|
|
|
18
20
|
class ScheduleTask:
|
|
19
21
|
def __init__(self, app: 'App', key: str):
|
|
@@ -55,13 +57,15 @@ class ScheduleTask:
|
|
|
55
57
|
|
|
56
58
|
@property
|
|
57
59
|
def fn(self) -> Callable:
|
|
58
|
-
|
|
60
|
+
fn = self._app._managers_['schedules'][self.key]['fn']
|
|
61
|
+
return loads(uncompress(memoryview(fn).toreadonly() if len(fn) >= DATA_LENGTH else fn))
|
|
59
62
|
|
|
60
63
|
@fn.setter
|
|
61
64
|
def fn(self, value: Callable):
|
|
65
|
+
value = dumps(value, recurse = True)
|
|
62
66
|
self._app._managers_['schedules'][self.key] = {
|
|
63
67
|
**self._app._managers_['schedules'][self.key],
|
|
64
|
-
'fn':
|
|
68
|
+
'fn': compress(memoryview(value).toreadonly() if len(value) >= DATA_LENGTH else value),
|
|
65
69
|
'needUpdate': True
|
|
66
70
|
}
|
|
67
71
|
|
|
@@ -209,7 +213,8 @@ class ScheduleTask:
|
|
|
209
213
|
【只读】 上一次的返回值
|
|
210
214
|
'''
|
|
211
215
|
|
|
212
|
-
|
|
216
|
+
lastReturn = self._app._managers_['schedules'][self.key]['lastReturn']
|
|
217
|
+
return loads(uncompress(memoryview(lastReturn).toreadonly() if len(lastReturn) >= DATA_LENGTH else lastReturn))
|
|
213
218
|
|
|
214
219
|
@property
|
|
215
220
|
def endTimer(self) -> datetime | None:
|
|
@@ -317,11 +322,12 @@ class Scheduler:
|
|
|
317
322
|
{logger_encode(format_exc()[:-1])}''')
|
|
318
323
|
result = None
|
|
319
324
|
|
|
325
|
+
result = dumps(result, recurse = True)
|
|
320
326
|
self._app._managers_['schedules'][task.key] = {
|
|
321
327
|
**self._app._managers_['schedules'][task.key],
|
|
322
328
|
'total_repetition_num': task.total_repetition_num + 1,
|
|
323
329
|
'lastTimer': datetime_fromtimestamp(runTime),
|
|
324
|
-
'lastReturn':
|
|
330
|
+
'lastReturn': compress(memoryview(result).toreadonly() if len(result) >= DATA_LENGTH else result)
|
|
325
331
|
}
|
|
326
332
|
queues[0].put(True)
|
|
327
333
|
|
|
@@ -415,9 +421,10 @@ class Scheduler:
|
|
|
415
421
|
startTimer = datetime_now()
|
|
416
422
|
|
|
417
423
|
if fn:
|
|
424
|
+
fn = dumps(fn, recurse = True)
|
|
418
425
|
self._app._managers_['schedules'][key] = {
|
|
419
426
|
'timer': timer,
|
|
420
|
-
'fn':
|
|
427
|
+
'fn': compress(memoryview(fn).toreadonly() if len(fn) > DATA_LENGTH else fn),
|
|
421
428
|
'startTimer': startTimer,
|
|
422
429
|
'expected_repetition_num': expected_repetition_num,
|
|
423
430
|
'total_repetition_num': 0,
|
|
@@ -432,9 +439,10 @@ class Scheduler:
|
|
|
432
439
|
return
|
|
433
440
|
|
|
434
441
|
def wrapper(fn):
|
|
442
|
+
fn = dumps(fn, recurse = True)
|
|
435
443
|
self._app._managers_['schedules'][key] = {
|
|
436
444
|
'timer': timer,
|
|
437
|
-
'fn':
|
|
445
|
+
'fn': compress(memoryview(fn).toreadonly() if len(fn) >= DATA_LENGTH else fn),
|
|
438
446
|
'startTimer': startTimer,
|
|
439
447
|
'expected_repetition_num': expected_repetition_num,
|
|
440
448
|
'total_repetition_num': 0,
|
|
@@ -49,8 +49,8 @@ Static: <cyan>{self._app.server.static}</cyan>''' if self._app.workspace.static
|
|
|
49
49
|
def loadedModules(self) -> List[Tuple[str, str]]:
|
|
50
50
|
return [
|
|
51
51
|
(f'''Modules:
|
|
52
|
-
'
|
|
53
|
-
'
|
|
52
|
+
{' | '.join(self._app.modules)}''', f'''Modules:
|
|
53
|
+
{' | '.join(self._app.modules)}''')
|
|
54
54
|
]
|
|
55
55
|
|
|
56
56
|
def loadingLocalModule(self, precent: float, module: str) -> Tuple[str, str]:
|
|
@@ -69,8 +69,8 @@ Static: <cyan>{self._app.server.static}</cyan>''' if self._app.workspace.static
|
|
|
69
69
|
|
|
70
70
|
return [
|
|
71
71
|
(f'''Local Modules:
|
|
72
|
-
'
|
|
73
|
-
'
|
|
72
|
+
{' | '.join(foldernames)}''', f'''Local Modules:
|
|
73
|
+
{' | '.join(foldernames)}''')
|
|
74
74
|
]
|
|
75
75
|
|
|
76
76
|
def worker_starting(self) -> List[Tuple[str, str]]:
|
|
@@ -85,7 +85,7 @@ Static: <cyan>{self._app.server.static}</cyan>''' if self._app.workspace.static
|
|
|
85
85
|
|
|
86
86
|
def http(self, protocol: 'HttpProtocol') -> List[Tuple[str, str]]:
|
|
87
87
|
return [
|
|
88
|
-
(f'The {protocol.request.client} accessed {protocol.request.method} {protocol.request.fullPath} and returned {protocol.response.status}', f'The <cyan>{protocol.request.client}</cyan> accessed <cyan>{protocol.request.method}
|
|
88
|
+
(f'The {protocol.request.client} accessed {protocol.request.method} {protocol.request.fullPath} and returned {protocol.response.status}', f'The <cyan>{protocol.request.client}</cyan> accessed <cyan>{protocol.request.method} {logger.encode(protocol.request.fullPath)}</cyan> and returned <blue>{protocol.response.status}</blue>')
|
|
89
89
|
]
|
|
90
90
|
|
|
91
91
|
def http_500(self, protocol: 'HttpProtocol', e: BaseException) -> List[Tuple[str, str]]:
|
|
@@ -98,7 +98,7 @@ Static: <cyan>{self._app.server.static}</cyan>''' if self._app.workspace.static
|
|
|
98
98
|
|
|
99
99
|
def websocket_response(self, protocol: 'WebsocketProtocol') -> List[Tuple[str, str]]:
|
|
100
100
|
return [
|
|
101
|
-
(f'The {protocol.request.client} accessed {protocol.request.method} {protocol.request.fullPath} and returned {protocol.response.status}', f'The <cyan>{protocol.request.client}</cyan> accessed <cyan>{protocol.request.method}
|
|
101
|
+
(f'The {protocol.request.client} accessed {protocol.request.method} {protocol.request.fullPath} and returned {protocol.response.status}', f'The <cyan>{protocol.request.client}</cyan> accessed <cyan>{protocol.request.method} {logger.encode(protocol.request.fullPath)}</cyan> and returned <blue>{protocol.response.status}</blue>')
|
|
102
102
|
]
|
|
103
103
|
|
|
104
104
|
def websocket_500(self, protocol: 'WebsocketProtocol', e: BaseException) -> List[Tuple[str, str]]:
|
|
@@ -111,12 +111,12 @@ Static: <cyan>{self._app.server.static}</cyan>''' if self._app.workspace.static
|
|
|
111
111
|
|
|
112
112
|
def websocket_connection(self, protocol: 'WebsocketProtocol') -> List[Tuple[str, str]]:
|
|
113
113
|
return [
|
|
114
|
-
(f'The {protocol.request.client} connected {protocol.request.method} {protocol.request.fullPath}', f'The <cyan>{protocol.request.client}</cyan> connected <cyan>{protocol.request.method}
|
|
114
|
+
(f'The {protocol.request.client} connected {protocol.request.method} {protocol.request.fullPath}', f'The <cyan>{protocol.request.client}</cyan> connected <cyan>{protocol.request.method} {logger.encode(protocol.request.fullPath)}</cyan>')
|
|
115
115
|
]
|
|
116
116
|
|
|
117
117
|
def websocket_disconnection(self, protocol: 'WebsocketProtocol') -> List[Tuple[str, str]]:
|
|
118
118
|
return [
|
|
119
|
-
(f'The {protocol.request.client} disconnected {protocol.request.method} {protocol.request.fullPath}', f'The <cyan>{protocol.request.client}</cyan> disconnected <cyan>{protocol.request.method}
|
|
119
|
+
(f'The {protocol.request.client} disconnected {protocol.request.method} {protocol.request.fullPath}', f'The <cyan>{protocol.request.client}</cyan> disconnected <cyan>{protocol.request.method} {logger.encode(protocol.request.fullPath)}</cyan>')
|
|
120
120
|
]
|
|
121
121
|
|
|
122
122
|
def worker_stopping(self) -> List[Tuple[str, str]]:
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
from json import loads
|
|
2
1
|
from typing import TYPE_CHECKING
|
|
3
2
|
from functools import wraps
|
|
4
3
|
|
|
5
4
|
from pydantic import BaseModel, ValidationError
|
|
5
|
+
from orjson import loads
|
|
6
6
|
|
|
7
7
|
from CheeseAPI.response import JsonResponse, Response
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from CheeseAPI.response import BaseResponse
|
|
11
11
|
|
|
12
|
-
SCOPES = ('path', 'args', 'form', 'cookie', 'headers')
|
|
13
|
-
SCOPES_JSON = ('form', 'args')
|
|
14
|
-
JSON_PREFIX = ('{', '[')
|
|
15
|
-
JSON_SUFFIX = ('}', ']')
|
|
12
|
+
SCOPES = frozenset(('path', 'args', 'form', 'cookie', 'headers'))
|
|
13
|
+
SCOPES_JSON = frozenset(('form', 'args'))
|
|
14
|
+
JSON_PREFIX = frozenset(('{', '['))
|
|
15
|
+
JSON_SUFFIX = frozenset(('}', ']'))
|
|
16
16
|
|
|
17
17
|
class ValidateError(Exception):
|
|
18
18
|
def __init__(self, response: 'BaseResponse' = Response(status = 400)):
|
|
@@ -56,6 +56,8 @@ def validator(validator: BaseModel):
|
|
|
56
56
|
def wrapper(fn):
|
|
57
57
|
@wraps(fn)
|
|
58
58
|
async def decorator(*args, **kwargs):
|
|
59
|
+
request = kwargs['request']
|
|
60
|
+
|
|
59
61
|
_kwargs = {}
|
|
60
62
|
for key in validator.model_fields.keys():
|
|
61
63
|
for scope in SCOPES:
|
|
@@ -64,11 +66,11 @@ def validator(validator: BaseModel):
|
|
|
64
66
|
_kwargs[key] = kwargs[key]
|
|
65
67
|
elif scope == 'headers':
|
|
66
68
|
_key = key.replace('_', '-')
|
|
67
|
-
if _key in
|
|
68
|
-
_kwargs[key] = getattr(
|
|
69
|
+
if _key in request.headers:
|
|
70
|
+
_kwargs[key] = getattr(request, scope)[_key]
|
|
69
71
|
else:
|
|
70
|
-
if getattr(
|
|
71
|
-
_kwargs[key] = getattr(
|
|
72
|
+
if getattr(request, scope) and key in getattr(request, scope):
|
|
73
|
+
_kwargs[key] = getattr(request, scope)[key]
|
|
72
74
|
|
|
73
75
|
if scope in SCOPES_JSON and _kwargs[key][0] in JSON_PREFIX and _kwargs[key][-1] in JSON_SUFFIX:
|
|
74
76
|
try:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: CheeseAPI
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: 一款web协程框架
|
|
5
5
|
Project-URL: Source, https://github.com/CheeseUnknown/CheeseAPI
|
|
6
6
|
Author-email: Cheese Unknown <cheese@cheese.ren>
|
|
@@ -11,7 +11,9 @@ Requires-Dist: cheesesignal==1.2.*
|
|
|
11
11
|
Requires-Dist: dill
|
|
12
12
|
Requires-Dist: email-validator
|
|
13
13
|
Requires-Dist: httptools
|
|
14
|
+
Requires-Dist: orjson
|
|
14
15
|
Requires-Dist: pydantic
|
|
16
|
+
Requires-Dist: python-snappy
|
|
15
17
|
Requires-Dist: setproctitle
|
|
16
18
|
Requires-Dist: uvloop
|
|
17
19
|
Requires-Dist: websockets
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "CheeseAPI"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.7.0"
|
|
8
8
|
description = "一款web协程框架"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license-files = { paths = [ "LICENSE" ] }
|
|
@@ -23,7 +23,9 @@ dependencies = [
|
|
|
23
23
|
"dill",
|
|
24
24
|
"pydantic",
|
|
25
25
|
"email-validator",
|
|
26
|
-
"setproctitle"
|
|
26
|
+
"setproctitle",
|
|
27
|
+
"orjson",
|
|
28
|
+
"python-snappy"
|
|
27
29
|
]
|
|
28
30
|
|
|
29
31
|
[project.urls]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|