import asyncio import threading import time from .uv import UVLoop import asyncio def future_handler(future, loop, exception_handler, response): try: future.result() return None except Exception as error: if hasattr(exception_handler, '__call__'): exception_handler(loop, error, response) else: try: #just log in console the error to call attention print("Uncaught Exception: %s" % str(error)) if response != None: response.write_status(500).end("Internal Error") finally: return None return None class Loop: def __init__(self, exception_handler=None): self.loop = asyncio.new_event_loop() self.uv_loop = UVLoop() if hasattr(exception_handler, '__call__'): self.exception_handler = exception_handler self.loop.set_exception_handler(lambda loop, context: exception_handler(loop, context, None)) else: self.exception_handler = None asyncio.set_event_loop(self.loop) self.started = False self.last_defer = False def set_timeout(self, timeout, callback, user_data): return self.uv_loop.create_timer(timeout, 0, callback, user_data) def create_future(self): return self.loop.create_future() def start(self): self.started = True #run asyncio once per tick def tick(loop): #run once asyncio loop.run_once_asyncio() #use check for calling asyncio once per tick self.timer = self.uv_loop.create_timer(0, 1, tick, self) # self.timer = self.uv_loop.create_check(tick, self) def run(self): self.uv_loop.run() def run_once(self): self.uv_loop.run_once() def run_once_asyncio(self): # with suppress(asyncio.CancelledError): #run only one step self.loop.call_soon(self.loop.stop) self.loop.run_forever() def stop(self): if(self.started): self.timer.stop() self.started = False #unbind run_once #if is still running stops if self.loop.is_running(): self.loop.stop() self.last_defer = None # Find all running tasks in main thread: pending = asyncio.all_tasks(self.loop) # Run loop until tasks done self.loop.run_until_complete(asyncio.gather(*pending)) #Exposes native loop for uWS def get_native_loop(self): return self.uv_loop.get_native_loop() def run_async(self, task, response=None): #with run_once future = asyncio.ensure_future(task, loop=self.loop) #with threads future.add_done_callback(lambda f: future_handler(f, self.loop, self.exception_handler, response)) #force asyncio run once to enable req in async functions before first await self.run_once_asyncio() #if response != None: #set auto cork # response.needs_cork = True return future # if sys.version_info >= (3, 11) # with asyncio.Runner(loop_factory=uvloop.new_event_loop) as runner: # runner.run(main()) # else: # uvloop.install() # asyncio.run(main()) #see ./native/uv_selector.txt # will only work on linux and macos # class UVSelector(asyncio.SelectorEventLoop): # def register(self, fileobj, events, data=None): # fd = fileobj.fileno() # if fd == -1: # return None # mask = int(events) # selector_key = (fs, mask, data) # pass # def tick(self): # pass # # We expose our own event loop for use with asyncio # class AsyncioUVLoop(asyncio.SelectorEventLoop): # def __init__(self): # self.selector = UVSelector() # super().__init__(self.selector) # def call_soon(self, *args, **kwargs): # self.selector.tick() # return super().call_soon(*args, **kwargs) # def call_at(self, *args, **kwargs): # self.selector.tick() # return super().call_at(*args, **kwargs) # asyncio.set_event_loop(uws.Loop()) # asyncio.get_event_loop().run_forever()