CheeseAPI 0.0.1__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-0.0.1/CheeseAPI/__init__.py +11 -0
- CheeseAPI-0.0.1/CheeseAPI/app.py +420 -0
- CheeseAPI-0.0.1/CheeseAPI/cSignal.py +38 -0
- CheeseAPI-0.0.1/CheeseAPI/exception.py +17 -0
- CheeseAPI-0.0.1/CheeseAPI/file.py +66 -0
- CheeseAPI-0.0.1/CheeseAPI/module.py +59 -0
- CheeseAPI-0.0.1/CheeseAPI/request.py +99 -0
- CheeseAPI-0.0.1/CheeseAPI/response.py +384 -0
- CheeseAPI-0.0.1/CheeseAPI/route.py +140 -0
- CheeseAPI-0.0.1/CheeseAPI/server.py +100 -0
- CheeseAPI-0.0.1/CheeseAPI/system.py +22 -0
- CheeseAPI-0.0.1/CheeseAPI/websocket.py +46 -0
- CheeseAPI-0.0.1/CheeseAPI/workspace.py +9 -0
- CheeseAPI-0.0.1/CheeseAPI.egg-info/PKG-INFO +286 -0
- CheeseAPI-0.0.1/CheeseAPI.egg-info/SOURCES.txt +21 -0
- CheeseAPI-0.0.1/CheeseAPI.egg-info/dependency_links.txt +1 -0
- CheeseAPI-0.0.1/CheeseAPI.egg-info/requires.txt +4 -0
- CheeseAPI-0.0.1/CheeseAPI.egg-info/top_level.txt +1 -0
- CheeseAPI-0.0.1/LICENSE +21 -0
- CheeseAPI-0.0.1/PKG-INFO +286 -0
- CheeseAPI-0.0.1/README.md +271 -0
- CheeseAPI-0.0.1/setup.cfg +4 -0
- CheeseAPI-0.0.1/setup.py +29 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import os, sys
|
|
2
|
+
|
|
3
|
+
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
|
4
|
+
|
|
5
|
+
from app import App
|
|
6
|
+
from route import Route
|
|
7
|
+
from response import Response, JsonResponse, RedirectResponse, FileResponse
|
|
8
|
+
from request import Request
|
|
9
|
+
from file import File, MediaFile
|
|
10
|
+
from websocket import websocket
|
|
11
|
+
from cSignal import signal, Signal
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import os, time, inspect, traceback, multiprocessing
|
|
2
|
+
from typing import Callable, AsyncIterator, Dict, List, Any, Set
|
|
3
|
+
from multiprocessing.process import BaseProcess
|
|
4
|
+
|
|
5
|
+
import uvicorn, CheeseLog, asyncio
|
|
6
|
+
from CheeseLog import logger, Logger
|
|
7
|
+
|
|
8
|
+
import exception
|
|
9
|
+
from route import Route, matchPath
|
|
10
|
+
from request import Request
|
|
11
|
+
from response import Response, BaseResponse, FileResponse
|
|
12
|
+
from file import File
|
|
13
|
+
from websocket import websocket
|
|
14
|
+
from module import LocalModule, Module
|
|
15
|
+
from cSignal import signal
|
|
16
|
+
|
|
17
|
+
class App:
|
|
18
|
+
def __init__(self):
|
|
19
|
+
from system import System
|
|
20
|
+
from workspace import Workspace
|
|
21
|
+
from server import Server
|
|
22
|
+
|
|
23
|
+
self.process: BaseProcess = multiprocessing.current_process()
|
|
24
|
+
self.logger: Logger = logger
|
|
25
|
+
|
|
26
|
+
self.system: System = System()
|
|
27
|
+
self.workspace: Workspace = Workspace()
|
|
28
|
+
self.server: Server = Server(self)
|
|
29
|
+
self.modules: Set[Module] | Set[str] = set()
|
|
30
|
+
self.localModules: Set[LocalModule] | Set[str] | bool = True
|
|
31
|
+
|
|
32
|
+
self.route: Route = Route('')
|
|
33
|
+
|
|
34
|
+
self.server_startingHandles: List[Callable] = []
|
|
35
|
+
self.server_endingHandles: List[Callable] = []
|
|
36
|
+
# http
|
|
37
|
+
self.http_response404Handles: List[Callable] = []
|
|
38
|
+
self.http_response405Handles: List[Callable] = []
|
|
39
|
+
self.http_response500Handles: List[Callable] = []
|
|
40
|
+
self.http_beforeRequestHandles: List[Callable] = []
|
|
41
|
+
self.http_afterResponseHandles: List[Callable] = []
|
|
42
|
+
# websocket
|
|
43
|
+
self.websocket_beforeConnectionHandles: List[Callable] = []
|
|
44
|
+
self.websocket_afterDisconnectHandles: List[Callable] = []
|
|
45
|
+
self.websocket_errorHandles: List[Callable] = []
|
|
46
|
+
self.websocket_notFoundHandles: List[Callable] = []
|
|
47
|
+
|
|
48
|
+
signal.register('server_startingHandle')
|
|
49
|
+
signal.register('server_endingHandle')
|
|
50
|
+
signal.register('http_response404Handle')
|
|
51
|
+
signal.register('http_response405Handle')
|
|
52
|
+
signal.register('http_response500Handle')
|
|
53
|
+
signal.register('http_beforeRequestHandle')
|
|
54
|
+
signal.register('http_afterResponseHandle')
|
|
55
|
+
signal.register('websocket_beforeConnectionHandle')
|
|
56
|
+
signal.register('websocket_afterDisconnectHandle')
|
|
57
|
+
signal.register('websocket_errorHandle')
|
|
58
|
+
signal.register('websocket_notFoundHandle')
|
|
59
|
+
|
|
60
|
+
if self.process.name != 'MainProcess':
|
|
61
|
+
self.init()
|
|
62
|
+
|
|
63
|
+
async def __call__(self, scope, receive, send):
|
|
64
|
+
''' Server started '''
|
|
65
|
+
if scope['type'] == 'lifespan':
|
|
66
|
+
message = await receive()
|
|
67
|
+
if message['type'] == 'lifespan.startup':
|
|
68
|
+
startTime = time.time() - self.startTimer
|
|
69
|
+
if self.process.name == 'MainProcess':
|
|
70
|
+
CheeseLog.starting('The server started, took {:.6f} seconds'.format(startTime), 'The server started, took \033[34m{:.6f}\033[0m seconds'.format(startTime))
|
|
71
|
+
else:
|
|
72
|
+
CheeseLog.starting(f'The worker {os.getpid()} started, took ' + '{:.6f} seconds'.format(startTime), f'The worker \033[34m{os.getpid()}\033[0m started, took ' + '\033[34m{:.6f}\033[0m seconds'.format(startTime))
|
|
73
|
+
|
|
74
|
+
''' Http request '''
|
|
75
|
+
if scope['type'] in [ 'http', 'https' ]:
|
|
76
|
+
try:
|
|
77
|
+
timer = time.time()
|
|
78
|
+
body = b''
|
|
79
|
+
bodyFlag = True
|
|
80
|
+
while bodyFlag:
|
|
81
|
+
message = await receive()
|
|
82
|
+
body += message.get('body', b'')
|
|
83
|
+
bodyFlag = message.get('more_body', False)
|
|
84
|
+
request = Request(scope, body)
|
|
85
|
+
response = None
|
|
86
|
+
requestFunc = None
|
|
87
|
+
|
|
88
|
+
# Static
|
|
89
|
+
if self.server.STATIC_PATH and request.path.startswith(self.server.STATIC_PATH):
|
|
90
|
+
try:
|
|
91
|
+
response = FileResponse(File(path = self.workspace.STATIC_PATH + request.path[len(self.server.STATIC_PATH):]))
|
|
92
|
+
except:
|
|
93
|
+
...
|
|
94
|
+
|
|
95
|
+
if not isinstance(response, FileResponse):
|
|
96
|
+
# 404
|
|
97
|
+
requestFunc, kwargs = matchPath(request.path)
|
|
98
|
+
kwargs['request'] = request
|
|
99
|
+
if not isinstance(requestFunc, dict):
|
|
100
|
+
if signal.receiver('http_response404Handle'):
|
|
101
|
+
await signal.send_async('http_response404Handle', kwargs)
|
|
102
|
+
for http_response404Handle in self.http_response404Handles:
|
|
103
|
+
_response = await self.doFunc(http_response404Handle, kwargs)
|
|
104
|
+
if isinstance(_response, BaseResponse):
|
|
105
|
+
response = _response
|
|
106
|
+
if not isinstance(response, BaseResponse):
|
|
107
|
+
response = Response(status = 404)
|
|
108
|
+
|
|
109
|
+
# 405
|
|
110
|
+
elif request.method not in requestFunc:
|
|
111
|
+
if signal.receiver('http_response405Handle'):
|
|
112
|
+
await signal.send_async('http_response405Handle', kwargs)
|
|
113
|
+
for http_response405Handle in self.http_response405Handles:
|
|
114
|
+
_response = await self.doFunc(http_response405Handle, kwargs)
|
|
115
|
+
if isinstance(_response, BaseResponse):
|
|
116
|
+
response = _response
|
|
117
|
+
if not isinstance(response, BaseResponse):
|
|
118
|
+
response = Response(status = 405)
|
|
119
|
+
|
|
120
|
+
# Other...
|
|
121
|
+
else:
|
|
122
|
+
if signal.receiver('http_beforeRequestHandle'):
|
|
123
|
+
await signal.send_async('http_beforeRequestHandle', kwargs)
|
|
124
|
+
for http_beforeRequestHandle in self.http_beforeRequestHandles:
|
|
125
|
+
_response = await self.doFunc(http_beforeRequestHandle, kwargs)
|
|
126
|
+
if isinstance(_response, BaseResponse):
|
|
127
|
+
response = _response
|
|
128
|
+
|
|
129
|
+
if not isinstance(response, BaseResponse):
|
|
130
|
+
requestFunc = requestFunc[request.method]
|
|
131
|
+
response = await self.doFunc(requestFunc, kwargs)
|
|
132
|
+
|
|
133
|
+
if isinstance(response, BaseResponse):
|
|
134
|
+
if signal.receiver('http_afterResponseHandle'):
|
|
135
|
+
await signal.send_async('http_afterResponseHandle', kwargs)
|
|
136
|
+
for http_afterResponseHandle in self.http_afterResponseHandles:
|
|
137
|
+
kwargs['response'] = response
|
|
138
|
+
_response = await self.doFunc(http_afterResponseHandle, kwargs)
|
|
139
|
+
if isinstance(_response, BaseResponse):
|
|
140
|
+
response = _response
|
|
141
|
+
else:
|
|
142
|
+
CheeseLog.danger(f'The error occured while accessing the {request.method} {request.fullPath}\nTraceback (most recent call last):\n File "{inspect.getsourcefile(requestFunc)}", line {inspect.getsourcelines(requestFunc)[1]}, in <module>\n Function {requestFunc.__name__} needs to return Response, not {response.__class__.__name__}')
|
|
143
|
+
response = Response(status = 500)
|
|
144
|
+
except Exception as e:
|
|
145
|
+
CheeseLog.danger(f'The error occured while accessing the {request.method} {request.fullPath}\n{traceback.format_exc()}'[:-1], f'The error occured while accessing the \033[36m{request.method} {request.fullPath}\033[0m\n{traceback.format_exc()}'[:-1])
|
|
146
|
+
if signal.receiver('http_response500Handle'):
|
|
147
|
+
await signal.send_async('http_response500Handle', kwargs)
|
|
148
|
+
for http_response500Handle in self.http_response500Handles:
|
|
149
|
+
kwargs['exception'] = e
|
|
150
|
+
_response = await self.doFunc(http_response500Handle, kwargs)
|
|
151
|
+
if isinstance(_response, BaseResponse):
|
|
152
|
+
response = _response
|
|
153
|
+
if not isinstance(response, BaseResponse):
|
|
154
|
+
response = Response(status = 500)
|
|
155
|
+
|
|
156
|
+
headers = []
|
|
157
|
+
for key, value in response.headers.items():
|
|
158
|
+
headers.append([key.encode(), value.encode()])
|
|
159
|
+
await send({
|
|
160
|
+
'type': 'http.response.start',
|
|
161
|
+
'status': response.status,
|
|
162
|
+
'headers': headers
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
body = response.body
|
|
166
|
+
if isinstance(response.body, Callable):
|
|
167
|
+
body = response.body()
|
|
168
|
+
if isinstance(body, AsyncIterator):
|
|
169
|
+
try:
|
|
170
|
+
async for value in body:
|
|
171
|
+
if isinstance(body, bytes):
|
|
172
|
+
await send({
|
|
173
|
+
'type': 'http.response.body',
|
|
174
|
+
'body': value,
|
|
175
|
+
'more_body': True
|
|
176
|
+
})
|
|
177
|
+
else:
|
|
178
|
+
await send({
|
|
179
|
+
'type': 'http.response.body',
|
|
180
|
+
'body': str(value).encode(),
|
|
181
|
+
'more_body': True
|
|
182
|
+
})
|
|
183
|
+
except Exception as e:
|
|
184
|
+
await send({
|
|
185
|
+
'type': 'http.response.body',
|
|
186
|
+
'body': b''
|
|
187
|
+
})
|
|
188
|
+
else:
|
|
189
|
+
if isinstance(body, bytes):
|
|
190
|
+
await send({
|
|
191
|
+
'type': 'http.response.body',
|
|
192
|
+
'body': body
|
|
193
|
+
})
|
|
194
|
+
else:
|
|
195
|
+
await send({
|
|
196
|
+
'type': 'http.response.body',
|
|
197
|
+
'body': str(body).encode()
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
diffTime = time.time() - timer
|
|
201
|
+
CheeseLog.http(f'{request.ip} accessed {request.method} {request.fullPath}, returned {response.status}, took ' + '{:.6f}'.format(diffTime) + ' seconds', f'{request.ip} accessed \033[36m{request.method} {request.fullPath}\033[0m, returned \033[34m{response.status}\033[0m, took ' + '\033[34m{:.6f}\033[0m'.format(diffTime) + ' seconds')
|
|
202
|
+
|
|
203
|
+
''' Websocket '''
|
|
204
|
+
if scope['type'] in [ 'websocket', 'websockets' ]:
|
|
205
|
+
try:
|
|
206
|
+
request = Request(scope)
|
|
207
|
+
requestFunc, kwargs = matchPath(request.path)
|
|
208
|
+
kwargs['request'] = request
|
|
209
|
+
if requestFunc is None or 'WEBSOCKET' not in requestFunc:
|
|
210
|
+
for websocket_notFoundHandle in self.websocket_notFoundHandles:
|
|
211
|
+
await self.doFunc(websocket_notFoundHandle, kwargs)
|
|
212
|
+
return
|
|
213
|
+
requestFunc = requestFunc['WEBSOCKET']
|
|
214
|
+
|
|
215
|
+
if signal.receiver('websocket_beforeConnectionHandle'):
|
|
216
|
+
await signal.send_async('websocket_beforeConnectionHandle', kwargs)
|
|
217
|
+
for websocket_beforeConnectionHandle in self.websocket_beforeConnectionHandles:
|
|
218
|
+
await self.doFunc(websocket_beforeConnectionHandle, kwargs)
|
|
219
|
+
|
|
220
|
+
await send({
|
|
221
|
+
'type': 'websocket.accept'
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
if (await receive())['type'] == 'websocket.connect':
|
|
225
|
+
CheeseLog.websocket(f'{request.ip} connected {request.path}', f'{request.ip} connected \033[36m{request.path}\033[0m')
|
|
226
|
+
|
|
227
|
+
websocket._CLIENTS[request.sid] = asyncio.Queue()
|
|
228
|
+
async def sendHandle():
|
|
229
|
+
try:
|
|
230
|
+
while True:
|
|
231
|
+
await (await websocket._CLIENTS[request.sid].get())(send)
|
|
232
|
+
except asyncio.CancelledError:
|
|
233
|
+
...
|
|
234
|
+
task = asyncio.create_task(sendHandle())
|
|
235
|
+
|
|
236
|
+
while True:
|
|
237
|
+
message = await receive()
|
|
238
|
+
if message['type'] == 'websocket.receive':
|
|
239
|
+
if 'text' in message:
|
|
240
|
+
kwargs['value'] = message['text']
|
|
241
|
+
elif 'bytes' in message:
|
|
242
|
+
kwargs['value'] = message['bytes']
|
|
243
|
+
await self.doFunc(requestFunc, kwargs)
|
|
244
|
+
elif message['type'] == 'websocket.disconnect':
|
|
245
|
+
del websocket._CLIENTS[request.sid]
|
|
246
|
+
task.cancel()
|
|
247
|
+
await task
|
|
248
|
+
CheeseLog.websocket(f'{request.ip} disconnected {request.path}', f'{request.ip} disconnected \033[36m{request.path}\033[0m')
|
|
249
|
+
break
|
|
250
|
+
|
|
251
|
+
if signal.receiver('websocket_afterDisconnectHandle'):
|
|
252
|
+
await signal.send_async('websocket_afterDisconnectHandle', kwargs)
|
|
253
|
+
for websocket_afterDisconnectHandle in self.websocket_afterDisconnectHandles:
|
|
254
|
+
await self.doFunc(websocket_afterDisconnectHandle, kwargs)
|
|
255
|
+
except Exception as e:
|
|
256
|
+
CheeseLog.danger(f'The error occured while accessing the WEBSOCKET {request.fullPath}\n{traceback.format_exc()}'[:-1], f'The error occured while accessing the \033[36mWEBSOCKET {request.fullPath}\033[0m\n{traceback.format_exc()}'[:-1])
|
|
257
|
+
for websocket_errorHandle in self.websocket_errorHandles:
|
|
258
|
+
kwargs['exception'] = e
|
|
259
|
+
await self.doFunc(websocket_errorHandle, kwargs)
|
|
260
|
+
|
|
261
|
+
def init(self):
|
|
262
|
+
self.startTimer: float = time.time()
|
|
263
|
+
|
|
264
|
+
_modules = set()
|
|
265
|
+
if self.process.name == 'MainProcess' and len(self.modules):
|
|
266
|
+
CheeseLog.starting(f'Modules:\n{" | ".join(self.modules)}')
|
|
267
|
+
for module in self.modules:
|
|
268
|
+
_modules.add(Module(_modules, module))
|
|
269
|
+
self.modules = _modules
|
|
270
|
+
|
|
271
|
+
if self.localModules is True:
|
|
272
|
+
self.localModules = set()
|
|
273
|
+
for folderName in os.listdir(self.workspace.BASE_PATH):
|
|
274
|
+
if folderName[0] == '.':
|
|
275
|
+
continue
|
|
276
|
+
folderPath = os.path.join(self.workspace.BASE_PATH, folderName)
|
|
277
|
+
if os.path.isdir(folderPath) and folderPath not in [ self.workspace.BASE_PATH + self.workspace.STATIC_PATH[:-1], self.workspace.BASE_PATH + self.workspace.MEDIA_PATH[:-1], self.workspace.BASE_PATH + self.workspace.LOG_PATH[:-1], self.workspace.BASE_PATH + '/__pycache__' ]:
|
|
278
|
+
self.localModules.add(folderName)
|
|
279
|
+
if self.process.name == 'MainProcess' and len(self.localModules):
|
|
280
|
+
CheeseLog.starting(f'Local modules:\n{" | ".join(self.localModules)}')
|
|
281
|
+
_localModules = set()
|
|
282
|
+
for module in self.localModules:
|
|
283
|
+
_localModules.add(LocalModule(self.workspace.BASE_PATH, module))
|
|
284
|
+
self.localModules = _localModules
|
|
285
|
+
|
|
286
|
+
def run(self):
|
|
287
|
+
global app
|
|
288
|
+
app = self
|
|
289
|
+
|
|
290
|
+
CheeseLog.starting(f'Started CheeseAPI master process {os.getpid()}', f'Started CheeseAPI master process \033[34m{os.getpid()}\033[0m')
|
|
291
|
+
CheeseLog.starting('The application starts loading...')
|
|
292
|
+
|
|
293
|
+
CheeseLog.starting('''System information:
|
|
294
|
+
system: ''' + {
|
|
295
|
+
'WINDOWS': 'Windows',
|
|
296
|
+
'LINUX': 'Linux',
|
|
297
|
+
'MACOS': 'MacOS',
|
|
298
|
+
'OTHER': 'Other'
|
|
299
|
+
}[self.system.SYSTEM.value] + f'''
|
|
300
|
+
python version: {self.system.PYTHON_VERSION}''' + (f'''
|
|
301
|
+
CheeseAPI version: {self.system.CHEESEAPI_VERSION}''' if self.system.CHEESEAPI_VERSION is not None else ''))
|
|
302
|
+
|
|
303
|
+
CheeseLog.starting(f'''Workspace information:
|
|
304
|
+
CheeseAPI path: {self.workspace.CHEESEAPI_PATH}
|
|
305
|
+
base path: {self.workspace.BASE_PATH}''' + (f'''
|
|
306
|
+
static path: .{self.workspace.STATIC_PATH}''' if self.server.STATIC_PATH is not False else '') + f'''
|
|
307
|
+
media path: .{self.workspace.MEDIA_PATH}''' + (f'''
|
|
308
|
+
log path: .{self.workspace.LOG_PATH}''' if self.server.LOG_FILENAME is not False else ''), f'''Workspace information:
|
|
309
|
+
CheeseAPI path: \033[4;36m{self.workspace.CHEESEAPI_PATH}\033[0m
|
|
310
|
+
base path: \033[4;36m{self.workspace.BASE_PATH}\033[0m''' + (f'''
|
|
311
|
+
static path: \033[4;36m.{self.workspace.STATIC_PATH}\033[0m''' if self.server.STATIC_PATH is not False else '') + f'''
|
|
312
|
+
media path: \033[4;36m.{self.workspace.MEDIA_PATH}\033[0m''' + (f'''
|
|
313
|
+
log path: \033[4;36m.{self.workspace.LOG_PATH}\033[0m''' if self.server.LOG_FILENAME is not False else ''))
|
|
314
|
+
|
|
315
|
+
CheeseLog.starting(f'''Server information:
|
|
316
|
+
host: {self.server.HOST}
|
|
317
|
+
port: {self.server.PORT}
|
|
318
|
+
workers: {self.server.WORKERS}
|
|
319
|
+
is reload: {self.server.IS_RELOAD}
|
|
320
|
+
is debug: {self.server.IS_DEBUG}
|
|
321
|
+
is request logged: {self.server.IS_REQUEST_LOGGED}''' + (f'''
|
|
322
|
+
static path: {self.server.STATIC_PATH}''' if self.server.STATIC_PATH is not False else '') + (f'''
|
|
323
|
+
current log file path: .{logger.filePath[len(self.workspace.BASE_PATH):]}''' if self.server.LOG_FILENAME is not False else ''), f'''Server information:
|
|
324
|
+
host: \033[36m{self.server.HOST}\033[0m
|
|
325
|
+
port: \033[34m{self.server.PORT}\033[0m
|
|
326
|
+
workers: \033[34m{self.server.WORKERS}\033[0m
|
|
327
|
+
is reload: \033[34m{self.server.IS_RELOAD}\033[0m
|
|
328
|
+
is debug: \033[34m{self.server.IS_DEBUG}\033[0m
|
|
329
|
+
is request logged: \033[34m{self.server.IS_REQUEST_LOGGED}\033[0m''' + (f'''
|
|
330
|
+
static path: \033[34m{self.server.STATIC_PATH}\033[0m''' if self.server.STATIC_PATH is not False else '') + (f'''
|
|
331
|
+
current log file path: \033[4;36m.{logger.filePath[len(self.workspace.BASE_PATH):]}\033[0m''' if self.server.LOG_FILENAME is not False else ''))
|
|
332
|
+
|
|
333
|
+
self.init()
|
|
334
|
+
|
|
335
|
+
CheeseLog.starting(f'The server running on http://{self.server.HOST}:{self.server.PORT}', f'The server running on \033[4;36mhttp://{self.server.HOST}:{self.server.PORT}\033[0m')
|
|
336
|
+
|
|
337
|
+
if signal.receiver('server_startingHandle'):
|
|
338
|
+
signal.send('server_startingHandle')
|
|
339
|
+
for server_startingHandle in self.server_startingHandles:
|
|
340
|
+
server_startingHandle()
|
|
341
|
+
|
|
342
|
+
uvicorn.run(
|
|
343
|
+
'app:app',
|
|
344
|
+
host = self.server.HOST,
|
|
345
|
+
port = self.server.PORT,
|
|
346
|
+
reload = self.server.IS_RELOAD,
|
|
347
|
+
workers = self.server.WORKERS,
|
|
348
|
+
log_level = 'critical'
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
if signal.receiver('server_endingHandle'):
|
|
352
|
+
signal.send('server_endingHandle')
|
|
353
|
+
for server_endingHandle in self.server_endingHandles:
|
|
354
|
+
server_endingHandle()
|
|
355
|
+
|
|
356
|
+
print('')
|
|
357
|
+
CheeseLog.ending('The application tries to stop...')
|
|
358
|
+
runningTime = time.time() - self.startTimer
|
|
359
|
+
endingMessage = 'The application stopped, running '
|
|
360
|
+
endingColorfulMessage = 'The application stopped, running '
|
|
361
|
+
days = int(runningTime // 86400)
|
|
362
|
+
if days:
|
|
363
|
+
endingMessage += f'{days} days '
|
|
364
|
+
endingColorfulMessage += f'\033[34m{days}\033[0m days '
|
|
365
|
+
hours = int(runningTime % 24 // 3600)
|
|
366
|
+
if days or hours:
|
|
367
|
+
endingMessage += f'{hours} hours '
|
|
368
|
+
endingColorfulMessage += f'\033[34m{hours}\033[0m hours '
|
|
369
|
+
minutes = int(runningTime % 3600 // 60)
|
|
370
|
+
if days or hours or minutes:
|
|
371
|
+
endingMessage += f'{minutes} minutes '
|
|
372
|
+
endingColorfulMessage += f'\033[34m{minutes}\033[0m minutes '
|
|
373
|
+
endingMessage += '{:.6f} seconds'.format(runningTime % 60)
|
|
374
|
+
endingColorfulMessage += '\033[34m{:.6f}\033[0m seconds'.format(runningTime % 60)
|
|
375
|
+
CheeseLog.ending(endingMessage, endingColorfulMessage)
|
|
376
|
+
if self.logger.is_alive():
|
|
377
|
+
self.logger.stop()
|
|
378
|
+
|
|
379
|
+
async def doFunc(self, func: Callable, kwargs: Dict[str, Any] = {}):
|
|
380
|
+
_kwargs = {}
|
|
381
|
+
sig = inspect.signature(func)
|
|
382
|
+
for key, value in kwargs.items():
|
|
383
|
+
if key in sig.parameters or 'kwargs' in sig.parameters:
|
|
384
|
+
_kwargs[key] = value
|
|
385
|
+
if inspect.iscoroutinefunction(func):
|
|
386
|
+
return await func(**_kwargs)
|
|
387
|
+
else:
|
|
388
|
+
return func(**_kwargs)
|
|
389
|
+
|
|
390
|
+
def server_startingHandle(self, func: Callable):
|
|
391
|
+
self.server_startingHandles.append(func)
|
|
392
|
+
|
|
393
|
+
def server_endingHandle(self, func: Callable):
|
|
394
|
+
self.server_endingHandles.append(func)
|
|
395
|
+
|
|
396
|
+
def http_response404Handle(self, func: Callable):
|
|
397
|
+
self.http_response404Handles.append(func)
|
|
398
|
+
|
|
399
|
+
def http_response405Handle(self, func: Callable):
|
|
400
|
+
self.http_response405Handles.append(func)
|
|
401
|
+
|
|
402
|
+
def http_response500Handle(self, func: Callable):
|
|
403
|
+
self.http_response500Handles.append(func)
|
|
404
|
+
|
|
405
|
+
def http_beforeRequestHandle(self, func: Callable):
|
|
406
|
+
self.http_beforeRequestHandles.append(func)
|
|
407
|
+
|
|
408
|
+
def http_afterResponseHandle(self, func: Callable):
|
|
409
|
+
self.http_afterResponseHandles.append(func)
|
|
410
|
+
|
|
411
|
+
def websocket_beforeConnectionHandle(self, func: Callable):
|
|
412
|
+
self.websocket_beforeConnectionHandles.append(func)
|
|
413
|
+
|
|
414
|
+
def websocket_afterDisconnectHandle(self, func: Callable):
|
|
415
|
+
self.websocket_afterDisconnectHandles.append(func)
|
|
416
|
+
|
|
417
|
+
def websocket_errorHandle(self, func: Callable):
|
|
418
|
+
self.websocket_errorHandles.append(func)
|
|
419
|
+
|
|
420
|
+
app: App | None = None
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
import blinker
|
|
4
|
+
|
|
5
|
+
class Signal:
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self._values: Dict[str, blinker.NamedSignal] = {}
|
|
8
|
+
|
|
9
|
+
def register(self, name: str):
|
|
10
|
+
if name in self._values:
|
|
11
|
+
raise KeyError('this name has been registered')
|
|
12
|
+
|
|
13
|
+
self._values[name] = blinker.signal(name)
|
|
14
|
+
|
|
15
|
+
def connect(self, name: str):
|
|
16
|
+
if name not in self._values:
|
|
17
|
+
raise KeyError('no signal with this name')
|
|
18
|
+
|
|
19
|
+
def decorator(func):
|
|
20
|
+
self._values[name].connect(func, weak = False)
|
|
21
|
+
return decorator
|
|
22
|
+
|
|
23
|
+
def receiver(self, name: str):
|
|
24
|
+
if name not in self._values:
|
|
25
|
+
raise KeyError('no signal with this name')
|
|
26
|
+
return self._values[name].receivers
|
|
27
|
+
|
|
28
|
+
def send(self, name: str, *args, **kwargs):
|
|
29
|
+
if name not in self._values:
|
|
30
|
+
raise KeyError('no signal with this name')
|
|
31
|
+
self._values[name].send(*args, **kwargs)
|
|
32
|
+
|
|
33
|
+
async def send_async(self, name: str, *args, **kwargs):
|
|
34
|
+
if name not in self._values:
|
|
35
|
+
raise KeyError('no signal with this name')
|
|
36
|
+
await self._values[name].send_async(*args, **kwargs)
|
|
37
|
+
|
|
38
|
+
signal = Signal()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import sys, traceback, threading
|
|
2
|
+
|
|
3
|
+
from CheeseLog import error, danger
|
|
4
|
+
|
|
5
|
+
def sysException(*args, **kwargs):
|
|
6
|
+
try:
|
|
7
|
+
raise args[1]
|
|
8
|
+
except:
|
|
9
|
+
error(f'The error occured while the program running:\n{traceback.format_exc()}'[:-1])
|
|
10
|
+
sys.excepthook = sysException
|
|
11
|
+
|
|
12
|
+
def threadException(*args, **kwargs):
|
|
13
|
+
try:
|
|
14
|
+
raise args[0][1]
|
|
15
|
+
except:
|
|
16
|
+
danger(f'The error occured while the program running:\n{traceback.format_exc()}'[:-1])
|
|
17
|
+
threading.excepthook = threadException
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import overload
|
|
3
|
+
|
|
4
|
+
import CheeseLog
|
|
5
|
+
|
|
6
|
+
class File:
|
|
7
|
+
@overload
|
|
8
|
+
def __init__(self, path: str):
|
|
9
|
+
...
|
|
10
|
+
|
|
11
|
+
@overload
|
|
12
|
+
def __init__(self, name: str, data: bytes):
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
def __init__(self, name: str | None = None, data: bytes | None = None, path: str | None = None):
|
|
16
|
+
from app import app
|
|
17
|
+
|
|
18
|
+
self.name: str | None = name
|
|
19
|
+
self.data: bytes | None = data
|
|
20
|
+
self.path: str | None = path
|
|
21
|
+
|
|
22
|
+
if path:
|
|
23
|
+
self.filePath = path
|
|
24
|
+
if not os.path.exists(app.workspace.BASE_PATH + self.filePath):
|
|
25
|
+
raise FileNotFoundError(f'no file with this path: \'{self.filePath}\'')
|
|
26
|
+
if os.path.isdir(app.workspace.BASE_PATH + self.filePath):
|
|
27
|
+
raise IsADirectoryError(f'the file is a directory: \'{self.filePath}\'')
|
|
28
|
+
self.name = self.filePath.split('/')[-1]
|
|
29
|
+
with open(app.workspace.BASE_PATH + self.filePath, 'rb') as f:
|
|
30
|
+
self.data = f.read()
|
|
31
|
+
|
|
32
|
+
def save(self, path: str | None = None):
|
|
33
|
+
from app import app
|
|
34
|
+
|
|
35
|
+
if path:
|
|
36
|
+
if path[-1] == '/':
|
|
37
|
+
self.path = path + self.name
|
|
38
|
+
filepath = app.workspace.BASE_PATH + self.path
|
|
39
|
+
else:
|
|
40
|
+
self.path = path
|
|
41
|
+
self.name = path.split('/')[-1]
|
|
42
|
+
filepath = app.workspace.BASE_PATH + self.path
|
|
43
|
+
os.makedirs(os.path.dirname(filepath), exist_ok = True)
|
|
44
|
+
if os.path.exists(filepath):
|
|
45
|
+
CheeseLog.warning(f'the file will be covered: {self.path}', f'the file will be covered: \033[36m{self.path}\033[0m')
|
|
46
|
+
with open(filepath, 'w') as f:
|
|
47
|
+
f.write(self.data)
|
|
48
|
+
else:
|
|
49
|
+
if not self.path:
|
|
50
|
+
raise ValueError('could not get the file path')
|
|
51
|
+
os.makedirs(os.path.dirname(app.workspace.BASE_PATH + self.path), exist_ok = True)
|
|
52
|
+
if os.path.exists(filepath):
|
|
53
|
+
CheeseLog.warning(f'the file will be covered: {self.path}', f'the file will be covered: \033[36m{self.path}\033[0m')
|
|
54
|
+
with open(filepath, 'wb') as f:
|
|
55
|
+
f.write(self.data)
|
|
56
|
+
|
|
57
|
+
def saveMedia(self, path: str):
|
|
58
|
+
from app import app
|
|
59
|
+
|
|
60
|
+
self.save(app.workspace.MEDIA_PATH + path)
|
|
61
|
+
|
|
62
|
+
class MediaFile(File):
|
|
63
|
+
def __init__(self, path: str):
|
|
64
|
+
from app import app
|
|
65
|
+
|
|
66
|
+
super().__init__(None, None, app.workspace.MEDIA_PATH + path)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import os, traceback, inspect, pkg_resources
|
|
2
|
+
from typing import Set
|
|
3
|
+
|
|
4
|
+
import CheeseLog
|
|
5
|
+
|
|
6
|
+
class Module:
|
|
7
|
+
def __init__(self, modules, name: str):
|
|
8
|
+
self.name: str = name
|
|
9
|
+
self.subModules: Set[str] = set()
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
mainModule = __import__(name)
|
|
13
|
+
self.version: str = pkg_resources.get_distribution(self.name).version
|
|
14
|
+
except:
|
|
15
|
+
CheeseLog.error(f'The error occured while the module \'{name}\' loading\n{traceback.format_exc()}'[:-1])
|
|
16
|
+
raise SystemExit()
|
|
17
|
+
|
|
18
|
+
dependencies = None
|
|
19
|
+
try:
|
|
20
|
+
dependencies = mainModule.CheeseAPI_module_dependencies
|
|
21
|
+
except:
|
|
22
|
+
...
|
|
23
|
+
if dependencies:
|
|
24
|
+
for dependency in dependencies:
|
|
25
|
+
if modules not in modules:
|
|
26
|
+
modules.add(Module(modules, dependency))
|
|
27
|
+
|
|
28
|
+
modulePath = os.path.dirname(inspect.getfile(mainModule))
|
|
29
|
+
for filename in os.listdir(modulePath):
|
|
30
|
+
filePath = os.path.join(modulePath, filename)
|
|
31
|
+
if os.path.isfile(filePath) and filename != '__init__.py':
|
|
32
|
+
filename = filename[:-3]
|
|
33
|
+
try:
|
|
34
|
+
module = __import__(f'{self.name}.{filename}')
|
|
35
|
+
except:
|
|
36
|
+
CheeseLog.error(f'The error occured while the module \'{name}\' loading\n{traceback.format_exc()}'[:-1])
|
|
37
|
+
raise SystemExit()
|
|
38
|
+
|
|
39
|
+
self.subModules.add(module)
|
|
40
|
+
|
|
41
|
+
class LocalModule:
|
|
42
|
+
def __init__(self, basePath: str, name: str):
|
|
43
|
+
self.name: str = name
|
|
44
|
+
self.subModules: Set[str] = set()
|
|
45
|
+
|
|
46
|
+
modulePath = os.path.join(basePath, name)
|
|
47
|
+
if not os.path.isdir(modulePath):
|
|
48
|
+
raise ImportError(f'could not find module \'{name}\'')
|
|
49
|
+
|
|
50
|
+
for filename in os.listdir(modulePath):
|
|
51
|
+
filePath = os.path.join(modulePath, filename)
|
|
52
|
+
if os.path.isfile(filePath) and filename != '__init__.py':
|
|
53
|
+
filename = filename[:-3]
|
|
54
|
+
try:
|
|
55
|
+
module = __import__(f'{self.name}.{filename}')
|
|
56
|
+
except:
|
|
57
|
+
CheeseLog.error(f'The error occured while the local module \'{name}\' loading\n{traceback.format_exc()}'[:-1])
|
|
58
|
+
raise SystemExit()
|
|
59
|
+
self.subModules.add(module)
|