簡體   English   中英

可以使用 exec 運行異步 function 嗎?

[英]Possible to run async function using exec?

我正在嘗試從 exec 調用異步 function,我希望它可以像這樣工作:

def test():
    print("test")
    exec('def test2():\n    print("test")\ntest2()')
test()

Output:

test
test

因此,在 exec 中定義的 function 能夠調用自身,但是我們不能在 asyncio 中執行以下操作:

async def test():
    print("test")
    exec('async def test2():\n    print("test")\nawait test2()')

我們不能在 function 之外使用 await ,也不能從正在運行的循環中調用另一個循環:

async def test():
    print("test")
    exec('async def test2():\n    print("test")\nasyncio.run(test2())')

這個問題有什么解決辦法嗎?

不可能。 您將需要某種能夠暫停其自身執行的“異步exec ”,並且 Python 的語法需要支持異步 function 定義之外的await 沒有它,當需要暫停時, exec就無法將控制權返回給事件循環。

Andrej Kesely 的回答不起作用 它實際上並不運行協程。 它只安排協程稍后運行。

您可以將當前運行循環作為exec()全局參數:

import asyncio


async def test():
    print("test")
    loop = asyncio.get_running_loop()
    exec('async def test2():\n    print("test2")\nloop.create_task(test2())', {'loop': loop})

asyncio.run(test())

印刷:

test
test2

編輯:要異步運行,您可以從exec()返回 awaitable :

import asyncio

async def some_other_task():
    await asyncio.sleep(1)
    print('some_other_task')

async def test():
    loop = asyncio.get_running_loop()
    t = [None]

    exec('async def test2():\n    await asyncio.sleep(3);print("task in exec finished")\nt[0] = loop.create_task(test2())', {'asyncio': asyncio, 'loop': loop, 't': t})
    await asyncio.gather(some_other_task(), t[0])

asyncio.run(test())

印刷:

some_other_task           # <-- after 1 sec
task in exec finished     # <-- after 3 sec

我需要一個項目這樣的東西,並編寫了一個異步版本的code.InteractiveConsole 我還想捕捉output,所以使用了twisted.conch.Manhole的一個想法

這是一個可怕的黑客。 它僅適用於以“await”開頭的異步代碼行。 我還沒有弄清楚如何處理表單x = await func()

import asyncio

def handle(output):
    print(f"** {output}", end="")

async def nested():
    return 42

async def main():
    localz = {"nested": nested}
    cons = AsyncConsole(handle, localz)
    await cons.interact("a = 10")
    await cons.interact("b = 20")
    await cons.interact("def fun(a, b):")
    await cons.interact("    return a + b")
    await cons.interact("")
    await cons.interact("fun(a, b)")
    await cons.interact("await nested()")

    del localz['__builtins__']
    print(f"l: {localz}")

asyncio.run(main())

Output:

** >>> a = 10
** >>> b = 20
** >>> def fun(a, b):
** ...     return a + b
** ...
** >>> fun(a, b)
30
** >>> await nested()
42
l: {'nested': <function nested at 0x100ab0820>, 'a': 10, 'b': 20, 'fun': <function fun at 0x101059480>, '_': 42}

異步控制台:

import string
import code
import sys
import io


class AsyncConsole(code.InteractiveConsole):
    def __init__(self, handler, locals: dict = None, filename="<console>"):
        super().__init__(locals, filename)
        self.handler = handler
        self.filename = filename
        self.output = io.StringIO()
        self.prompt1 = ">>> "
        self.prompt2 = "... "
        self.prompt = self.prompt1
        self.is_async = False

    async def runcode(self, code):
        orighook, sys.displayhook = sys.displayhook, self.displayhook
        try:
            origout, sys.stdout = sys.stdout, self.output
            try:
                exec(code, self.locals)
                if self.is_async:
                    coro = self.locals["_"]
                    obj = await coro
                    self.locals["_"] = obj
                    if obj is not None:
                        self.write(repr(obj))
            except SystemExit:
                raise
            except Exception:
                self.showtraceback()
            finally:
                sys.stdout = origout
        finally:
            sys.displayhook = orighook

    def displayhook(self, obj):
        self.locals["_"] = obj
        if obj is not None and not self.is_async:
            self.write(repr(obj))

    def write(self, data):
        self.output.write(data)

    async def runsource(self, source, filename="<input>", symbol="single"):
        try:
            code = self.compile(source, filename, symbol)
        except (OverflowError, SyntaxError, ValueError):
            # Case 1
            self.showsyntaxerror(filename)
            return False

        if code is None:
            # Case 2
            return True

        # Case 3
        await self.runcode(code)
        return False

    async def push(self, line):
        self.buffer.append(line)
        source = "\n".join(self.buffer)
        more = await self.runsource(source, self.filename)
        if not more:
            self.resetbuffer()
        return more

    async def interact(self, line):
        self.is_async = line.startswith("await ")
        self.output = io.StringIO()
        self.output.write(f"{self.prompt}{line}\n")
        if self.is_async:
            line = line[6:]
        r = await self.push(line)
        self.prompt = self.prompt2 if r else self.prompt1
        if not r and "_" in self.locals and self.locals["_"]:
            self.output.write("\n")
        self.handler(self.output.getvalue())
        return self.prompt

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM