CheeseAPI 2.0.5b1__tar.gz → 2.0.5b4__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,9 +1,10 @@
1
- import os, pathlib, multiprocessing, ssl, socket, asyncio, concurrent.futures, inspect
1
+ import os, pathlib, multiprocessing, ssl, socket, asyncio, concurrent.futures, inspect, importlib.util
2
2
  from typing import Type, Literal, Callable, AsyncIterable, TYPE_CHECKING
3
3
 
4
- import redis, signal
4
+ import signal, redis
5
5
  from CheeseLog import CheeseLogger, Message
6
6
 
7
+ from CheeseAPI import static
7
8
  from CheeseAPI.printer import Printer
8
9
  from CheeseAPI.signal import Signal
9
10
  from CheeseAPI.request import RequestProxy
@@ -31,6 +32,8 @@ class AppProxy:
31
32
  def start(self):
32
33
  signal.signal(signal.SIGTERM, lambda signum, frame: self.app.stop())
33
34
 
35
+ self._process_init()
36
+
34
37
  waiting_list = []
35
38
 
36
39
  try:
@@ -98,7 +101,31 @@ class AppProxy:
98
101
  'modules': module
99
102
  })
100
103
 
101
- for path in pathlib.Path(module.replace('.', '/')).glob('*.py'):
104
+ try:
105
+ spec = importlib.util.find_spec(module)
106
+ except (ImportError, ValueError, AttributeError):
107
+ continue
108
+
109
+ if spec is None:
110
+ continue
111
+
112
+ p = None
113
+ if spec.origin is None and spec.submodule_search_locations:
114
+ for location in spec.submodule_search_locations:
115
+ p = pathlib.Path(location)
116
+ if p.exists():
117
+ break
118
+ if not p:
119
+ continue
120
+
121
+ if spec.origin is None:
122
+ continue
123
+
124
+ origin = pathlib.Path(spec.origin)
125
+ if origin.name == '__init__.py':
126
+ origin = origin.parent
127
+
128
+ for path in origin.glob('*.py'):
102
129
  __import__(f'{module}.{path.stem}')
103
130
 
104
131
  self.after_load_module(i, module)
@@ -240,14 +267,6 @@ class AppProxy:
240
267
  loop = asyncio.get_event_loop()
241
268
  loop.set_default_executor(concurrent.futures.ThreadPoolExecutor())
242
269
 
243
- if self.app.sync_server_url:
244
- if WebsocketProxy.sync_servers is None:
245
- WebsocketProxy.sync_servers = (redis.Redis.from_url(self.app.sync_server_url), redis.asyncio.Redis.from_url(self.app.sync_server_url))
246
- if WebsocketProxy.data_encode is None:
247
- WebsocketProxy.data_encode = self.app.sync_server_data_encode
248
- if WebsocketProxy.data_decode is None:
249
- WebsocketProxy.data_decode = self.app.sync_server_data_decode
250
-
251
270
  await self.after_worker_start(is_first)
252
271
  await self.app.signal.after_worker_start.async_send(kwargs = {
253
272
  'is_first': is_first
@@ -443,10 +462,21 @@ class AppProxy:
443
462
  async def after_response(self, response: Response):
444
463
  ...
445
464
 
465
+ def _process_init(self, app: 'CheeseAPI' = None):
466
+ if not app:
467
+ app = self.app
468
+
469
+ static.websocket_data_decode = app.sync_server_data_decode
470
+ static.websocket_data_encode = app.sync_server_data_encode
471
+ if app.sync_server_url:
472
+ static.websocket_sync_servers = (redis.ConnectionPool.from_url(app.sync_server_url), redis.asyncio.ConnectionPool.from_url(app.sync_server_url))
473
+ static.websocket_sync_server = {}
474
+ app.scheduler._proxy.init(app)
475
+
446
476
  class CheeseAPI:
447
- __slots__ = ('_host', '_port', '_ipv6', '_logger_path', '_dual_stack', '_socket_backlog', '_socket_send_buffer_size', '_socket_receive_buffer_size', '_workers', '_ssl_cert', '_ssl_key', '_sync_server_url', '_static_path', '_printer', '_compress', '_compress_min_length', '_compress_level', '_manual_modules', '_exclude_modules', '_priority_modules', '_sync_server_data_encode', '_sync_server_data_decode', '_logger_messages', '_logger', '_is_running', '_request_timeout', '_keep_alive', '_keep_alive_timeout', '_keep_alive_max_requests', '_AppProxy_Class', '_RequestProxy_Class', '_proxy', '_signal', '_ResponseProxy_Class', '_RouteProxy_Class', '_route', '_WebsocketProxy_Class', '_cors', '_SchedulerProxy_Class', '_scheduler')
477
+ __slots__ = ('_host', '_port', '_ipv6', '_logger_path', '_dual_stack', '_socket_backlog', '_socket_send_buffer_size', '_socket_receive_buffer_size', '_workers', '_ssl_cert', '_ssl_key', '_sync_server_url', '_static_path', '_printer', '_compress', '_compress_min_length', '_compress_level', '_manual_modules', '_exclude_modules', '_priority_modules', '_sync_server_data_encode', '_sync_server_data_decode', '_logger_messages', '_logger', '_is_running', '_request_timeout', '_keep_alive', '_keep_alive_timeout', '_keep_alive_max_requests', '_AppProxy_Class', '_RequestProxy_Class', '_proxy', '_signal', '_ResponseProxy_Class', '_RouteProxy_Class', '_route', '_WebsocketProxy_Class', '_cors', '_SchedulerProxy_Class', '_scheduler', '_sync_server_timeout')
448
478
 
449
- def __init__(self, host: str | None = None, port: int = 5214, *, ipv6: bool = False, logger_path: str | None = None, dual_stack: bool = False, socket_backlog: int | None = None, socket_send_buffer_size: int | None = None, socket_receive_buffer_size: int | None = None, workers: int = 1, ssl_cert: str | None = None, ssl_key: str | None = None, sync_server_url: str | None = None, static_path: dict[str, str] = {}, printer: Type[Printer] = Printer, compress: list[Literal['gzip', 'br', 'zstd', 'deflate']] = ['gzip', 'br', 'zstd', 'deflate'], compress_min_length: int = 1024, compress_level: int = 6, manual_modules: list[str] = [], exclude_modules: list[str] = [], priority_modules: list[str] = [], sync_server_data_encode: Callable[[bytes], bytes] | None = None, sync_server_data_decode: Callable[[bytes], bytes] | None = None, logger_messages: dict[str, 'Message'] = {}, request_timeout: float | None = None, keep_alive: bool = True, keep_alive_timeout: float = 5, keep_alive_max_requests: int = 100, AppProxy_Class: Type[AppProxy] = AppProxy, RequestProxy_Class: Type[RequestProxy] = RequestProxy, ResponseProxy_Class: Type[ResponseProxy] = ResponseProxy, RouteProxy_Class: Type[RouteProxy] = RouteProxy, cors_allow_origins: list[str] = ['*'], cors_allow_methods: Literal['GET', 'PUT', 'POST', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD', 'CONNECT'] = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'PATCH', 'CONNECT'], cors_allow_headers: list[str] = ['*'], cors_allow_credentials: bool = True, cors_expose_headers: list[str] = [], cors_max_age: int | None = None, WebsocketProxy_Class: Type[WebsocketProxy] = WebsocketProxy, route_patterns: list['Pattern'] = [], SchedulerProxy_Class: Type[SchedulerProxy] = SchedulerProxy):
479
+ def __init__(self, host: str | None = None, port: int = 5214, *, ipv6: bool = False, logger_path: str | None = None, dual_stack: bool = False, socket_backlog: int | None = None, socket_send_buffer_size: int | None = None, socket_receive_buffer_size: int | None = None, workers: int = 1, ssl_cert: str | None = None, ssl_key: str | None = None, sync_server_url: str | None = None, static_path: dict[str, str] = {}, printer: Type[Printer] = Printer, compress: list[Literal['gzip', 'br', 'zstd', 'deflate']] = ['gzip', 'br', 'zstd', 'deflate'], compress_min_length: int = 1024, compress_level: int = 6, manual_modules: list[str] = [], exclude_modules: list[str] = [], priority_modules: list[str] = [], sync_server_data_encode: Callable[[bytes], bytes] | None = None, sync_server_data_decode: Callable[[bytes], bytes] | None = None, logger_messages: dict[str, 'Message'] = {}, request_timeout: float | None = None, keep_alive: bool = True, keep_alive_timeout: float = 5, keep_alive_max_requests: int = 100, AppProxy_Class: Type[AppProxy] = AppProxy, RequestProxy_Class: Type[RequestProxy] = RequestProxy, ResponseProxy_Class: Type[ResponseProxy] = ResponseProxy, RouteProxy_Class: Type[RouteProxy] = RouteProxy, cors_allow_origins: list[str] = ['*'], cors_allow_methods: Literal['GET', 'PUT', 'POST', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD', 'CONNECT'] = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'PATCH', 'CONNECT'], cors_allow_headers: list[str] = ['*'], cors_allow_credentials: bool = True, cors_expose_headers: list[str] = [], cors_max_age: int | None = None, WebsocketProxy_Class: Type[WebsocketProxy] = WebsocketProxy, route_patterns: list['Pattern'] = [], SchedulerProxy_Class: Type[SchedulerProxy] = SchedulerProxy, sync_server_timeout: float = 5):
450
480
  '''
451
481
  - Args
452
482
  - logger_path: 日志文件路径,支持日期格式化
@@ -478,6 +508,7 @@ class CheeseAPI:
478
508
  - WebsocketProxy_Class: 若想要对 Websocket 处理逻辑进行处理,可传入自定义的 WebsocketProxy 类
479
509
  - route_patterns: 自定义路由校验规则
480
510
  - SchedulerProxy_Class: 自定义任务调度器代理类
511
+ - sync_server_timeout: 同步服务器操作超时时间
481
512
  '''
482
513
 
483
514
  self._host: str = host if host is not None else ('::' if ipv6 else '0.0.0.0')
@@ -513,6 +544,7 @@ class CheeseAPI:
513
544
  self._RouteProxy_Class: Type[RouteProxy] = RouteProxy_Class
514
545
  self._WebsocketProxy_Class: Type[WebsocketProxy] = WebsocketProxy_Class
515
546
  self._SchedulerProxy_Class: Type[SchedulerProxy] = SchedulerProxy_Class
547
+ self._sync_server_timeout: float = sync_server_timeout
516
548
 
517
549
  self._logger: CheeseLogger = CheeseLogger(self.logger_path, messages = {
518
550
  'START': Message('START', 20, message_template_styled = '(<green>%k</green>) <black>%t</black> > %c'),
@@ -533,16 +565,10 @@ class CheeseAPI:
533
565
  self.route.patterns.extend(route_patterns)
534
566
  self.route.patterns.sort(key = lambda x: x['weight'], reverse = True)
535
567
 
536
- def __setstate__(self, state: tuple[None, dict[str, any]]):
537
- for key, value in state[1].items():
568
+ def __setstate__(self, data: tuple[None, dict[str, any]]):
569
+ for key, value in data[1].items():
538
570
  setattr(self, key, value)
539
-
540
- if self.sync_server_url and WebsocketProxy.sync_servers is None:
541
- WebsocketProxy.sync_servers = (redis.Redis.from_url(self.sync_server_url), redis.asyncio.Redis.from_url(self.sync_server_url))
542
- if WebsocketProxy.data_encode is None:
543
- WebsocketProxy.data_encode = self.sync_server_data_encode
544
- if WebsocketProxy.data_decode is None:
545
- WebsocketProxy.data_decode = self.sync_server_data_decode
571
+ self._proxy._process_init(self)
546
572
 
547
573
  def start(self):
548
574
  self._proxy.start()
@@ -829,3 +855,11 @@ class CheeseAPI:
829
855
  '''
830
856
 
831
857
  return self._SchedulerProxy_Class
858
+
859
+ @property
860
+ def sync_server_timeout(self) -> float:
861
+ '''
862
+ 同步服务器操作超时时间
863
+ '''
864
+
865
+ return self._sync_server_timeout
@@ -5,7 +5,7 @@ from CheeseAPI.response import Response
5
5
  if TYPE_CHECKING:
6
6
  from CheeseAPI.request import Request
7
7
 
8
- HTTP_METHOD_TYPE = Literal['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'PATCH']
8
+ HTTP_METHOD_TYPE = Literal['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'PATCH', 'CONNECT']
9
9
 
10
10
  class CORS:
11
11
  __slots__ = ('allow_origins', 'allow_methods', 'allow_headers', 'allow_credentials', 'expose_headers', 'max_age')
@@ -11,7 +11,7 @@ if TYPE_CHECKING:
11
11
  HTTP_METHOD_TYPE = Literal['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE', 'WEBSOCKET']
12
12
 
13
13
  class Request:
14
- __slots__ = ('_proxy', '_ip', '_method', '_path', '_params', '_headers', '_query', '_body', '_json', '_form', '_files', '_cookies', '_full_path', '_ranges', '_fn')
14
+ __slots__ = ('_proxy', '_ip', '_method', '_path', '_params', '_headers', '_query', '_body', '_json', '_form', '_files', '_cookies', '_full_path', '_ranges', '_fn', '_file')
15
15
 
16
16
  def __init__(self, app: 'CheeseAPI', client_socket: socket.socket, addr: tuple[str, int]):
17
17
  self._proxy: RequestProxy = app.RequestProxy_Class(app, self, client_socket)
@@ -26,6 +26,7 @@ class Request:
26
26
  self._json: dict | list | None = None
27
27
  self._form: dict[str, str] | None = None
28
28
  self._files: dict[str, File] | None = None
29
+ self._file: File | None = None
29
30
  self._cookies: dict[str, str] | None = None
30
31
  self._full_path: str | None = None
31
32
  self._ranges: list[tuple[int, int | None]] | None = None
@@ -93,6 +94,10 @@ class Request:
93
94
  def files(self) -> dict[str, bytes] | None:
94
95
  return self._files
95
96
 
97
+ @property
98
+ def file(self) -> File | None:
99
+ return self._file
100
+
96
101
  @property
97
102
  def cookies(self) -> dict[str, str] | None:
98
103
  return self._cookies
@@ -183,6 +188,7 @@ class RequestProxy:
183
188
  data = None
184
189
  try:
185
190
  data = await asyncio.wait_for(loop.sock_recv(self.client_socket, self.app.socket_receive_buffer_size), self.app.request_timeout)
191
+ self.request._body += data
186
192
  except asyncio.TimeoutError:
187
193
  raise
188
194
  if not data: