GNServer 0.0.0.0.7__py3-none-any.whl → 0.0.0.0.9__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.
- GNServer/_app.py +65 -45
- {gnserver-0.0.0.0.7.dist-info → gnserver-0.0.0.0.9.dist-info}/METADATA +1 -1
- gnserver-0.0.0.0.9.dist-info/RECORD +7 -0
- GNServer/models.py +0 -25
- gnserver-0.0.0.0.7.dist-info/RECORD +0 -8
- {gnserver-0.0.0.0.7.dist-info → gnserver-0.0.0.0.9.dist-info}/WHEEL +0 -0
- {gnserver-0.0.0.0.7.dist-info → gnserver-0.0.0.0.9.dist-info}/licenses/LICENSE +0 -0
- {gnserver-0.0.0.0.7.dist-info → gnserver-0.0.0.0.9.dist-info}/top_level.txt +0 -0
GNServer/_app.py
CHANGED
@@ -110,6 +110,59 @@ def guess_type(filename: str) -> str:
|
|
110
110
|
return mime_map.get(ext, "application/octet-stream")
|
111
111
|
|
112
112
|
|
113
|
+
import re
|
114
|
+
from typing import List
|
115
|
+
|
116
|
+
# regex для !{var}, поддерживает вложенность через точку
|
117
|
+
TPL_VAR_RE = re.compile(r'(?<!\\)!\{([A-Za-z_][A-Za-z0-9_\.]*)\}')
|
118
|
+
|
119
|
+
# список mime, которые считаем текстовыми
|
120
|
+
TEXTUAL_MIME_PREFIXES = [
|
121
|
+
"text/", # text/html, text/css, text/plain
|
122
|
+
]
|
123
|
+
TEXTUAL_MIME_EXACT = {
|
124
|
+
"application/javascript",
|
125
|
+
"application/json",
|
126
|
+
"application/xml",
|
127
|
+
"application/xhtml+xml"
|
128
|
+
}
|
129
|
+
TEXTUAL_MIME_SUFFIXES = (
|
130
|
+
"+xml", # например application/rss+xml
|
131
|
+
"+json", # application/ld+json
|
132
|
+
)
|
133
|
+
|
134
|
+
def extract_template_vars(filedata: bytes, mime: str) -> List[str]:
|
135
|
+
"""
|
136
|
+
Ищет все !{var} в тексте, если MIME относится к текстовым.
|
137
|
+
"""
|
138
|
+
mime = (mime or "").lower().strip()
|
139
|
+
|
140
|
+
# определяем, текстовый ли mime
|
141
|
+
is_textual = (
|
142
|
+
mime.startswith(tuple(TEXTUAL_MIME_PREFIXES))
|
143
|
+
or mime in TEXTUAL_MIME_EXACT
|
144
|
+
or mime.endswith(TEXTUAL_MIME_SUFFIXES)
|
145
|
+
or "javascript" in mime
|
146
|
+
or "json" in mime
|
147
|
+
or "xml" in mime
|
148
|
+
)
|
149
|
+
|
150
|
+
if not is_textual:
|
151
|
+
return []
|
152
|
+
|
153
|
+
try:
|
154
|
+
text = filedata.decode("utf-8", errors="ignore")
|
155
|
+
except Exception:
|
156
|
+
return []
|
157
|
+
|
158
|
+
return list(set(m.group(1) for m in TPL_VAR_RE.finditer(text)))
|
159
|
+
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
|
113
166
|
@dataclass
|
114
167
|
class Route:
|
115
168
|
method: str
|
@@ -288,9 +341,9 @@ class App:
|
|
288
341
|
return gn.GNResponse("gn:backend:404", {'error': 'Not Found'})
|
289
342
|
|
290
343
|
|
291
|
-
def fastFile(self, path: str, file_path: str, cors: Optional[gn.CORSObject] = None):
|
344
|
+
def fastFile(self, path: str, file_path: str, cors: Optional[gn.CORSObject] = None, template: Optional[gn.TemplateObject] = None, payload: Optional[dict] = None):
|
292
345
|
@self.get(path)
|
293
|
-
async def r_static(
|
346
|
+
async def r_static():
|
294
347
|
nonlocal file_path
|
295
348
|
if file_path.endswith('/'):
|
296
349
|
file_path = file_path[:-1]
|
@@ -298,13 +351,11 @@ class App:
|
|
298
351
|
if not os.path.isfile(file_path):
|
299
352
|
return gn.GNResponse('gn:backend:404', cors=cors)
|
300
353
|
|
301
|
-
|
302
|
-
|
303
|
-
data = await f.read()
|
354
|
+
fileObject = gn.FileObject(file_path, template)
|
355
|
+
return gn.GNResponse('ok', payload=payload, files=fileObject, cors=cors)
|
304
356
|
|
305
|
-
return gn.GNResponse('ok', {'files': [{'mime-type': mime_type, 'data': data}]}, cors=cors)
|
306
357
|
|
307
|
-
def static(self, path: str, dir_path: str, cors: Optional[gn.CORSObject] = None):
|
358
|
+
def static(self, path: str, dir_path: str, cors: Optional[gn.CORSObject] = None, template: Optional[gn.TemplateObject] = None, payload: Optional[dict] = None):
|
308
359
|
@self.get(f"{path}/{{_path:path}}")
|
309
360
|
async def r_static(_path: str):
|
310
361
|
file_path = os.path.join(dir_path, _path)
|
@@ -314,12 +365,9 @@ class App:
|
|
314
365
|
|
315
366
|
if not os.path.isfile(file_path):
|
316
367
|
return gn.GNResponse('gn:backend:404', cors=cors)
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
data = await f.read()
|
321
|
-
|
322
|
-
return gn.GNResponse('ok', {'files': [{'mime-type': mime_type, 'data': data}]}, cors=cors)
|
368
|
+
|
369
|
+
fileObject = gn.FileObject(file_path, template)
|
370
|
+
return gn.GNResponse('ok', payload=payload, files=fileObject, cors=cors)
|
323
371
|
|
324
372
|
|
325
373
|
|
@@ -429,10 +477,6 @@ class App:
|
|
429
477
|
|
430
478
|
response = await self._api.dispatch(request)
|
431
479
|
|
432
|
-
|
433
|
-
response = await self.resolve_extra_response(response)
|
434
|
-
|
435
|
-
|
436
480
|
if inspect.isasyncgen(response):
|
437
481
|
async for chunk in response: # type: ignore[misc]
|
438
482
|
chunk._stream = True
|
@@ -455,7 +499,7 @@ class App:
|
|
455
499
|
except Exception as e:
|
456
500
|
logger.error('GNServer: error\n' + traceback.format_exc())
|
457
501
|
|
458
|
-
response = gn.GNResponse('gn:backend:500
|
502
|
+
response = gn.GNResponse('gn:backend:500')
|
459
503
|
self._quic.send_stream_data(request.stream_id, response.serialize(3), end_stream=True)
|
460
504
|
self.transmit()
|
461
505
|
|
@@ -463,36 +507,12 @@ class App:
|
|
463
507
|
if response._cors is None:
|
464
508
|
response._cors = self._api._cors
|
465
509
|
|
510
|
+
await response.assembly()
|
511
|
+
|
466
512
|
return response
|
467
513
|
|
468
514
|
|
469
|
-
|
470
|
-
|
471
|
-
file_types = (
|
472
|
-
'html',
|
473
|
-
'css',
|
474
|
-
'js',
|
475
|
-
'svg'
|
476
|
-
)
|
477
|
-
|
478
|
-
if isinstance(response, gn.GNResponse):
|
479
|
-
payload = response.payload
|
480
|
-
if payload is not None:
|
481
|
-
for ext_file in file_types:
|
482
|
-
ext_file_ = payload.get(ext_file)
|
483
|
-
if ext_file_ is not None:
|
484
|
-
if isinstance(ext_file_, str):
|
485
|
-
if ext_file_.startswith('/') or ext_file_.startswith('./'):
|
486
|
-
try:
|
487
|
-
async with await anyio.open_file(ext_file_, mode="rb") as file:
|
488
|
-
data = await file.read()
|
489
|
-
|
490
|
-
payload.pop(ext_file)
|
491
|
-
payload['files'] = [{'mime-type': guess_type(ext_file_.split('/')[-1]), 'data': data}]
|
492
|
-
except Exception as e:
|
493
|
-
payload[ext_file] = f'GNServer error: {e}'
|
494
|
-
logger.debug(f'error resolving extra response -> {traceback.format_exc()}')
|
495
|
-
return response
|
515
|
+
|
496
516
|
|
497
517
|
|
498
518
|
|
@@ -0,0 +1,7 @@
|
|
1
|
+
GNServer/__init__.py,sha256=l9B6gZZ2gTkWvNdla--J15_Bj0BtP8RyPsrMI6amgDo,1376
|
2
|
+
GNServer/_app.py,sha256=dKgGAFVwDMGOt7i4HGF2TrVz0XSGjE0TV2hRJHfUnxA,18273
|
3
|
+
gnserver-0.0.0.0.9.dist-info/licenses/LICENSE,sha256=WH_t7dKZyWJ5Ld07eYIkUG4Tv6zZWXtAdsUqYAUesn0,1084
|
4
|
+
gnserver-0.0.0.0.9.dist-info/METADATA,sha256=Y7bXZtN9rVDJFkIuyoEIWVUWPRVYpVjvvMaVZxnRwds,804
|
5
|
+
gnserver-0.0.0.0.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
6
|
+
gnserver-0.0.0.0.9.dist-info/top_level.txt,sha256=-UOUBuD4u7Qkb1o5PdcwyA3kx8xCH2lwy0tJHi26Wb4,9
|
7
|
+
gnserver-0.0.0.0.9.dist-info/RECORD,,
|
GNServer/models.py
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
GNServer/__init__.py,sha256=l9B6gZZ2gTkWvNdla--J15_Bj0BtP8RyPsrMI6amgDo,1376
|
2
|
-
GNServer/_app.py,sha256=w2rK4nrDWVZ_f0c3tC2cVckvkb8gY4Xudajiajw6yqQ,18534
|
3
|
-
GNServer/models.py,sha256=3HTbPgXMcltK3hh3RWii0z6X2qYitXmPWzud8pOc4Rk,50
|
4
|
-
gnserver-0.0.0.0.7.dist-info/licenses/LICENSE,sha256=WH_t7dKZyWJ5Ld07eYIkUG4Tv6zZWXtAdsUqYAUesn0,1084
|
5
|
-
gnserver-0.0.0.0.7.dist-info/METADATA,sha256=bqObiR6rIxTCq4Lg_GU9oRyYbw6B1wg2NggJV2qrg5c,804
|
6
|
-
gnserver-0.0.0.0.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
-
gnserver-0.0.0.0.7.dist-info/top_level.txt,sha256=-UOUBuD4u7Qkb1o5PdcwyA3kx8xCH2lwy0tJHi26Wb4,9
|
8
|
-
gnserver-0.0.0.0.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|