【问题标题】:Python 3.6 AST module doesn't recognize async methodsPython 3.6 AST 模块无法识别异步方法
【发布时间】:2017-07-06 18:35:37
【问题描述】:

Transcrypt Python to JavaScript 编译器中实现协程时,我遇到了以下奇怪的问题。

Transcrypt 使用 CPython 3.6 的本机解析器来生成 AST。 对于异步全局函数 defs,它会生成一个 AsyncFunctionDef 节点。 但是对于异步方法,它没有! 尽管如此,CPython 本身似乎可以正确编译异步方法。

所以下面这段代码使用 CPython 运行,但 Transcrypt 无法运行它,因为 CPython 的 AST 模块生成的 AST 似乎缺少方法的 AsyncFunctionDef 节点(与全局函数相反)。

所以以下代码不会生成 AsyncFunctionDef 节点:

class C:
    def __init__ (self):
        self.aTime = 2

    async def g (self, waw, asio):
        print ('g0')
        await waw (self.aTime, asio)
        print ('g1')

我错过了什么?官方支持异步方法,不是吗? 在PEP 492 中找不到任何具体内容。

示例完整代码为:

from org.transcrypt.stubs.browser import __pragma__, __envir__

# Note that CPython will ignore all pragma's



# Provide waitAWhile for Transcrypt

__pragma__ ('js', '{}', '''
    function waitAWhile (aTime, asio) {
      return new Promise (resolve => {
        setTimeout (() => {
          resolve (aTime);
        }, 1000 * aTime);
      });
    }
''')



# Provide waitAWhile for CPython

__pragma__ ('skip') # Compile time, needed because import is done compile time

import asyncio

def waitAWhile (aTime, asio):
    return asio.sleep (aTime)

__pragma__ ('noskip')



# Actual code to be tested    

async def f (waw, asio):
    print ('f0')
    await waw (2, asio)
    print ('f1')

class C:
    def __init__ (self):
        self.aTime = 2

    async def g (self, waw, asio):
        print ('g0')
        await waw (self.aTime, asio)
        print ('g1')

c = C ()


# Just call async functions for Transcrypt, since in the browser JavaScript is event driven by default

if __envir__.executor_name == __envir__.transpiler_name:
    f (waitAWhile, None)
    c.g (waitAWhile, None)
    c.g (waitAWhile, None)
    f (waitAWhile, None)



# Create event loop and tasks for CPython, since it isn't event driven by default

else:
    eventLoop = asyncio.get_event_loop ()
    tasks = [
        eventLoop.create_task (f (waitAWhile, asyncio)),
        eventLoop.create_task (c.g (waitAWhile, asyncio)),
        eventLoop.create_task (c.g (waitAWhile, asyncio)),
        eventLoop.create_task (f (waitAWhile, asyncio)),
    ]

    waitingTasks = asyncio.wait (tasks)
    eventLoop.run_until_complete (waitingTasks)
    eventLoop.close ()

【问题讨论】:

    标签: python async-await transcrypt


    【解决方案1】:

    最终我让解析器正常工作。 我最初一定是在其他地方阻止了解析。 可能我忘了从树上更高的节点调用visit。 不幸的是,我无法再重现该问题。

    对于那些感兴趣的人,解析器代码位于:

    https://github.com/QQuick/Transcrypt/blob/master/transcrypt/modules/org/transcrypt/compiler.py

    第 2045 行。

    最重要的是:Python 的 ast 模块运行良好,尽管它可以使用更多文档。

    有一个(非常紧凑但可用的)第 3 方文档:

    https://greentreesnakes.readthedocs.io/en/latest/

    【讨论】:

    • 手动调用 self.visit 将很难维护,最好在覆盖方法结束时调用 self.generic_visit(node)
    • 我不确定我是否理解正确。文档说:generic_visit(node) 这个访问者在节点的所有子节点上调用 visit()。但在很多情况下,我不想去看所有的孩子。
    • 写了很多NodeVisitors,通常“不想拜访所有孩子”是特例,在这种情况下你可以早点return(无)。我在编写NodeVisitors 时发现的大多数错误都与此问题中的描述完全相同,并且由于缺少visit / generic_visit——我一直坚持的一个简单的最佳实践是始终拥有self.generic_visit(node)作为我的任何visit_* 方法的最后一行。
    • 谢谢,我在使用解析器时会记住这一点!
    猜你喜欢
    • 1970-01-01
    • 2013-08-16
    • 2017-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-08
    • 1970-01-01
    • 2018-11-27
    相关资源
    最近更新 更多