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.
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/__init__.py +1 -1
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/app.py +3 -3
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/scheduler.py +85 -100
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/validator.py +1 -1
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/websocket.py +38 -31
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/PKG-INFO +1 -1
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/pyproject.toml +1 -1
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/.gitignore +0 -0
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/cors.py +0 -0
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/file.py +0 -0
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/printer.py +0 -0
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/request.py +0 -0
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/response.py +0 -0
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/route.py +0 -0
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/signal.py +0 -0
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/CheeseAPI/static.py +0 -0
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/LICENSE +0 -0
- {cheeseapi-2.0.7b4 → cheeseapi-2.0.8b2}/README.md +0 -0
|
@@ -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
|
-
|
|
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,
|
|
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
|
|
385
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
|
400
|
-
|
|
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
|
|
408
|
-
redis.Redis(connection_pool = static.scheduler_sync_servers[0])
|
|
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
|
|
440
|
-
|
|
424
|
+
if queue.qsize():
|
|
425
|
+
break
|
|
441
426
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
|
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
|
|
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
|
|
466
|
-
|
|
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 = (
|
|
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 = (
|
|
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
|
-
|
|
519
|
-
if not
|
|
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
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
|
|
533
|
-
if not
|
|
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
|
-
|
|
522
|
+
local_task._queue.put(None)
|
|
538
523
|
self._tasks.pop(key, None)
|
|
539
|
-
|
|
540
|
-
|
|
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
|
-
|
|
550
|
-
if not
|
|
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
|
-
|
|
555
|
-
|
|
556
|
-
|
|
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
|
-
|
|
564
|
-
if not
|
|
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
|
-
|
|
553
|
+
local_task._queue.put(None)
|
|
569
554
|
self._tasks.pop(key, None)
|
|
570
|
-
|
|
571
|
-
|
|
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
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|