­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ ­ """ Helpers/utils for working with tornado asynchronous stuff """ import contextlib import logging import sys import threading import salt.ext.tornado.concurrent import salt.ext.tornado.ioloop log = logging.getLogger(__name__) @contextlib.contextmanager def current_ioloop(io_loop): """ A context manager that will set the current ioloop to io_loop for the context """ orig_loop = salt.ext.tornado.ioloop.IOLoop.current() io_loop.make_current() try: yield finally: orig_loop.make_current() class SyncWrapper: """ A wrapper to make Async classes synchronous This is uses as a simple wrapper, for example: asynchronous = AsyncClass() # this method would regularly return a future future = asynchronous.async_method() sync = SyncWrapper(async_factory_method, (arg1, arg2), {'kwarg1': 'val'}) # the sync wrapper will automatically wait on the future ret = sync.async_method() """ def __init__( self, cls, args=None, kwargs=None, async_methods=None, close_methods=None, loop_kwarg=None, ): self.io_loop = salt.ext.tornado.ioloop.IOLoop(make_current=False) if args is None: args = [] if kwargs is None: kwargs = {} if async_methods is None: async_methods = [] if close_methods is None: close_methods = [] self.loop_kwarg = loop_kwarg self.cls = cls if loop_kwarg: kwargs[self.loop_kwarg] = self.io_loop with current_ioloop(self.io_loop): self.obj = cls(*args, **kwargs) self._async_methods = list( set(async_methods + getattr(self.obj, "async_methods", [])) ) self._close_methods = list( set(close_methods + getattr(self.obj, "close_methods", [])) ) def _populate_async_methods(self): """ We need the '_coroutines' attribute on classes until we can depricate tornado<4.5. After that 'is_coroutine_fuction' will always be available. """ if hasattr(self.obj, "_coroutines"): self._async_methods += self.obj._coroutines def __repr__(self): return f"