如何将像 JSX 这样的语言嵌入到 python 脚本中?

tri*_*ixn 8 python parsing interpreter template-engine jsx

在我们公司,我们喜欢编写 django 驱动的应用程序,我们也喜欢使用 React。最近,我们考虑为 Python 编写一个基于组件的模板引擎,其中可以使用 JSX 将模板编写为类似 React 的组件。

理想情况下,应该可以将 JSX 嵌入到 python 代码中,以便您可以编写如下组件:

header.pyx

import PyReact
from my_awsome_project.components import Logo, Link


def Header(context):
    page_title = context.get('page_title')
    links = context.get('links')

    return (
        <div>
            <Logo /> 
            {page_title}
            <ul>
                {[<Link href={link.url}>{link.title}</Link> for link in links]}
            </ul>
        </div>
    )
Run Code Online (Sandbox Code Playgroud)

这当然需要首先转译文件以获得有效的 python 代码。它会转换为有点类似的东西:

import PyReact
from my_awsome_project.components import Logo, Link


def Header(context):
    page_title = context.get('page_title')
    links = context.get('links')

    return (
        PyReact.createComponent('div', context, [
            PyReact.createComponent(Logo),
            page_title,
            PyReact.createComponent('ul', context, [
                 [
                      PyReact.createComponent(Link, {'href': link.url}, link.title)
                      for link in links
                 ]
            ]),
        ])
    )
Run Code Online (Sandbox Code Playgroud)

问题是:我将如何编写这样的转译器?

我们还考虑过,我们可以返回一个包含独立解析的 JSX 的字符串,而不是将 JSX 直接嵌入到 python 代码中。这是一个更好/更简单的方法吗?

osp*_*der 7

事实上,这是可能的。有一个名为:packed[1] 的库。但该项目似乎已被放弃,最后一次提交是 5 年前。引用它的自述文件:

@packed
def tag(self):
    share = get_share_link()
    return <a href={share}>Share on internet</a>

# to:

@packed
def tag(self):
   share = get_share_link()
   return Elem(
        'a',
        {
            'href': share,
        },
        'Share on internet',
    )
Run Code Online (Sandbox Code Playgroud)

不管怎样,如果你正在寻找一种除了 jinja 模板之外通过 python 在服务器端渲染 html 的新方法,看看 htmx,它看起来很有前途。

  1. https://github.com/michaeljones/packed

  • 我最近在寻找类似的东西,想知道为什么这种方法没有变得更流行,并发现了另外两个具有类似想法的库: - https://github.com/gvanrossum/pyxl3 - Pyxl 是一个开源包扩展 Python 以支持内联 HTML。(还有一个分叉,https://github.com/pyxl4/pyxl4,但两者似乎都或多或少被废弃了。) - https://github.com/twidi/mixt/ - 直接用 python 编写 html 组件,你就可以了美丽但有争议的混合物。[...]基于pyxl。(这个看起来更新,也更活跃。) (5认同)
  • 我真的希望有一个专门用于“X 是否有库?”的堆栈交换。输入问题,因为它们总是在这里被关闭,这真的很烦人;这是我想问这样的问题的第一个地方,因为这里到处都是知道要推荐哪些库的人。 (4认同)

ric*_*ici 3

我认为这对于 SO 来说基本上是一个太宽泛的问题,任何答案都会在 SO 关于意见的指导方针的边缘滑冰。您本质上是在寻求有关复杂问题的设计建议,而 SO 并不是真正用于此目的。

尽管如此,这仍然是一个有趣的问题。我将尝试解决这些问题,而不会冒险太深入固执己见的设计(因为我确实对这个主题有意见)。

  1. 转译是实用的,至少在理论上是这样,如果你能成功,它将给你带来合理的性能。

  2. 反复重新解析模板字符串给我的印象是效率低下且复杂;复杂性与评估嵌入的 Python 代码有关,您需要在定义字符串文字的范围内执行此操作,这可能不是解析字符串的范围。

  3. JSX 风格的词法分析和解析并不是特别复杂,但是您假设的转译器还需要了解 Python 词法和句法分析。Python 的标准库包含用于词法分析和解析 Python 的模块,但据我所知,它们不可扩展,这可能会使它们很难与嵌入式语言一起使用。您可以编写自己的词法分析器和解析器(可能使用您选择的代码生成器),也可以将词法分析器和解析器基于某些开源 Python 实现。在这两种情况下,您的可维护性挑战将是使您的自定义代码与未来的 Python 版本保持同步。

  4. 将伪 HTML 嵌入到另一种语言中的主要问题是检测 a 何时<是比较运算符以及何时启动模板。<最简单的解决方案是仅当被词法分析为完整标记(因此<=始终是运算符)、后跟标识符并且在需要表达式的语法环境中遇到时才允许使用模板。

  5. 上面的最后一个要求是确保3 < count(例如)不会欺骗转译器认为它将看到一个<count...>组件。我很确定在 Python 中你可以使用基于前面标记的简单词法规则,但是需要完整的语法分析来验证这一点

  6. 一旦启动模板,它将继续,直到到达匹配的结束标记;如果需要标签匹配,那就非常简单了。但它比自下而上的解析更适合自上而下的解析,因为结束标记匹配是上下文相关的。如果词法分析和句法分析之间密切合​​作,那么这很容易做到,但这种合作有时会令人不悦:-)

  7. 由于嵌入模板中的 Python 代码本身可以包含嵌入模板,而该模板又可以嵌入更多 Python 代码等,因此您的分析将需要递归。预期的递归深度不是很大,因此递归本身没有问题,但许多解析器生成器不能优雅地处理这种递归。我建议使用(或实现)一个“推送解析器”和一个与缓冲区处理程序分离的词法分析器框架,这样您就可以轻松地更改缓冲区中间的扫描器。

  8. 缓冲区处理可以非常简单;最低要求只是一个字符串和该字符串的索引。如果您在缓冲区处理程序中隔离实现细节,那么您应该能够稍后更改为不同的实现,例如不需要在开始解析之前提供整个输入的实现。您可能实际上并不需要该功能,但维护独立组件总是好的,以防万一

  9. 转译器面临的另一个挑战是将其与 Python 的模块系统集成。Pythonic 集成可能建议在导入模块时执行转译。另一方面,您可能希望能够分发预转译包,该包可以在不安装转译器的情况下使用,并且不依赖于转译器的特定版本。如果您花一些时间思考这一点,您可能会避免以后出现问题。(例如,Ply 问题导致无法将 Ply 项目捆绑到单文件分发系统中。)

希望有点帮助。