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.
@@ -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 os.path.join(app.workspace.base, filePath), 'w' if isinstance(self.data, str) else 'wb') as f:
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
- return loads(self._app._managers_['schedules'][self.key]['fn'])
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': dumps(value, recurse = True),
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
- return loads(self._app._managers_['schedules'][self.key]['lastReturn'])
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': dumps(result, recurse = True)
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': dumps(fn, recurse = True),
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': dumps(fn, recurse = True),
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
- ''' + ' | '.join(self._app.modules), f'''Modules:
53
- ''' + ' | '.join(self._app.modules))
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
- ''' + ' | '.join(foldernames), f'''Local Modules:
73
- ''' + ' | '.join(foldernames))
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} ' + logger.encode(protocol.request.fullPath) + f'</cyan> and returned <blue>{protocol.response.status}</blue>')
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} ' + logger.encode(protocol.request.fullPath) + f'</cyan> and returned <blue>{protocol.response.status}</blue>')
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} ' + logger.encode(protocol.request.fullPath) + f'</cyan>')
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} ' + logger.encode(protocol.request.fullPath) + f'</cyan>')
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 kwargs['request'].headers:
68
- _kwargs[key] = getattr(kwargs['request'], scope)[_key]
69
+ if _key in request.headers:
70
+ _kwargs[key] = getattr(request, scope)[_key]
69
71
  else:
70
- if getattr(kwargs['request'], scope) and key in getattr(kwargs['request'], scope):
71
- _kwargs[key] = getattr(kwargs['request'], scope)[key]
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.6.24
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.6.24"
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