CheeseAPI 0.0.1__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.
CheeseAPI/request.py ADDED
@@ -0,0 +1,99 @@
1
+ import json, traceback, re
2
+ from typing import TypeVar, overload, Dict
3
+
4
+ import CheeseType.network, xmltodict, CheeseLog
5
+
6
+ from file import File
7
+
8
+ T = TypeVar('T')
9
+
10
+ class BaseItem:
11
+ def __init__(self):
12
+ self._values: Dict[str, str] = {}
13
+
14
+ def get(self, key: str, default: T = None) -> str | File | T:
15
+ if key not in self._values:
16
+ return default
17
+ return self._values[key]
18
+
19
+ class Args(BaseItem):
20
+ def __init__(self, query: str):
21
+ super().__init__()
22
+
23
+ for q in query.split('&'):
24
+ q = q.split('=')
25
+ self._values[q[0]] = q[1] if len(q) > 1 else None
26
+
27
+ class Form(BaseItem):
28
+ @overload
29
+ def __init__(self):
30
+ ...
31
+
32
+ @overload
33
+ def __init__(self, body: str):
34
+ ...
35
+
36
+ @overload
37
+ def __init__(self, body: bytes, spliter: bytes):
38
+ ...
39
+
40
+ def __init__(self, body: str | bytes | None = None, spliter: bytes | None = None):
41
+ super().__init__()
42
+
43
+ if body and not spliter:
44
+ for s in body.split('&'):
45
+ s = s.split('=')
46
+ self._values[s[0]] = s[1]
47
+
48
+ elif body and spliter:
49
+ body = body[2:-4].split(spliter)[1:-1]
50
+ for s in body:
51
+ key = re.findall(rb'\bname="(.*?)"', s)[0].decode()
52
+ value = s.split(b'\r\n\r\n')[1][:-4]
53
+ filename = re.findall(rb'\bfilename="(.*?)"', s)
54
+ if len(filename):
55
+ self._values[key] = File(filename[0].decode(), value)
56
+ else:
57
+ self._values[key] = value.decode()
58
+
59
+ class Request:
60
+ def __init__(self, scope, body: bytes | None = None):
61
+ query_string = scope['query_string'].decode()
62
+
63
+ self.ip: CheeseType.network.IPv4 = scope['client'][0]
64
+ self.path: str = scope['path']
65
+ self.fullPath: str = self.path + '?' + query_string if query_string else self.path
66
+ self.scheme = scope['scheme']
67
+
68
+ self.headers: Dict[str, str] = {}
69
+
70
+ if scope['type'] in [ 'http', 'https' ]:
71
+ self.method: str = scope['method']
72
+ self.args = Args(query_string)
73
+ self.body = None
74
+ self.form: Form = Form()
75
+ for header in scope['headers']:
76
+ key = header[0].decode()
77
+ value = header[1].decode()
78
+ self.headers[key] = value
79
+ if key == 'content-type':
80
+ try:
81
+ if value in [ 'application/json', 'application/javascript' ]:
82
+ self.body = json.loads(body)
83
+ elif value == 'application/xml':
84
+ self.body = xmltodict.parse(body)
85
+ elif value in [ 'text/plain', 'text/html' ]:
86
+ self.body = body.decode()
87
+ elif value.startswith('multipart/form-data;'):
88
+ self.form = Form(body, header[1].split(b'boundary=')[1].split(b';')[0])
89
+ elif value == 'application/x-www-form-urlencoded':
90
+ self.form = Form(body.decode())
91
+ except:
92
+ CheeseLog.danger(f'{self.method} {self.fullPath} cannot parse request.body correctly:\n{traceback.format_exc()}'[:-1], f'\033[36m{self.method} {self.fullPath}\033[0m cannot parse request.body correctly:\n{traceback.format_exc()}'[:-1])
93
+ else:
94
+ for header in scope['headers']:
95
+ key = header[0].decode()
96
+ value = header[1].decode()
97
+ self.headers[key] = value
98
+ if key == 'sec-websocket-key':
99
+ self.sid: str = value
CheeseAPI/response.py ADDED
@@ -0,0 +1,384 @@
1
+ import json
2
+ from typing import Dict, Any
3
+
4
+ from file import File
5
+
6
+ CONTENT_TYPE = {
7
+ 'tif': 'image/tiff',
8
+ '001': 'application/x-001',
9
+ '301': 'application/x-301',
10
+ '323': 'text/h323',
11
+ '906': 'application/x-906',
12
+ '907': 'drawing/907',
13
+ 'a11': 'application/x-a11',
14
+ 'acp': 'audio/x-mei-aac',
15
+ 'ai': 'application/postscript',
16
+ 'aif': 'audio/aiff',
17
+ 'aifc': 'audio/aiff',
18
+ 'aiff': 'audio/aiff',
19
+ 'anv': 'application/x-anv',
20
+ 'asa': 'text/asa',
21
+ 'asf': 'video/x-ms-asf',
22
+ 'asp': 'text/asp',
23
+ 'asx': 'video/x-ms-asf',
24
+ 'au': 'audio/basic',
25
+ 'avi': 'video/avi',
26
+ 'awf': 'application/vnd.adobe.workflow',
27
+ 'biz': 'text/xml',
28
+ 'bmp': 'application/x-bmp',
29
+ 'bot': 'application/x-bot',
30
+ 'c4t': 'application/x-c4t',
31
+ 'c90': 'application/x-c90',
32
+ 'cal': 'application/x-cals',
33
+ 'cat': 'application/vnd.ms-pki.seccat',
34
+ 'cdf': 'application/x-netcdf',
35
+ 'cdr': 'application/x-cdr',
36
+ 'cel': 'application/x-cel',
37
+ 'cer': 'application/x-x509-ca-cert',
38
+ 'cg4': 'application/x-g4',
39
+ 'cgm': 'application/x-cgm',
40
+ 'cit': 'application/x-cit',
41
+ 'class': 'java/*',
42
+ 'cml': 'text/xml',
43
+ 'cmp': 'application/x-cmp',
44
+ 'cmx': 'application/x-cmx',
45
+ 'cot': 'application/x-cot',
46
+ 'crl': 'application/pkix-crl',
47
+ 'crt': 'application/x-x509-ca-cert',
48
+ 'csi': 'application/x-csi',
49
+ 'css': 'text/css',
50
+ 'cut': 'application/x-cut',
51
+ 'dbf': 'application/x-dbf',
52
+ 'dbm': 'application/x-dbm',
53
+ 'dbx': 'application/x-dbx',
54
+ 'dcd': 'text/xml',
55
+ 'dcx': 'application/x-dcx',
56
+ 'der': 'application/x-x509-ca-cert',
57
+ 'dgn': 'application/x-dgn',
58
+ 'dib': 'application/x-dib',
59
+ 'dll': 'application/x-msdownload',
60
+ 'doc': 'application/msword',
61
+ 'dot': 'application/msword',
62
+ 'drw': 'application/x-drw',
63
+ 'dtd': 'text/xml',
64
+ 'dwf': 'Model/vnd.dwf',
65
+ 'dwf': 'application/x-dwf',
66
+ 'dwg': 'application/x-dwg',
67
+ 'dxb': 'application/x-dxb',
68
+ 'dxf': 'application/x-dxf',
69
+ 'edn': 'application/vnd.adobe.edn',
70
+ 'emf': 'application/x-emf',
71
+ 'eml': 'message/rfc822',
72
+ 'ent': 'text/xml',
73
+ 'epi': 'application/x-epi',
74
+ 'eps': 'application/x-ps',
75
+ 'etd': 'application/x-ebx',
76
+ 'exe': 'application/x-msdownload',
77
+ 'fax': 'image/fax',
78
+ 'fdf': 'application/vnd.fdf',
79
+ 'fif': 'application/fractals',
80
+ 'fo': 'text/xml',
81
+ 'frm': 'application/x-frm',
82
+ 'g4': 'application/x-g4',
83
+ 'gbr': 'application/x-gbr',
84
+ '': 'application/x-',
85
+ 'gif': 'image/gif',
86
+ 'gl2': 'application/x-gl2',
87
+ 'gp4': 'application/x-gp4',
88
+ 'hgl': 'application/x-hgl',
89
+ 'hmr': 'application/x-hmr',
90
+ 'hpg': 'application/x-hpgl',
91
+ 'hpl': 'application/x-hpl',
92
+ 'hqx': 'application/mac-binhex40',
93
+ 'hrf': 'application/x-hrf',
94
+ 'hta': 'application/hta',
95
+ 'htc': 'text/x-component',
96
+ 'htm': 'text/html',
97
+ 'html': 'text/html',
98
+ 'htt': 'text/webviewhtml',
99
+ 'htx': 'text/html',
100
+ 'icb': 'application/x-icb',
101
+ 'ico': 'image/x-icon',
102
+ 'iff': 'application/x-iff',
103
+ 'ig4': 'application/x-g4',
104
+ 'igs': 'application/x-igs',
105
+ 'iii': 'application/x-iphone',
106
+ 'img': 'application/x-img',
107
+ 'ins': 'application/x-internet-signup',
108
+ 'isp': 'application/x-internet-signup',
109
+ 'IVF': 'video/x-ivf',
110
+ 'java': 'java/*',
111
+ 'jfif': 'image/jpeg',
112
+ 'jpe': 'image/jpeg',
113
+ 'jpeg': 'image/jpeg',
114
+ 'jpg': 'image/jpeg',
115
+ 'js': 'application/x-javascript',
116
+ 'jsp': 'text/html',
117
+ 'la1': 'audio/x-liquid-file',
118
+ 'lar': 'application/x-laplayer-reg',
119
+ 'latex': 'application/x-latex',
120
+ 'lavs': 'audio/x-liquid-secure',
121
+ 'lbm': 'application/x-lbm',
122
+ 'lmsff': 'audio/x-la-lms',
123
+ 'ls': 'application/x-javascript',
124
+ 'ltr': 'application/x-ltr',
125
+ 'm1v': 'video/x-mpeg',
126
+ 'm2v': 'video/x-mpeg',
127
+ 'm3u': 'audio/mpegurl',
128
+ 'm4e': 'video/mpeg4',
129
+ 'mac': 'application/x-mac',
130
+ 'man': 'application/x-troff-man',
131
+ 'math': 'text/xml',
132
+ 'mdb': 'application/msaccess',
133
+ 'mfp': 'application/x-shockwave-flash',
134
+ 'mht': 'message/rfc822',
135
+ 'mhtml': 'message/rfc822',
136
+ 'mi': 'application/x-mi',
137
+ 'mid': 'audio/mid',
138
+ 'midi': 'audio/mid',
139
+ 'mil': 'application/x-mil',
140
+ 'mml': 'text/xml',
141
+ 'mnd': 'audio/x-musicnet-download',
142
+ 'mns': 'audio/x-musicnet-stream',
143
+ 'mocha': 'application/x-javascript',
144
+ 'movie': 'video/x-sgi-movie',
145
+ 'mp1': 'audio/mp1',
146
+ 'mp2': 'audio/mp2',
147
+ 'mp2v': 'video/mpeg',
148
+ 'mp3': 'audio/mp3',
149
+ 'mp4': 'video/mpeg4',
150
+ 'mpa': 'video/x-mpg',
151
+ 'mpd': 'application/vnd.ms-project',
152
+ 'mpe': 'video/x-mpeg',
153
+ 'mpeg': 'video/mpg',
154
+ 'mpg': 'video/mpg',
155
+ 'mpga': 'audio/rn-mpeg',
156
+ 'mpp': 'application/vnd.ms-project',
157
+ 'mps': 'video/x-mpeg',
158
+ 'mpt': 'application/vnd.ms-project',
159
+ 'mpv': 'video/mpg',
160
+ 'mpv2': 'video/mpeg',
161
+ 'mpw': 'application/vnd.ms-project',
162
+ 'mpx': 'application/vnd.ms-project',
163
+ 'mtx': 'text/xml',
164
+ 'mxp': 'application/x-mmxp',
165
+ 'net': 'image/pnetvue',
166
+ 'nrf': 'application/x-nrf',
167
+ 'nws': 'message/rfc822',
168
+ 'odc': 'text/x-ms-odc',
169
+ 'out': 'application/x-out',
170
+ 'p10': 'application/pkcs10',
171
+ 'p12': 'application/x-pkcs12',
172
+ 'p7b': 'application/x-pkcs7-certificates',
173
+ 'p7c': 'application/pkcs7-mime',
174
+ 'p7m': 'application/pkcs7-mime',
175
+ 'p7r': 'application/x-pkcs7-certreqresp',
176
+ 'p7s': 'application/pkcs7-signature',
177
+ 'pc5': 'application/x-pc5',
178
+ 'pci': 'application/x-pci',
179
+ 'pcl': 'application/x-pcl',
180
+ 'pcx': 'application/x-pcx',
181
+ 'pdf': 'application/pdf',
182
+ 'pdx': 'application/vnd.adobe.pdx',
183
+ 'pfx': 'application/x-pkcs12',
184
+ 'pgl': 'application/x-pgl',
185
+ 'pic': 'application/x-pic',
186
+ 'pko': 'application/vnd.ms-pki.pko',
187
+ 'pl': 'application/x-perl',
188
+ 'plg': 'text/html',
189
+ 'pls': 'audio/scpls',
190
+ 'plt': 'application/x-plt',
191
+ 'png': 'image/png',
192
+ 'pot': 'application/vnd.ms-powerpoint',
193
+ 'ppa': 'application/vnd.ms-powerpoint',
194
+ 'ppm': 'application/x-ppm',
195
+ 'pps': 'application/vnd.ms-powerpoint',
196
+ 'ppt': 'application/vnd.ms-powerpoint',
197
+ 'pr': 'application/x-pr',
198
+ 'prf': 'application/pics-rules',
199
+ 'prn': 'application/x-prn',
200
+ 'prt': 'application/x-prt',
201
+ 'ps': 'application/postscript',
202
+ 'ptn': 'application/x-ptn',
203
+ 'pwz': 'application/vnd.ms-powerpoint',
204
+ 'r3t': 'text/vnd.rn-realtext3d',
205
+ 'ra': 'audio/vnd.rn-realaudio',
206
+ 'ram': 'audio/x-pn-realaudio',
207
+ 'ras': 'application/x-ras',
208
+ 'rat': 'application/rat-file',
209
+ 'rdf': 'text/xml',
210
+ 'rec': 'application/vnd.rn-recording',
211
+ 'red': 'application/x-red',
212
+ 'rgb': 'application/x-rgb',
213
+ 'rjs': 'application/vnd.rn-realsystem-rjs',
214
+ 'rjt': 'application/vnd.rn-realsystem-rjt',
215
+ 'rlc': 'application/x-rlc',
216
+ 'rle': 'application/x-rle',
217
+ 'rm': 'application/vnd.rn-realmedia',
218
+ 'rmf': 'application/vnd.adobe.rmf',
219
+ 'rmi': 'audio/mid',
220
+ 'rmj': 'application/vnd.rn-realsystem-rmj',
221
+ 'rmm': 'audio/x-pn-realaudio',
222
+ 'rmp': 'application/vnd.rn-rn_music_package',
223
+ 'rms': 'application/vnd.rn-realmedia-secure',
224
+ 'rmvb': 'application/vnd.rn-realmedia-vbr',
225
+ 'rmx': 'application/vnd.rn-realsystem-rmx',
226
+ 'rnx': 'application/vnd.rn-realplayer',
227
+ 'rp': 'image/vnd.rn-realpix',
228
+ 'rpm': 'audio/x-pn-realaudio-plugin',
229
+ 'rsml': 'application/vnd.rn-rsml',
230
+ 'rt': 'text/vnd.rn-realtext',
231
+ 'rtf': 'application/msword',
232
+ 'rv': 'video/vnd.rn-realvideo',
233
+ 'sam': 'application/x-sam',
234
+ 'sat': 'application/x-sat',
235
+ 'sdp': 'application/sdp',
236
+ 'sdw': 'application/x-sdw',
237
+ 'sit': 'application/x-stuffit',
238
+ 'slb': 'application/x-slb',
239
+ 'sld': 'application/x-sld',
240
+ 'slk': 'drawing/x-slk',
241
+ 'smi': 'application/smil',
242
+ 'smil': 'application/smil',
243
+ 'smk': 'application/x-smk',
244
+ 'snd': 'audio/basic',
245
+ 'sol': 'text/plain',
246
+ 'sor': 'text/plain',
247
+ 'spc': 'application/x-pkcs7-certificates',
248
+ 'spl': 'application/futuresplash',
249
+ 'spp': 'text/xml',
250
+ 'ssm': 'application/streamingmedia',
251
+ 'sst': 'application/vnd.ms-pki.certstore',
252
+ 'stl': 'application/vnd.ms-pki.stl',
253
+ 'stm': 'text/html',
254
+ 'sty': 'application/x-sty',
255
+ 'svg': 'text/xml',
256
+ 'swf': 'application/x-shockwave-flash',
257
+ 'tdf': 'application/x-tdf',
258
+ 'tg4': 'application/x-tg4',
259
+ 'tga': 'application/x-tga',
260
+ 'tif': 'image/tiff',
261
+ 'tiff': 'image/tiff',
262
+ 'tld': 'text/xml',
263
+ 'top': 'drawing/x-top',
264
+ 'torrent': 'application/x-bittorrent',
265
+ 'tsd': 'text/xml',
266
+ 'txt': 'text/plain',
267
+ 'uin': 'application/x-icq',
268
+ 'uls': 'text/iuls',
269
+ 'vcf': 'text/x-vcard',
270
+ 'vda': 'application/x-vda',
271
+ 'vdx': 'application/vnd.visio',
272
+ 'vml': 'text/xml',
273
+ 'vpg': 'application/x-vpeg005',
274
+ 'vsd': 'application/vnd.visio',
275
+ 'vss': 'application/vnd.visio',
276
+ 'vst': 'application/vnd.visio',
277
+ 'vsw': 'application/vnd.visio',
278
+ 'vsx': 'application/vnd.visio',
279
+ 'vtx': 'application/vnd.visio',
280
+ 'vxml': 'text/xml',
281
+ 'wav': 'audio/wav',
282
+ 'wax': 'audio/x-ms-wax',
283
+ 'wb1': 'application/x-wb1',
284
+ 'wb2': 'application/x-wb2',
285
+ 'wb3': 'application/x-wb3',
286
+ 'wbmp': 'image/vnd.wap.wbmp',
287
+ 'wiz': 'application/msword',
288
+ 'wk3': 'application/x-wk3',
289
+ 'wk4': 'application/x-wk4',
290
+ 'wkq': 'application/x-wkq',
291
+ 'wks': 'application/x-wks',
292
+ 'wm': 'video/x-ms-wm',
293
+ 'wma': 'audio/x-ms-wma',
294
+ 'wmd': 'application/x-ms-wmd',
295
+ 'wmf': 'application/x-wmf',
296
+ 'wml': 'text/vnd.wap.wml',
297
+ 'wmv': 'video/x-ms-wmv',
298
+ 'wmx': 'video/x-ms-wmx',
299
+ 'wmz': 'application/x-ms-wmz',
300
+ 'wp6': 'application/x-wp6',
301
+ 'wpd': 'application/x-wpd',
302
+ 'wpg': 'application/x-wpg',
303
+ 'wpl': 'application/vnd.ms-wpl',
304
+ 'wq1': 'application/x-wq1',
305
+ 'wr1': 'application/x-wr1',
306
+ 'wri': 'application/x-wri',
307
+ 'wrk': 'application/x-wrk',
308
+ 'ws': 'application/x-ws',
309
+ 'ws2': 'application/x-ws',
310
+ 'wsc': 'text/scriptlet',
311
+ 'wsdl': 'text/xml',
312
+ 'wvx': 'video/x-ms-wvx',
313
+ 'xdp': 'application/vnd.adobe.xdp',
314
+ 'xdr': 'text/xml',
315
+ 'xfd': 'application/vnd.adobe.xfd',
316
+ 'xfdf': 'application/vnd.adobe.xfdf',
317
+ 'xhtml': 'text/html',
318
+ 'xls': 'application/vnd.ms-excel',
319
+ 'xlw': 'application/x-xlw',
320
+ 'xml': 'text/xml',
321
+ 'xpl': 'audio/scpls',
322
+ 'xq': 'text/xml',
323
+ 'xql': 'text/xml',
324
+ 'xquery': 'text/xml',
325
+ 'xsd': 'text/xml',
326
+ 'xsl': 'text/xml',
327
+ 'xslt': 'text/xml',
328
+ 'xwd': 'application/x-xwd',
329
+ 'x_b': 'application/x-x_b',
330
+ 'sis': 'application/vnd.symbian.install',
331
+ 'sisx': 'application/vnd.symbian.install',
332
+ 'x_t': 'application/x-x_t',
333
+ 'ipa': 'application/vnd.iphone',
334
+ 'apk': 'application/vnd.android.package-archive',
335
+ 'xap': 'application/x-silverlight-app',
336
+ }
337
+
338
+ class BaseResponse:
339
+ def __init__(self, body: Any = '', status: int = 200, headers: Dict[str, str] = {}) -> None:
340
+ self.status: int = status
341
+ self.headers: Dict[str, str] = headers
342
+ self.body: Any = body
343
+
344
+ class Response(BaseResponse):
345
+ def __init__(self, body: Any = '', status: int = 200, headers: Dict[str, str] = {}):
346
+ _headers = {
347
+ 'content-type': 'text/plain; charset=utf-8'
348
+ }
349
+ _headers.update(headers)
350
+ super().__init__(body, status, _headers)
351
+
352
+ class JsonResponse(BaseResponse):
353
+ def __init__(self, body: Dict = {}, status: int = 200, headers: Dict[str, str] = {}):
354
+ _headers = {
355
+ 'content-type': 'application/json; charset=utf-8'
356
+ }
357
+ _headers.update(headers)
358
+ super().__init__(json.dumps(body), status, _headers)
359
+
360
+ class RedirectResponse(BaseResponse):
361
+ def __init__(self, url: str, status: int = 301, headers: Dict[str, str] = {}):
362
+ _headers = {
363
+ 'Location': url
364
+ }
365
+ _headers.update(headers)
366
+ super().__init__(b'', status, _headers)
367
+
368
+ class FileResponse(BaseResponse):
369
+ def __init__(self, file: File, isDownloaded: bool = False, headers: Dict[str, str] = {}):
370
+ fileSuffix = file.name.split('.')
371
+ if len(fileSuffix) == 1:
372
+ fileSuffix = ''
373
+ else:
374
+ fileSuffix = fileSuffix[-1]
375
+ if isDownloaded or fileSuffix not in CONTENT_TYPE:
376
+ _headers = {
377
+ 'content-type': 'application/octet-stream'
378
+ }
379
+ else:
380
+ _headers = {
381
+ 'content-type': CONTENT_TYPE[fileSuffix]
382
+ }
383
+ _headers.update(headers)
384
+ super().__init__(file.data, 200, _headers)
CheeseAPI/route.py ADDED
@@ -0,0 +1,140 @@
1
+ import re, uuid
2
+ from typing import Self, Callable, Dict, List, Tuple, Any
3
+
4
+ PATTERNS: Dict[str, re.Pattern] = {
5
+ 'str': {
6
+ 'pattern': r'.+',
7
+ 'type': str
8
+ },
9
+ 'int': {
10
+ 'pattern': r'[-+]?[0-9]+',
11
+ 'type': int
12
+ },
13
+ 'float': {
14
+ 'pattern': r'[-+]?[0-9]+.[0-9]+',
15
+ 'type': float
16
+ },
17
+ 'uuid': {
18
+ 'pattern': r'r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"',
19
+ 'type': uuid.UUID
20
+ }
21
+ }
22
+
23
+ _PATHS: dict = {}
24
+
25
+ class _MatchPathNode:
26
+ def __init__(self):
27
+ self.children: dict = {}
28
+ self.key: str | None = None
29
+ self.isEnd: bool = False
30
+
31
+ class _MatchPath:
32
+ def __init__(self):
33
+ self.root: _MatchPathNode = _MatchPathNode()
34
+
35
+ def insert(self, path: str, func: Callable, methods: List[str]):
36
+ node = self.root
37
+ for part in path.split('/')[1:]:
38
+ if re.match(r'<\w+:\w+>', part):
39
+ part = part[1:-1].split(':')
40
+ _part = f'<:{part[1]}>'
41
+ if _part not in node.children:
42
+ node.children[_part] = _MatchPathNode()
43
+ node.children[_part].key = part[0]
44
+ part = _part
45
+ else:
46
+ if part not in node.children:
47
+ node.children[part] = _MatchPathNode()
48
+ node = node.children[part]
49
+ node.isEnd = True
50
+
51
+ for method in methods:
52
+ node.children[method] = func
53
+
54
+ def match(self, path: str) -> Tuple[Dict[str, Callable], Dict[str, Any]]:
55
+ kwargs = {}
56
+ node = self.root
57
+ for part in path.split('/')[1:]:
58
+ if part not in node.children:
59
+ flag = False
60
+ for key, value in reversed(PATTERNS.items()):
61
+ if re.fullmatch(value['pattern'], part) and f'<:{key}>' in node.children:
62
+ kwargs[node.children[f'<:{key}>'].key] = value['type'](part)
63
+ part = f'<:{key}>'
64
+ flag = True
65
+ break
66
+ if not flag:
67
+ return None, {}
68
+ node = node.children[part]
69
+ if node.isEnd:
70
+ return node.children, kwargs
71
+ else:
72
+ return None, {}
73
+
74
+ _MATCH_PATH: _MatchPath = _MatchPath()
75
+
76
+ class Route:
77
+ def __init__(self, prefix: str = '', parent: Self | None = None):
78
+ self.parent: Route | None = parent
79
+ if self.parent is not None:
80
+ self.parent.routers.append(self)
81
+ prefix = self.parent.prefix + prefix
82
+ self.prefix: str = prefix
83
+ self.routers: List[Route] = []
84
+ self.paths: Dict[str, Dict[str, Callable]] = {}
85
+
86
+ def __call__(self, path: str, methods: List[str]):
87
+ def decorator(func):
88
+ self.default(path, methods, func)
89
+ return decorator
90
+
91
+ def default(self, path: str, methods: List[str], func: Callable):
92
+ _path = self.prefix + path
93
+ if _path not in _PATHS:
94
+ _PATHS[_path] = {}
95
+ if _path not in self.paths:
96
+ self.paths[_path] = {}
97
+
98
+ if not isinstance(_PATHS[_path], dict):
99
+ _PATHS[_path] = {}
100
+ for method in methods:
101
+ _PATHS[_path][method] = func
102
+ self.paths[_path][method] = func
103
+
104
+ _MATCH_PATH.insert(_path, func, methods)
105
+
106
+ def get(self, path: str):
107
+ def decorator(func):
108
+ self.default(path, [ 'GET' ], func)
109
+ return decorator
110
+
111
+ def post(self, path: str):
112
+ def decorator(func):
113
+ self.default(path, [ 'POST' ], func)
114
+ return decorator
115
+
116
+ def delete(self, path: str):
117
+ def decorator(func):
118
+ self.default(path, [ 'DELETE' ], func)
119
+ return decorator
120
+
121
+ def patch(self, path: str):
122
+ def decorator(func):
123
+ self.default(path, [ 'PATCH' ], func)
124
+ return decorator
125
+
126
+ def put(self, path: str):
127
+ def decorator(func):
128
+ self.default(path, [ 'PUT' ], func)
129
+ return decorator
130
+
131
+ def websocket(self, path: str):
132
+ def decorator(func):
133
+ self.default(path, [ 'WEBSOCKET' ], func)
134
+ return decorator
135
+
136
+ def matchPath(path: str) -> Tuple[Dict[str, Callable], Dict[str, Any]]:
137
+ if path in _PATHS:
138
+ return _PATHS[path], {}
139
+
140
+ return _MATCH_PATH.match(path)