GNServer 0.0.0.0.6__py3-none-any.whl → 0.0.0.0.8__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/__init__.py CHANGED
@@ -30,7 +30,7 @@ DEALINGS IN THE SOFTWARE.
30
30
  """
31
31
 
32
32
  from ._app import App
33
- from KeyisBClient.gn import GNRequest, GNResponse
33
+ from KeyisBClient.gn import GNRequest, GNResponse, CORSObject
34
34
 
35
35
 
36
36
 
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
@@ -230,6 +283,7 @@ class App:
230
283
 
231
284
  def delete(self, path: str, *, cors: Optional[gn.CORSObject] = None):
232
285
  return self.route("DELETE", path, cors)
286
+
233
287
 
234
288
  def setRouteCors(self, cors: Optional[gn.CORSObject] = None):
235
289
  self._cors = cors
@@ -287,7 +341,23 @@ class App:
287
341
  return gn.GNResponse("gn:backend:404", {'error': 'Not Found'})
288
342
 
289
343
 
290
- def static(self, path: str, dir_path: str):
344
+ def fastFile(self, path: str, file_path: str, cors: Optional[gn.CORSObject] = None):
345
+ @self.get(path)
346
+ async def r_static():
347
+ nonlocal file_path
348
+ if file_path.endswith('/'):
349
+ file_path = file_path[:-1]
350
+
351
+ if not os.path.isfile(file_path):
352
+ return gn.GNResponse('gn:backend:404', cors=cors)
353
+
354
+ mime_type = guess_type(file_path.split('/')[-1])
355
+ async with aiofiles.open(file_path, "rb") as f:
356
+ data = await f.read()
357
+
358
+ return gn.GNResponse('ok', {'files': [{'mime-type': mime_type, 'data': data}]}, cors=cors)
359
+
360
+ def static(self, path: str, dir_path: str, cors: Optional[gn.CORSObject] = None):
291
361
  @self.get(f"{path}/{{_path:path}}")
292
362
  async def r_static(_path: str):
293
363
  file_path = os.path.join(dir_path, _path)
@@ -296,13 +366,13 @@ class App:
296
366
  file_path = file_path[:-1]
297
367
 
298
368
  if not os.path.isfile(file_path):
299
- return gn.GNResponse('gn:backend:404')
369
+ return gn.GNResponse('gn:backend:404', cors=cors)
300
370
 
301
371
  mime_type = guess_type(file_path.split('/')[-1])
302
372
  async with aiofiles.open(file_path, "rb") as f:
303
373
  data = await f.read()
304
374
 
305
- return gn.GNResponse('ok', {'files': [{'mime-type': mime_type, 'data': data}]})
375
+ return gn.GNResponse('ok', {'files': [{'mime-type': mime_type, 'data': data}]}, cors=cors)
306
376
 
307
377
 
308
378
 
@@ -412,14 +482,10 @@ class App:
412
482
 
413
483
  response = await self._api.dispatch(request)
414
484
 
415
-
416
- response = await self.resolve_extra_response(response)
417
-
418
-
419
485
  if inspect.isasyncgen(response):
420
486
  async for chunk in response: # type: ignore[misc]
421
487
  chunk._stream = True
422
- chunk = self.resolve_response(chunk)
488
+ chunk = await self.resolve_response(chunk)
423
489
  self._quic.send_stream_data(request.stream_id, chunk.serialize(3), end_stream=False)
424
490
  self.transmit()
425
491
 
@@ -431,14 +497,14 @@ class App:
431
497
  return
432
498
 
433
499
 
434
- response = self.resolve_response(response)
500
+ response = await self.resolve_response(response)
435
501
  self._quic.send_stream_data(request.stream_id, response.serialize(3), end_stream=True)
436
502
  logger.debug(f'Отправлен на сервер ответ -> {response.command} {response.payload if response.payload and len((response.payload)) < 200 else ''}')
437
503
  self.transmit()
438
504
  except Exception as e:
439
505
  logger.error('GNServer: error\n' + traceback.format_exc())
440
506
 
441
- response = gn.GNResponse('gn:backend:500:Internal Server Error')
507
+ response = gn.GNResponse('gn:backend:500')
442
508
  self._quic.send_stream_data(request.stream_id, response.serialize(3), end_stream=True)
443
509
  self.transmit()
444
510
 
@@ -446,36 +512,12 @@ class App:
446
512
  if response._cors is None:
447
513
  response._cors = self._api._cors
448
514
 
515
+ await response.assembly()
516
+
449
517
  return response
450
518
 
451
519
 
452
- async def resolve_extra_response(self, response: Union[gn.GNResponse, AsyncGenerator[gn.GNResponse, None]]) -> Union[gn.GNResponse, AsyncGenerator[gn.GNResponse, None]]:
453
-
454
- file_types = (
455
- 'html',
456
- 'css',
457
- 'js',
458
- 'svg'
459
- )
460
-
461
- if isinstance(response, gn.GNResponse):
462
- payload = response.payload
463
- if payload is not None:
464
- for ext_file in file_types:
465
- ext_file_ = payload.get(ext_file)
466
- if ext_file_ is not None:
467
- if isinstance(ext_file_, str):
468
- if ext_file_.startswith('/') or ext_file_.startswith('./'):
469
- try:
470
- async with await anyio.open_file(ext_file_, mode="rb") as file:
471
- data = await file.read()
472
-
473
- payload.pop(ext_file)
474
- payload['files'] = [{'mime-type': guess_type(ext_file_.split('/')[-1]), 'data': data}]
475
- except Exception as e:
476
- payload[ext_file] = f'GNServer error: {e}'
477
- logger.debug(f'error resolving extra response -> {traceback.format_exc()}')
478
- return response
520
+
479
521
 
480
522
 
481
523
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GNServer
3
- Version: 0.0.0.0.6
3
+ Version: 0.0.0.0.8
4
4
  Summary: GNServer
5
5
  Home-page: https://github.com/KeyisB/libs/tree/main/GNServer
6
6
  Author: KeyisB
@@ -0,0 +1,7 @@
1
+ GNServer/__init__.py,sha256=l9B6gZZ2gTkWvNdla--J15_Bj0BtP8RyPsrMI6amgDo,1376
2
+ GNServer/_app.py,sha256=WM7gJqSzgUl7fHNPLhzGeyfJOSC4yj4dUZxowSSbWms,18340
3
+ gnserver-0.0.0.0.8.dist-info/licenses/LICENSE,sha256=WH_t7dKZyWJ5Ld07eYIkUG4Tv6zZWXtAdsUqYAUesn0,1084
4
+ gnserver-0.0.0.0.8.dist-info/METADATA,sha256=WCL7mc5UxeFicgVe2tLAw0oyCqKlgpGJ0uPg-bygirU,804
5
+ gnserver-0.0.0.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ gnserver-0.0.0.0.8.dist-info/top_level.txt,sha256=-UOUBuD4u7Qkb1o5PdcwyA3kx8xCH2lwy0tJHi26Wb4,9
7
+ gnserver-0.0.0.0.8.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- GNServer/__init__.py,sha256=T6kT6WoJpmIXashMSHfuafb9DSePUIzw192h27j9a4M,1364
2
- GNServer/_app.py,sha256=8JcVzozjtdLtQjPThi-wR2OaAYNvYfaIqDReCmmFmrg,17801
3
- GNServer/models.py,sha256=3HTbPgXMcltK3hh3RWii0z6X2qYitXmPWzud8pOc4Rk,50
4
- gnserver-0.0.0.0.6.dist-info/licenses/LICENSE,sha256=WH_t7dKZyWJ5Ld07eYIkUG4Tv6zZWXtAdsUqYAUesn0,1084
5
- gnserver-0.0.0.0.6.dist-info/METADATA,sha256=S5wZWPijiIAS9Kd8BkMeKs7PJtY0CMeMVvp149dQyhI,804
6
- gnserver-0.0.0.0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- gnserver-0.0.0.0.6.dist-info/top_level.txt,sha256=-UOUBuD4u7Qkb1o5PdcwyA3kx8xCH2lwy0tJHi26Wb4,9
8
- gnserver-0.0.0.0.6.dist-info/RECORD,,