petter2025 commited on
Commit
eccf061
·
verified ·
1 Parent(s): 3621c2a

Create utils/async_runner.py

Browse files
Files changed (1) hide show
  1. utils/async_runner.py +99 -0
utils/async_runner.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Async utilities for safe async/sync integration
3
+ """
4
+ import asyncio
5
+ import functools
6
+ from typing import Any, Callable, Coroutine
7
+ import logging
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ class AsyncRunner:
12
+ """Safely run async functions in sync context"""
13
+
14
+ @staticmethod
15
+ def run_async(coro: Coroutine) -> Any:
16
+ """
17
+ Run async coroutine in sync context safely
18
+
19
+ Args:
20
+ coro: Async coroutine to run
21
+
22
+ Returns:
23
+ Result of the coroutine
24
+ """
25
+ try:
26
+ # Try to get existing event loop
27
+ loop = asyncio.get_running_loop()
28
+ # We're already in async context - create task
29
+ logger.debug("Running in existing async context")
30
+ future = asyncio.create_task(coro)
31
+ return future
32
+ except RuntimeError:
33
+ # No running loop, create one
34
+ logger.debug("Creating new event loop for async execution")
35
+ loop = asyncio.new_event_loop()
36
+ asyncio.set_event_loop(loop)
37
+ try:
38
+ return loop.run_until_complete(coro)
39
+ finally:
40
+ loop.close()
41
+
42
+ @staticmethod
43
+ def sync_wrapper(async_func: Callable) -> Callable:
44
+ """
45
+ Decorator to make async function callable from sync context
46
+
47
+ Args:
48
+ async_func: Async function to wrap
49
+
50
+ Returns:
51
+ Sync-compatible function
52
+ """
53
+ @functools.wraps(async_func)
54
+ def wrapper(*args, **kwargs) -> Any:
55
+ return AsyncRunner.run_async(async_func(*args, **kwargs))
56
+ return wrapper
57
+
58
+
59
+ def async_to_sync(async_func: Callable) -> Callable:
60
+ """
61
+ Convenience decorator to convert async function to sync
62
+
63
+ Usage:
64
+ @async_to_sync
65
+ async def my_async_function():
66
+ ...
67
+
68
+ # Can now be called synchronously
69
+ result = my_async_function()
70
+ """
71
+ return AsyncRunner.sync_wrapper(async_func)
72
+
73
+
74
+ class SafeEventLoop:
75
+ """Context manager for safe event loop handling"""
76
+
77
+ def __init__(self, create_new: bool = False):
78
+ self.create_new = create_new
79
+ self.loop = None
80
+ self.original_loop = None
81
+
82
+ def __enter__(self):
83
+ if self.create_new:
84
+ self.original_loop = asyncio.get_event_loop_policy().get_event_loop()
85
+ self.loop = asyncio.new_event_loop()
86
+ asyncio.set_event_loop(self.loop)
87
+ else:
88
+ try:
89
+ self.loop = asyncio.get_running_loop()
90
+ except RuntimeError:
91
+ self.loop = asyncio.new_event_loop()
92
+ asyncio.set_event_loop(self.loop)
93
+ return self.loop
94
+
95
+ def __exit__(self, exc_type, exc_val, exc_tb):
96
+ if self.create_new and self.original_loop:
97
+ asyncio.set_event_loop(self.original_loop)
98
+ if not self.loop.is_closed():
99
+ self.loop.close()