CheeseAPI 2.0.7b4__tar.gz → 2.0.8b2__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.
@@ -6,4 +6,4 @@ from CheeseAPI.websocket import Websocket
6
6
  from CheeseAPI.file import File
7
7
  from CheeseAPI.route import Route, RouteProxy
8
8
  from CheeseAPI.validator import validator
9
-
9
+ from CheeseAPI.scheduler import Task
@@ -334,7 +334,7 @@ class AppProxy:
334
334
  async def get_response(self, request: Request) -> Response:
335
335
  if inspect.isfunction(request.fn):
336
336
  try:
337
- signature = inspect.signature(request.fn)
337
+ signature = inspect.signature(request.fn, follow_wrapped = False)
338
338
  if 'request' in signature.parameters or any(p.kind == inspect.Parameter.VAR_KEYWORD for p in signature.parameters.values()):
339
339
  return await request.fn(request = request)
340
340
  else:
@@ -473,9 +473,9 @@ class AppProxy:
473
473
  static.websocket_data_decode = app.sync_server_data_decode
474
474
  static.websocket_data_encode = app.sync_server_data_encode
475
475
  if app.sync_server_url:
476
- static.websocket_sync_servers = (redis.ConnectionPool.from_url(app.sync_server_url), redis.asyncio.ConnectionPool.from_url(app.sync_server_url))
476
+ static.websocket_sync_servers = (redis.ConnectionPool.from_url(app.sync_server_url, socket_timeout = app.sync_server_timeout, socket_connect_timeout = app.sync_server_timeout), redis.asyncio.ConnectionPool.from_url(app.sync_server_url, socket_timeout = app.sync_server_timeout, socket_connect_timeout = app.sync_server_timeout))
477
477
  static.websocket_sync_server = {}
478
- app.scheduler._proxy.init(app)
478
+ static.scheduler_sync_servers = (redis.ConnectionPool.from_url(app.sync_server_url, socket_timeout = app.sync_server_timeout, socket_connect_timeout = app.sync_server_timeout), redis.asyncio.ConnectionPool.from_url(app.sync_server_url, socket_timeout = app.sync_server_timeout, socket_connect_timeout = app.sync_server_timeout))
479
479
 
480
480
  class CheeseAPI:
481
481
  __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')
@@ -1,4 +1,4 @@
1
- import inspect
1
+ import inspect, os
2
2
  import datetime, uuid, threading, multiprocessing, asyncio, time, json
3
3
  from typing import Callable, Literal, TYPE_CHECKING
4
4
 
@@ -228,12 +228,13 @@ class Scheduler:
228
228
  return self._proxy.get_tasks()
229
229
 
230
230
  class SchedulerProxy:
231
- __slots__ = ('app', '_tasks')
231
+ __slots__ = ('app', '_tasks', '_pubsub_ready')
232
232
 
233
233
  def __init__(self, app: 'CheeseAPI'):
234
234
  self.app: 'CheeseAPI' = app
235
235
 
236
236
  self._tasks: dict[str, Task] = {}
237
+ self._pubsub_ready: bool = False
237
238
 
238
239
  def __getstate__(self):
239
240
  return None, {
@@ -243,26 +244,14 @@ class SchedulerProxy:
243
244
  def __setstate__(self, state):
244
245
  self.app = state[1]['app']
245
246
  self._tasks = {}
246
-
247
- def init(self, app = None):
248
- if not app:
249
- app = self.app
250
-
251
- if app.sync_server_url:
252
- static.scheduler_sync_servers = (redis.ConnectionPool.from_url(app.sync_server_url), redis.asyncio.ConnectionPool.from_url(app.sync_server_url))
253
- threading.Thread(target = self._start_pubsub, args = (app,), daemon = True).start()
254
- coro = self._async_start_pubsub(app)
255
- try:
256
- asyncio.create_task(coro)
257
- except RuntimeError:
258
- coro.close()
247
+ self._pubsub_ready = False
259
248
 
260
249
  def _start_pubsub(self, app: 'CheeseAPI'):
261
- pubsub = redis.Redis(connection_pool = static.scheduler_sync_servers[0]).pubsub()
262
- pubsub.subscribe('CheeseAPI_scheduler')
263
250
  try:
264
251
  while True:
265
252
  try:
253
+ pubsub = redis.from_url(app.sync_server_url, socket_timeout = None, socket_connect_timeout = None).pubsub()
254
+ pubsub.subscribe('CheeseAPI_scheduler')
266
255
  for message in pubsub.listen():
267
256
  if message['type'] == 'message':
268
257
  data = json.loads(message['data'])
@@ -280,16 +269,15 @@ class SchedulerProxy:
280
269
  except redis.exceptions.RedisError:
281
270
  pubsub.close()
282
271
  time.sleep(app.sync_server_timeout)
283
- self._start_pubsub(app)
284
272
  except (KeyboardInterrupt, SystemExit):
285
273
  ...
286
274
 
287
275
  async def _async_start_pubsub(self, app: 'CheeseAPI'):
288
- pubsub = redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).pubsub()
289
- await pubsub.subscribe('CheeseAPI_scheduler')
290
276
  try:
291
277
  while True:
292
278
  try:
279
+ pubsub = redis.asyncio.from_url(app.sync_server_url, socket_timeout = None, socket_connect_timeout = None).pubsub()
280
+ await pubsub.subscribe('CheeseAPI_scheduler')
293
281
  async for message in pubsub.listen():
294
282
  if message['type'] == 'message':
295
283
  data = json.loads(message['data'])
@@ -307,12 +295,20 @@ class SchedulerProxy:
307
295
  except redis.exceptions.RedisError:
308
296
  await pubsub.close()
309
297
  await asyncio.sleep(app.sync_server_timeout)
310
- await self._async_start_pubsub(app)
311
298
  except (KeyboardInterrupt, SystemExit):
312
299
  ...
313
300
 
314
301
  def add(self, interval_time: float, fn: Callable | None = None, *, first_run_timer: datetime.datetime | None = None, expected_run_num: int | None = None, key: str | None = None, run_type: Literal['THREAD', 'PROCESS'] = 'THREAD', args: tuple = (), kwargs: dict = {}, auto_remove: bool = False, timeout: float | None = None) -> Callable | Task:
315
302
  if fn:
303
+ if not self._pubsub_ready:
304
+ self._pubsub_ready = True
305
+ threading.Thread(target = self._start_pubsub, args = (self.app,), daemon = True).start()
306
+ coro = self._async_start_pubsub(self.app)
307
+ try:
308
+ asyncio.create_task(coro)
309
+ except RuntimeError:
310
+ coro.close()
311
+
316
312
  task = Task(fn, interval_time, first_run_timer = first_run_timer, expected_run_num = expected_run_num, key = key, run_type = run_type, args = args, kwargs = kwargs, auto_remove = auto_remove, timeout = timeout, _scheduler_proxy = self)
317
313
  task._queue.put(None)
318
314
 
@@ -333,6 +329,15 @@ class SchedulerProxy:
333
329
 
334
330
  async def async_add(self, interval_time: float | None = None, fn: Callable | None = None, *, first_run_timer: datetime.datetime | None = None, expected_run_num: int | None = None, key: str | None = None, args: tuple = (), kwargs: dict = {}, auto_remove: bool = False, timeout: float | None = None) -> Callable | Task:
335
331
  if fn is not None:
332
+ if not self._pubsub_ready:
333
+ self._pubsub_ready = True
334
+ threading.Thread(target = self._start_pubsub, args = (self.app,), daemon = True).start()
335
+ coro = self._async_start_pubsub(self.app)
336
+ try:
337
+ asyncio.create_task(coro)
338
+ except RuntimeError:
339
+ coro.close()
340
+
336
341
  task = Task(fn, interval_time = interval_time, first_run_timer = first_run_timer, expected_run_num = expected_run_num, key = key, run_type = 'ASYNC', args = args, kwargs = kwargs, auto_remove = auto_remove, timeout = timeout, _scheduler_proxy = self)
337
342
  task._queue.put(None)
338
343
 
@@ -351,24 +356,16 @@ class SchedulerProxy:
351
356
  return _fn
352
357
  return wrapper
353
358
 
354
- def task_processing(self, key: str, queue: multiprocessing.Queue, fn, *args, **kwargs):
359
+ def task_processing(self, task: Task, queue: multiprocessing.Queue, fn, *args, **kwargs):
355
360
  try:
356
- task = self.get_task(key)
357
- if not task:
358
- return
359
-
360
361
  queue.get()
361
- first_run = True
362
362
 
363
+ if static.scheduler_sync_servers:
364
+ self.get_task(task.key)._queue.get()
363
365
  if task.first_run_timer:
364
366
  time.sleep(max(0, task.first_run_timer.timestamp() - time.time()))
365
367
 
366
368
  while not queue.qsize():
367
- if static.scheduler_sync_servers:
368
- task = self.get_task(key)
369
- if (not task or (not first_run and task._queue.qsize())):
370
- break
371
-
372
369
  now = time.time()
373
370
 
374
371
  try:
@@ -381,49 +378,37 @@ class SchedulerProxy:
381
378
  except Exception as e:
382
379
  self.app.printer.scheduler_error(e, task)
383
380
 
384
- if static.scheduler_sync_servers:
385
- task = self.get_task(key)
386
-
387
- if task:
388
- task._last_run_time = time.time() - now
389
- task._last_run_timer = datetime.datetime.fromtimestamp(now)
390
- task._run_num += 1
391
- if first_run:
392
- task._queue.get()
381
+ if queue.qsize():
382
+ break
393
383
 
394
- if static.scheduler_sync_servers:
395
- sync_server = redis.Redis(connection_pool = static.scheduler_sync_servers[0])
396
- sync_server.hset('CheeseAPI_scheduler_tasks', key, json.dumps(task._to_dict()))
397
- sync_server.hpexpire('CheeseAPI_scheduler_tasks', int(task.timeout * 1000), key)
384
+ task._last_run_time = time.time() - now
385
+ task._last_run_timer = datetime.datetime.fromtimestamp(now)
386
+ task._run_num += 1
398
387
 
399
- if (not task or task._queue.qsize()) or task.run_num_completed:
400
- break
388
+ if static.scheduler_sync_servers:
389
+ sync_server = redis.Redis(connection_pool = static.scheduler_sync_servers[0])
390
+ sync_server.hset('CheeseAPI_scheduler_tasks', task.key, json.dumps(task._to_dict()))
391
+ sync_server.hpexpire('CheeseAPI_scheduler_tasks', int(task.timeout * 1000), task.key)
401
392
 
402
- first_run = False
403
393
  time.sleep(max(0, task.interval_time - time.time() + now))
404
394
  except (KeyboardInterrupt, SystemExit):
405
395
  ...
406
396
 
407
- if static.scheduler_sync_servers and task:
408
- redis.Redis(connection_pool = static.scheduler_sync_servers[0]).hpersist('CheeseAPI_scheduler_tasks', key)
397
+ if static.scheduler_sync_servers:
398
+ _redis = redis.Redis(connection_pool = static.scheduler_sync_servers[0])
399
+ if _redis.hexists('CheeseAPI_scheduler_tasks', task.key):
400
+ _redis.hpersist('CheeseAPI_scheduler_tasks', task.key)
409
401
 
410
402
  async def async_task_processing(self, key: str, queue: multiprocessing.Queue, fn, *args, **kwargs):
411
- task = self.get_task(key)
412
- if not task:
413
- return
414
-
415
403
  queue.get()
416
- first_run = True
417
404
 
405
+ task = await self.async_get_task(key)
406
+ if static.scheduler_sync_servers:
407
+ task._queue.get()
418
408
  if task.first_run_timer:
419
409
  await asyncio.sleep(max(0, task.first_run_timer.timestamp() - time.time()))
420
410
 
421
411
  while not queue.qsize():
422
- if static.scheduler_sync_servers:
423
- task = await self.async_get_task(key)
424
- if (not task or (not first_run and task._queue.qsize())):
425
- break
426
-
427
412
  now = time.time()
428
413
 
429
414
  try:
@@ -436,34 +421,32 @@ class SchedulerProxy:
436
421
  except Exception as e:
437
422
  self.app.printer.scheduler_error(e, task)
438
423
 
439
- if static.scheduler_sync_servers:
440
- task = await self.async_get_task(key)
424
+ if queue.qsize():
425
+ break
441
426
 
442
- if task:
443
- task._last_run_time = time.time() - now
444
- task._last_run_timer = datetime.datetime.fromtimestamp(now)
445
- task._run_num += 1
446
- if first_run:
447
- task._queue.get()
427
+ task._last_run_time = time.time() - now
428
+ task._last_run_timer = datetime.datetime.fromtimestamp(now)
429
+ task._run_num += 1
448
430
 
449
- if static.scheduler_sync_servers:
450
- sync_server = redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1])
451
- await sync_server.hset('CheeseAPI_scheduler_tasks', key, json.dumps(task._to_dict()))
452
- await sync_server.hpexpire('CheeseAPI_scheduler_tasks', int(task.timeout * 1000), key)
431
+ if static.scheduler_sync_servers:
432
+ sync_server = redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1])
433
+ await sync_server.hset('CheeseAPI_scheduler_tasks', key, json.dumps(task._to_dict()))
434
+ await sync_server.hpexpire('CheeseAPI_scheduler_tasks', int(task.timeout * 1000), key)
453
435
 
454
- if (not task or task._queue.qsize()) or task.run_num_completed:
436
+ if task.run_num_completed:
455
437
  break
456
438
 
457
- first_run = False
458
439
  await asyncio.sleep(max(0, task.interval_time - time.time() + now))
459
440
 
460
- if not task or task.auto_remove:
441
+ if task.auto_remove:
461
442
  self._tasks.pop(key, None)
462
443
  if static.scheduler_sync_servers is not None:
463
444
  await redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).hdel('CheeseAPI_scheduler_tasks', key)
464
445
  else:
465
- if static.scheduler_sync_servers is not None:
466
- await redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).hpersist('CheeseAPI_scheduler_tasks', key)
446
+ if static.scheduler_sync_servers:
447
+ _redis = redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1])
448
+ if await _redis.hexists('CheeseAPI_scheduler_tasks', key):
449
+ await _redis.hpersist('CheeseAPI_scheduler_tasks', key)
467
450
 
468
451
  def join(self, task: Task):
469
452
  if task.run_type == 'THREAD' and isinstance(task._handler, threading.Thread):
@@ -487,9 +470,9 @@ class SchedulerProxy:
487
470
  return
488
471
 
489
472
  if task.run_type == 'THREAD':
490
- task._handler = threading.Thread(target = self.task_processing, args = (key, task._queue, task.fn, *task.args), kwargs = task.kwargs, daemon = True)
473
+ task._handler = threading.Thread(target = self.task_processing, args = (task, task._queue, task.fn, *task.args), kwargs = task.kwargs, daemon = True)
491
474
  elif task.run_type == 'PROCESS':
492
- task._handler = multiprocessing.get_context('spawn').Process(target = self.task_processing, args = (key, task._queue, task.fn, *task.args), kwargs = task.kwargs, daemon = True)
475
+ task._handler = multiprocessing.get_context('spawn').Process(target = self.task_processing, args = (task, task._queue, task.fn, *task.args), kwargs = task.kwargs, daemon = True)
493
476
  task._handler.start()
494
477
 
495
478
  if task.auto_remove:
@@ -515,29 +498,31 @@ class SchedulerProxy:
515
498
  if not task.is_running:
516
499
  raise KeyError(f'Task with key "{key}" is not running')
517
500
 
518
- _task = self._tasks.get(key)
519
- if not _task:
501
+ local_task = self._tasks.get(key)
502
+ if not local_task:
520
503
  if static.scheduler_sync_servers:
521
504
  redis.Redis(connection_pool = static.scheduler_sync_servers[0]).publish('CheeseAPI_scheduler', json.dumps(['stop', key]))
522
505
  else:
523
- _task._queue.put(None)
524
- if static.scheduler_sync_servers:
525
- redis.Redis(connection_pool = static.scheduler_sync_servers[0]).hset('CheeseAPI_scheduler_tasks', key, json.dumps(task._to_dict()))
506
+ local_task._queue.put(None)
507
+ if static.scheduler_sync_servers:
508
+ redis.Redis(connection_pool = static.scheduler_sync_servers[0]).hset('CheeseAPI_scheduler_tasks', key, json.dumps(task._to_dict()))
509
+
510
+ time.sleep(self.app.sync_server_timeout)
526
511
 
527
512
  def remove(self, key: str):
528
513
  task = self.get_task(key)
529
514
  if not task:
530
515
  return
531
516
 
532
- _task = self._tasks.get(key)
533
- if not _task:
517
+ local_task = self._tasks.get(key)
518
+ if not local_task:
534
519
  if static.scheduler_sync_servers:
535
520
  redis.Redis(connection_pool = static.scheduler_sync_servers[0]).publish('CheeseAPI_scheduler', json.dumps(['remove', key]))
536
521
  else:
537
- _task._queue.put(None)
522
+ local_task._queue.put(None)
538
523
  self._tasks.pop(key, None)
539
- if static.scheduler_sync_servers:
540
- redis.Redis(connection_pool = static.scheduler_sync_servers[0]).hdel('CheeseAPI_scheduler_tasks', key)
524
+ if static.scheduler_sync_servers:
525
+ redis.Redis(connection_pool = static.scheduler_sync_servers[0]).hdel('CheeseAPI_scheduler_tasks', key)
541
526
 
542
527
  async def async_stop(self, key: str):
543
528
  task = await self.async_get_task(key)
@@ -546,29 +531,29 @@ class SchedulerProxy:
546
531
  if not task.is_running:
547
532
  raise KeyError(f'Task with key "{key}" is not running')
548
533
 
549
- _task = self._tasks.get(key)
550
- if not _task:
534
+ local_task = self._tasks.get(key)
535
+ if not local_task:
551
536
  if static.scheduler_sync_servers:
552
537
  await redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).publish('CheeseAPI_scheduler', json.dumps(['stop', key]))
553
538
  else:
554
- _task._queue.put(None)
555
- if static.scheduler_sync_servers:
556
- await redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).hset('CheeseAPI_scheduler_tasks', key, json.dumps(task._to_dict()))
539
+ local_task._queue.put(None)
540
+ if static.scheduler_sync_servers:
541
+ await redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).hset('CheeseAPI_scheduler_tasks', key, json.dumps(task._to_dict()))
557
542
 
558
543
  async def async_remove(self, key: str):
559
544
  task = await self.async_get_task(key)
560
545
  if not task:
561
546
  return
562
547
 
563
- _task = self._tasks.get(key)
564
- if not _task:
548
+ local_task = self._tasks.get(key)
549
+ if not local_task:
565
550
  if static.scheduler_sync_servers:
566
551
  await redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).publish('CheeseAPI_scheduler', json.dumps(['remove', key]))
567
552
  else:
568
- _task._queue.put(None)
553
+ local_task._queue.put(None)
569
554
  self._tasks.pop(key, None)
570
- if static.scheduler_sync_servers:
571
- await redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).hdel('CheeseAPI_scheduler_tasks', key)
555
+ if static.scheduler_sync_servers:
556
+ await redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).hdel('CheeseAPI_scheduler_tasks', key)
572
557
 
573
558
  def get_task(self, key: str) -> Task | None:
574
559
  if static.scheduler_sync_servers is not None:
@@ -599,7 +584,7 @@ class SchedulerProxy:
599
584
  async def async_get_tasks(self) -> dict[str, Task]:
600
585
  if static.scheduler_sync_servers is not None:
601
586
  return {
602
- key: Task.from_dict(json.loads(data), self) for key, data in (await redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).hgetall('CheeseAPI_scheduler_tasks')).items()
587
+ key.decode(): Task.from_dict(json.loads(data), self) for key, data in (await redis.asyncio.Redis(connection_pool = static.scheduler_sync_servers[1]).hgetall('CheeseAPI_scheduler_tasks')).items()
603
588
  }
604
589
 
605
590
  return self._tasks
@@ -57,7 +57,7 @@ def validator(*, json_model: pydantic.BaseModel | None = None, form_model: pydan
57
57
  else:
58
58
  return Response(json.loads(e.json()), 400)
59
59
 
60
- signature = inspect.signature(fn)
60
+ signature = inspect.signature(fn, follow_wrapped = False)
61
61
  if 'request' in signature.parameters or any(p.kind == inspect.Parameter.VAR_KEYWORD for p in signature.parameters.values()):
62
62
  return await fn(*args, request = request, **kwargs)
63
63
  else:
@@ -2,7 +2,7 @@ import base64, hashlib, asyncio, ssl, struct, json
2
2
  from functools import partial
3
3
  from typing import TYPE_CHECKING, AsyncIterable, Self
4
4
 
5
- import redis
5
+ import redis, redis.exceptions
6
6
 
7
7
  from CheeseAPI import static
8
8
  from CheeseAPI.response import Response
@@ -230,7 +230,7 @@ class WebsocketProxy:
230
230
  async def connect(self) -> AsyncIterable[Response]:
231
231
  Websocket.connectors.setdefault(self.websocket.request.path, []).append(self.websocket)
232
232
  if self.app.sync_server_url and self.websocket.request.path not in static.websocket_sync_server:
233
- static.websocket_sync_server[self.websocket.request.path] = redis.asyncio.Redis.from_url(self.app.sync_server_url)
233
+ static.websocket_sync_server[self.websocket.request.path] = redis.asyncio.Redis.from_url(self.app.sync_server_url, socket_timeout = self.app.sync_server_timeout, socket_connect_timeout = self.app.sync_server_timeout)
234
234
  asyncio.create_task(self.sync_server_running())
235
235
 
236
236
  loop = asyncio.get_running_loop()
@@ -246,35 +246,42 @@ class WebsocketProxy:
246
246
  await self.websocket.on_connect()
247
247
 
248
248
  async def sync_server_running(self):
249
- if self.app.sync_server_url.startswith('redis'):
250
- pubsub = static.websocket_sync_server[self.websocket.request.path].pubsub()
251
- await pubsub.subscribe(self.websocket.request.path)
252
- async for message in pubsub.listen():
253
- if message['type'] != 'message':
254
- continue
255
-
256
- connectors = Websocket.connectors.get(self.websocket.request.path)
257
- if not connectors:
258
- continue
259
-
260
- message = message['data']
261
- if static.websocket_data_decode is not None:
262
- message = static.websocket_data_decode(message)
263
- message = json.loads(message.decode())
264
- if isinstance(message['key'], list):
265
- connectors = [connector for connector in connectors if connector.key in message['key']]
266
- elif isinstance(message['key'], str):
267
- connectors = [connector for connector in connectors if connector.key == message['key']]
268
- else:
269
- connectors = [connector for connector in connectors]
270
-
271
- if connectors:
272
- if message['event'] == 'send':
273
- for connector in connectors:
274
- asyncio.create_task(connector.send(message['data']))
275
- elif message['event'] == 'close':
276
- for connector in connectors:
277
- asyncio.create_task(connector.close())
249
+ try:
250
+ if self.app.sync_server_url.startswith('redis'):
251
+ while True:
252
+ try:
253
+ pubsub = redis.asyncio.from_url(self.app.sync_server_url, socket_timeout = None, socket_connect_timeout = None).pubsub()
254
+ await pubsub.subscribe(self.websocket.request.path)
255
+ async for message in pubsub.listen():
256
+ if message['type'] != 'message':
257
+ continue
258
+
259
+ connectors = Websocket.connectors.get(self.websocket.request.path)
260
+ if not connectors:
261
+ continue
262
+
263
+ message = message['data']
264
+ if static.websocket_data_decode is not None:
265
+ message = static.websocket_data_decode(message)
266
+ message = json.loads(message.decode())
267
+ if isinstance(message['key'], list):
268
+ connectors = [connector for connector in connectors if connector.key in message['key']]
269
+ elif isinstance(message['key'], str):
270
+ connectors = [connector for connector in connectors if connector.key == message['key']]
271
+ else:
272
+ connectors = [connector for connector in connectors]
273
+
274
+ if connectors:
275
+ if message['event'] == 'send':
276
+ for connector in connectors:
277
+ asyncio.create_task(connector.send(message['data']))
278
+ elif message['event'] == 'close':
279
+ for connector in connectors:
280
+ asyncio.create_task(connector.close())
281
+ except redis.exceptions.RedisError:
282
+ await asyncio.sleep(self.app.sync_server_timeout)
283
+ except (KeyboardInterrupt, SystemExit, asyncio.CancelledError):
284
+ ...
278
285
 
279
286
  async def message(self):
280
287
  while True:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: CheeseAPI
3
- Version: 2.0.7b4
3
+ Version: 2.0.8b2
4
4
  Summary: 一款web协程框架
5
5
  Project-URL: Source, https://github.com/CheeseUnknown/CheeseAPI
6
6
  Author-email: Cheese Unknown <cheese@cheese.ren>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "CheeseAPI"
7
- version = "2.0.7-beta.4"
7
+ version = "2.0.8-beta.2"
8
8
  description = "一款web协程框架"
9
9
  readme = "README.md"
10
10
  license-files = { paths = [ "LICENSE" ] }
File without changes
File without changes
File without changes