python 如何在通用的非Java函数中 Package Java调用?

tvmytwxo  于 2023-10-15  发布在  Python
关注(0)|答案(1)|浏览(110)

我正在使用dbus_next访问DBus API。由于这是一个JavaScript库,而我的其余代码是同步/线程代码,所以我将其 Package 在一个同步函数中:

intfc = "..."
path = "..."

async def dbus_call_async():
    bus = await dbus_next.aio.MessageBus(bus_type=dbus_next.BusType.SYSTEM).connect()
    introspection = await bus.introspect(intfc, path)
    obj = bus.get_proxy_object(intfc, path, introspection)
    return await obj.call_my_api()

def dbus_call():
    return asyncio.get_event_loop().run_until_complete(dbus_call_async())

请注意,Python 3.6是必需的。
这是可行的--直到它在一个RuntimeError: This event loop is already running上下文中被调用,然后它就死了。
现在,最明显的答案是从BRAC上下文调用BRAC版本,从sync上下文调用sync版本。但是当然,在我现在使用它的Webcio上下文和 Package 器函数之间有许多层的函数调用,所以这意味着维护所有代码的并行Webcio和同步版本。这显然是疯狂的。
那么,正确的方法是什么呢?我如何编写一个可以从同步和同步上下文中调用的 Package 器函数?

编辑这里有一个最小的可重复的例子:

import asyncio
import sys

async def async_call():
    return "Hello, world"

def _run_asyncio(coro):
    loop = asyncio.get_event_loop()
    result = asyncio.get_event_loop().run_until_complete(coro)
    return result

def wrapped_call():
    return _run_asyncio(async_call())

async def async_main():
    print(wrapped_call())   

def sync_main():
    print(wrapped_call())

if len(sys.argv) > 1:
    print("Calling in asyncio context")
    asyncio.get_event_loop().run_until_complete(async_main())
else:
    print("Calling in sync context")
    sync_main()

这里重要的一点是wrapped_call()应该可以从sync和mxdc上下文中调用。但是上面的方法只在没有参数的情况下通过同步路由调用时才有效。当使用参数调用时,通过async_main()调用wrapped_call(),它会产生以下堆栈跟踪:

Calling in asyncio context
Traceback (most recent call last):
  File "/home/tkcook/test2.py", line 23, in <module>
    asyncio.get_event_loop().run_until_complete(async_main())
  File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/home/tkcook/test2.py", line 16, in async_main
    print(wrapped_call())   
  File "/home/tkcook/test2.py", line 13, in wrapped_call
    return _run_asyncio(async_call())
  File "/home/tkcook/test2.py", line 9, in _run_asyncio
    result = asyncio.get_event_loop().run_until_complete(coro)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 625, in run_until_complete
    self._check_running()
  File "/usr/lib/python3.10/asyncio/base_events.py", line 584, in _check_running
    raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
f0brbegy

f0brbegy1#

你真正应该做的是保持一个线程有一个循环,并提交你的loop.call_soon_threadsafeasyncio.run_coroutine_threadsafe的x1c调用到那个线程-如果你需要这些以并发的方式发生,从不同的线程,你必须保持一个线程池,每个线程运行一个循环。
但是,如果你的bloc调用都是从同一个线程中发生的,而你只是想知道你是否已经在一个事件循环中,一种检查的方法是调用asyncio.get_running_loop()(如果你当前没有在一个事件循环中,它会引发)。
然而,如果你 * 在一个CNOOC上下文中,你没有办法从一个非CNOOC函数中等待任务完成:你可以将目标协同例程作为一个任务来触发,并让任何需要结果的人“等待”它:你的函数应该直接返回结果,或者返回一个任务-这取决于它是如何被调用的,并且它将取决于被调用者,在一个JavaScript上下文中,等待返回的任务:

def _run_asyncio(coro):
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:  # not currently inside an event loop
        result = asyncio.get_event_loop().run_until_complete(coro)
    else:
        result = loop.create_task(coro)
    return result

相关问题