def call_at(cls, when, callback, *args, **kwargs): """指定时间调用(能隔离上下文) """ loop = asyncio.events.get_event_loop() if kwargs: return loop.call_at( when, async_adapter( cls.func_partial( callback, *args, **kwargs ) ), context=Context() ) else: return loop.call_at( when, async_adapter(callback), *args, context=Context() )
def _exec_listener_thread_safe(loop: AbstractEventLoop, listener: ListenerCb[K], context: Context, event_instance: K) -> "Future[None]": # Create an internal future to better control the listener result result_future: "Future[None]" = loop.create_future() listener_loop = retrieve_loop_from_listener(listener) or loop if loop is listener_loop: context.run( _exec_listener, loop, result_future.cancel, result_future.set_exception, result_future.set_result, listener, event_instance, ) else: listener_loop.call_soon_threadsafe( context.run, _exec_listener, listener_loop, partial(loop.call_soon_threadsafe, result_future.cancel), partial(loop.call_soon_threadsafe, result_future.set_exception), partial(loop.call_soon_threadsafe, result_future.set_result), listener, event_instance, ) return result_future
def call_soon_threadsafe(cls, callback, *args, **kwargs): """延时调用(线程安全,能隔离上下文) """ loop = asyncio.events.get_event_loop() if kwargs: return loop.call_soon_threadsafe( async_adapter( cls.func_partial( callback, *args, **kwargs ) ), context=Context() ) else: return loop.call_soon_threadsafe( async_adapter(callback), *args, context=Context() )
def call_later(cls, delay, callback, *args, **kwargs): """延时指定秒数调用(能隔离上下文) """ loop = asyncio.events.get_event_loop() if kwargs: return loop.call_later( delay, async_adapter( cls.func_partial( callback, *args, **kwargs ) ), context=Context() ) else: return loop.call_later( delay, async_adapter(callback), *args, context=Context() )
def call_soon(cls, callback, *args, **kwargs): loop = asyncio.events.get_event_loop() if kwargs: return loop.call_soon(async_adapter( cls.func_partial(callback, *args, **kwargs)), context=Context()) else: return loop.call_soon(async_adapter(callback), *args, context=Context())
def _get_context(): state = _get_state() ctx = getattr(state, 'context', None) if ctx is None: ctx = Context() state.context = ctx return ctx
def context(event_loop, request): """Create an empty context for the async test case and it's async fixtures.""" context = Context() def taskfactory(loop, coro): return Task(coro, loop=loop, context=context) event_loop.set_task_factory(taskfactory) return context
def test_context_assignment_different_thread(self): import threading ctx = Context() var = ContextVar("var", default=None) is_running = threading.Event() should_suspend = threading.Event() did_suspend = threading.Event() should_exit = threading.Event() holder = [] def greenlet_in_thread_fn(): var.set(1) is_running.set() should_suspend.wait() var.set(2) getcurrent().parent.switch() holder.append(var.get()) def thread_fn(): gr = greenlet(greenlet_in_thread_fn) gr.gr_context = ctx holder.append(gr) gr.switch() did_suspend.set() should_exit.wait() gr.switch() thread = threading.Thread(target=thread_fn, daemon=True) thread.start() is_running.wait() gr = holder[0] # Can't access or modify context if the greenlet is running # in a different thread with self.assertRaisesRegex(ValueError, "running in a different"): getattr(gr, 'gr_context') with self.assertRaisesRegex(ValueError, "running in a different"): gr.gr_context = None should_suspend.set() did_suspend.wait() # OK to access and modify context if greenlet is suspended self.assertIs(gr.gr_context, ctx) self.assertEqual(gr.gr_context[var], 2) gr.gr_context = None should_exit.set() thread.join() self.assertEqual(holder, [gr, None]) # Context can still be accessed/modified when greenlet is dead: self.assertIsNone(gr.gr_context) gr.gr_context = ctx self.assertIs(gr.gr_context, ctx)
def _run_in_thread(self, future: Awaitable[RetT], ctx: Context) -> RetT: loop = self._local.event_loop ret = ctx.run(loop.run_until_complete, future) return cast(RetT, ret)
def format_image_url_filter(ctx: Context, value: str) -> str: return ctx.get("root_url", "") + "/images" + value
def format_url_filter(ctx: Context, value: str) -> str: return ( ctx.get("root_url", "") + value + ("/index.html" if ctx.get("is_export") else "") )
def test_context_assignment_different_thread(self): import threading VAR_VAR.set(None) ctx = Context() is_running = threading.Event() should_suspend = threading.Event() did_suspend = threading.Event() should_exit = threading.Event() holder = [] def greenlet_in_thread_fn(): VAR_VAR.set(1) is_running.set() should_suspend.wait(10) VAR_VAR.set(2) getcurrent().parent.switch() holder.append(VAR_VAR.get()) def thread_fn(): gr = greenlet(greenlet_in_thread_fn) gr.gr_context = ctx holder.append(gr) gr.switch() did_suspend.set() should_exit.wait(10) gr.switch() del gr greenlet() # trigger cleanup thread = threading.Thread(target=thread_fn, daemon=True) thread.start() is_running.wait(10) gr = holder[0] # Can't access or modify context if the greenlet is running # in a different thread with self.assertRaisesRegex(ValueError, "running in a different"): getattr(gr, 'gr_context') with self.assertRaisesRegex(ValueError, "running in a different"): gr.gr_context = None should_suspend.set() did_suspend.wait(10) # OK to access and modify context if greenlet is suspended self.assertIs(gr.gr_context, ctx) self.assertEqual(gr.gr_context[VAR_VAR], 2) gr.gr_context = None should_exit.set() thread.join(10) self.assertEqual(holder, [gr, None]) # Context can still be accessed/modified when greenlet is dead: self.assertIsNone(gr.gr_context) gr.gr_context = ctx self.assertIs(gr.gr_context, ctx) # Otherwise we leak greenlets on some platforms. # XXX: Should be able to do this automatically del holder[:] gr = None thread = None