贡献指南¶
内容
PyPy 是一个非常庞大的项目,以难以深入研究而闻名。其中一些名声是合理的,而另一些则是纯粹的偶然。对于所有愿意贡献的人来说,有三个重要的教训需要学习
- PyPy 有层次结构。架构中有许多部分彼此之间非常独立。下面会详细介绍,但通常表现为事情处于与你预期不同的层次。例如,如果你正在寻找 JIT 实现,你不会在 Python 编程语言的实现中找到它。
- 由于上述原因,我们非常重视测试驱动开发。这不仅是我们所相信的,而且 PyPy 的架构非常适合 TDD,而没有 TDD 则效果不佳。开发通常意味着在一个无关的角落逐步前进,一次一个单元测试;然后翻转一个巨大的开关,将所有内容整合在一起。(它通常开箱即用。如果不行,那就是我们没有写足够的单元测试。)值得重复的是 - PyPy 的方法在使用 TDD 时非常棒,而在不使用 TDD 时则效果不佳。
- PyPy 使用一组完全不同的工具 - 其中大多数包含在 PyPy 存储库中。没有 Makefile 也没有 autoconf。下面会详细介绍。
首先要记住的是,PyPy 项目与大多数其他项目截然不同。它也与传统的编译器项目不同,因此关于编译器的学术课程通常不适用或会导致错误的方向。但是,如果你想了解在现实世界中设计和构建运行时的工作原理,那么这是一个很棒的项目!
参与¶
PyPy 采用相对标准的开源开发流程。作为第一步,我们鼓励你加入我们的 pypy-dev 邮件列表 和 IRC 频道,详细信息可以在我们的 联系 部分找到。那里的人非常友好,可以为你指明正确的方向。
我们通常会非常慷慨地授予提交权限,因此如果你想用 PyPy 做点什么,你可以通过登录 https://foss.heptapod.net 并点击 PyPy 组页面 上的“请求访问”链接来成为“开发者”。我们还会举办编码冲刺,这些冲刺会单独宣布,通常会在 博客 上宣布。
与任何开源项目一样,问题应该在 问题跟踪器 上提交,并且欢迎 拉取请求 来修复问题。
进一步阅读:联系
你的第一个贡献¶
第一个也是最重要的规则是如何 **不** 贡献 PyPy,那就是“只是黑客一个功能”。这行不通,你会发现你的 PR 通常需要很多重新工作。有几个原因
- 构建时间很长
- PyPy 的层级分离非常厚重
- 通常需要 cPython 运行时的上下文
相反,请在开发者邮件列表或 IRC 频道联系我们,我们很乐意提供帮助!:)
一些第一个贡献的想法是
- 文档 - 这将使您了解 pypy 的架构
- 测试失败 - 在 nightly builds 中找到一个失败的测试,并修复它
- 缺少的语言特性 - 这些列在我们 issue tracker 中
源代码控制¶
PyPy 的主要 git 仓库托管在这里:https://github.com/pypy,而旧的仓库托管在这里:https://foss.heptapod.net/pypy.
Pypy 的旧仓库托管在 Heptapod 上。Heptapod 是 GitLab 社区版的友好分支,支持 Mercurial。 https://foss.heptapod.net 是一个面向自由和开源软件的公共实例(更多信息 here)。
感谢 Octobus 和 Clever Cloud 提供这项服务!
克隆¶
- 使用命令
git clone https://github.com/pypy/pypy.git
将 PyPy 仓库克隆到您的本地机器。这需要一两分钟,但只需要做一次。另请参见 https://pypy.pythonlang.cn/download.html#building-from-source 。 - 现在您拥有了 PyPy 仓库的完整副本。
编辑¶
- 编辑内容。使用
git diff
查看您所做的更改。使用git add
使 git 了解您添加的新文件,例如新的测试文件。使用git status
查看是否有此类文件。编写并运行测试!(请参阅本页的其余部分。) - 使用
git commit
定期提交。一行提交消息就可以了。我们喜欢大量的提交;只要您取得了一些进展,就进行一次提交,即使它只是一些尚未通过的新测试,或者修复了一些东西,即使并非所有测试都通过。一步一步地,您正在构建更改的历史记录,这是版本控制系统的意义所在。(有一些命令,例如git log
,您应该稍后阅读,以了解如何浏览此历史记录。) - 提交将保留在您的机器上,直到您执行
git push
将它们“推送到”您的分支。命令git push
和git pull
在提交之间进行复制,目标是所有相关的仓库最终都拥有完全相同的提交集。 - 您应该经常推送;没有真正的理由不这样做。请记住,即使它们被推送,在上面的设置中,提交只存在于您命名的分支中。是的,它们是公开可见的,但不要担心有人在 PyPy 的众多分支中走来走去,说“哈哈,看看那个人的糟糕编码风格”。试着进入这样的心态,您的工作不是秘密,这样很好。我们可能不会按原样接受它,而是要求您改进一些东西,但除非您不编写测试,否则我们不会评判您。
架构¶
PyPy 有层级结构,就像食人魔或洋葱一样。这些层级帮助我们将各个部分分开,以便独立进行工作,并使复杂性易于管理。这同样是如此复杂项目的一项基本要求。例如,为 JIT 编写新的优化通常**不会**涉及到 Python 解释器本身,或者 JIT 汇编器后端或垃圾收集器。相反,它需要在 rpython/jit/metainterp/optimizeopt/test/test_*
中编写小的测试,并修复那里的文件。之后,你只需编译 PyPy,一切应该都能正常工作。
进一步阅读:架构
从哪里开始?¶
PyPy 由相对独立的部分组成。你应该从最吸引你的部分开始(所有路径相对于 PyPy 顶层目录)。你可以查看我们的 目录参考,或者从以下几个点开始
- pypy/interpreter 包含字节码解释器:字节码分发器在 pypy/interpreter/pyopcode.py 中,帧和代码对象在 pypy/interpreter/eval.py 和 pypy/interpreter/pyframe.py 中,函数对象和参数传递在 pypy/interpreter/function.py 和 pypy/interpreter/argument.py 中,对象空间接口定义在 pypy/interpreter/baseobjspace.py 中,模块在 pypy/interpreter/module.py 和 pypy/interpreter/mixedmodule.py 中。支持字节码解释器的核心类型定义在 pypy/interpreter/typedef.py 中。
- pypy/interpreter/pyparser 包含一个递归下降解析器,以及允许它解析各种 Python 版本语法的语法文件。一旦语法被处理,解析器就可以通过上述机制被翻译成高效的代码。
- pypy/interpreter/astcompiler 包含编译器。它包含来自 CPython 的编译器包的修改版本,该版本修复了一些错误,并且可以被翻译。
- pypy/objspace/std 包含 标准对象空间。主文件是 pypy/objspace/std/objspace.py。对于每种类型,文件
xxxobject.py
包含类型为xxx
的对象的实现,作为第一个近似值。(某些类型有多种实现。)
构建¶
为了构建 PyPy,我们建议首先安装一个预构建的 PyPy(参见 下载和安装 PyPy)。可以使用 CPython 构建 PyPy,但运行时间会长得多——根据你的架构,运行时间会是两到三倍。
进一步阅读:构建
测试¶
测试驱动开发¶
相反,我们实践了很多测试驱动开发。这部分是因为编译器对质量要求很高,部分是因为没有其他方法可以解决如此复杂的项目,这会让你保持理智。可能有些人足够聪明,不需要它,我们不是其中之一。你可以考虑熟悉一下 pytest,因为这是我们用于测试的工具。我们在树的顶部发布了我们自己的调整版本的 pytest,所以 python -m pytest
会选择我们的版本,这意味着我们的测试需要使用该版本的 pytest 运行。
我们还在 extra_tests
目录中进行翻译后测试,这些测试在虚拟环境中从一个单独的目录运行,因此它们使用更新版本的 pytest。尽可能地,这些测试应该与 CPython 一起通过。
运行 PyPy 的单元测试¶
PyPy 开发一直以来都是彻底的测试驱动。测试有两种模式:那些在翻译之前在 RPython 之上运行的(未翻译测试)和那些在翻译后的 pypy
之上运行的(应用程序测试)。由于 RPython 是 Python2 的一种方言,未翻译的测试使用 python2 主机运行。
PyPy 源代码树附带一个内联版本的 py.test
,你可以通过键入以下命令来调用它
python2 pytest.py -h
你需要 构建时依赖项 才能成功运行测试,因为其中许多测试会编译 PyPy 的一小部分,然后在那个最小解释器中运行测试。 cpyext 测试还需要 pycparser,许多测试使用 hypothesis 构建案例。
现在开始运行一些测试。PyPy 有许多不同的测试目录,你可以使用 shell 自动补全来指向目录或文件
python2 pytest.py pypy/interpreter/test/test_pyframe.py
# or for running tests of a whole subdirectory
python2 pytest.py pypy/interpreter/
注意,不要尝试通过指向根目录甚至顶层子目录 pypy
来运行“所有”pypy 测试。这需要几个小时,并且会消耗大量的 RAM,不建议这样做。
要运行 CPython 回归测试,你应该从一个翻译后的 PyPy 开始,并像使用 CPython 一样运行测试(见下文)。但是,你也可以尝试在翻译之前运行测试,但要注意,这是通过一个在所有情况下都无法正常工作的黑客完成的,而且通常非常慢:py.test lib-python/2.7/test/test_datetime.py
。通常,一个更好的方法是提取一个最多几行的最小失败测试,并将其放入我们自己的测试之一中,位于 pypy/*/test/
。
应用程序级测试¶
虽然通常调用 python2 pytest.py 在运行在 CPython 之上的未翻译的 PyPy 上运行应用程序级测试,但我们有一个测试扩展来直接在主机 python 上运行测试。这对于 cpyext 等模块来说非常方便,可以比较和对比 CPython 和 PyPy 之间的测试结果。
应用程序级测试(文件名以 apptest_
而不是 test_
开头的测试)在将 -D 或 –direct-apptest 传递给 pytest 时直接在主机解释器上运行
pypy3 -m pytest -D pypy/interpreter/test/apptest_pyframe.py
混合级别测试(通常以 test_
开头的测试)可以通过使用 -A 或 –runappdirect 选项调用 pytest
python2 pytest.py -A pypy/module/cpyext/test
其中 python2 可以是 python2 或 pypy2。在 py3 分支上,收集阶段必须使用 python2 运行,因此未翻译的测试将使用
python2 pytest.py -A pypy/module/cpyext/test --python=path/to/pypy3
翻译后测试¶
如果您运行翻译,您最终将在运行翻译的目录中得到一个名为 pypy-c
(或 Python3 分支的 pypy3-c
)的二进制文件。
要从标准 CPython 回归测试套件运行测试,请使用常规的 Python 方式,即(使用确切的二进制文件名称)
./pypy3-c -m test.test_datetime
# or
./pypy3-c lib-python/3/test/test_audit.py
Buildbot¶
PyPy 在 https://buildbot.pypy.org 上运行一个基于 buildbot 的 CI 系统。这是由 https://foss.heptapod.net/pypy/buildbot 上的代码驱动的。x86_64、i686 和 aarch64 上的 Linux 运行器使用 Docker 容器,它管理依赖项。有关更多信息,请参阅 Dockerfile。Windows 运行器使用 externals 存储库的 win64_14x
分支中的依赖项。macOS 运行器(x86_64、arm64)使用 M1 机器上的 venv。
工具和实用程序¶
如果您对 PyPy Python 解释器的内部工作原理感兴趣,未翻译的 Python 解释器中有一些功能可以让您内省其内部结构。
解释器级控制台¶
要开始使用 PyPy 解释 Python,请安装 distutils 支持的 C 编译器,并使用 Python 2.7 或更高版本运行 PyPy
cd pypy
python bin/pyinteractive.py
几秒钟后(请记住:这是在 CPython 之上运行的),您应该位于 PyPy 提示符处,它与 Python 提示符相同,但多了一个“>”。
如果您在控制台上按 <Ctrl-C>,您将进入解释器级控制台,这是一个普通的 CPython 控制台。然后,您可以使用前缀 w_
访问 PyPy 的内部对象(例如 对象空间)以及您在 PyPy 提示符处创建的任何变量。
>>>> a = 123
>>>> <Ctrl-C>
*** Entering interpreter-level console ***
>>> w_a
W_IntObject(123)
该机制在两个方向上都有效。如果您在解释器级别使用 w_
前缀定义一个变量,您将在应用程序级别看到它。
>>> w_l = space.newlist([space.wrap(1), space.wrap("abc")])
>>> <Ctrl-D>
*** Leaving interpreter-level console ***
KeyboardInterrupt
>>>> l
[1, 'abc']
请注意,解释器级控制台的提示符只是“>>>”,因为它在 CPython 级别运行。如果您想返回 PyPy,请按 <Ctrl-D>(在 Linux 下)或 <Ctrl-Z>、<Enter>(在 Windows 下)。
还要注意,并非所有模块在此模式下默认可用(例如:_continuation
由 greenlet
需要),您可能需要使用其中一个 --withmod-...
命令行选项。
您可能对阅读有关 解释器级和应用程序级 之间区别的更多信息感兴趣。
pyinteractive.py 选项¶
要列出 PyPy 解释器命令行选项,请键入
cd pypy
python bin/pyinteractive.py --help
pyinteractive.py 支持 CPython 支持的大多数选项(除了大量可用于自定义 pyinteractive.py 的选项)。作为从命令行使用 PyPy 的示例,您可以键入
python pyinteractive.py --withmod-time -c "from test import pystone; pystone.main(10)"
或者,与常规 Python 一样,您只需在命令行上提供脚本名称即可
python pyinteractive.py --withmod-time ../../lib-python/2.7/test/pystone.py 10
选项 --withmod-xxx
启用内置模块 xxx
。默认情况下,几乎没有一个模块是启用的,因为初始化它们需要时间。如果您无论如何都想要启用所有内置模块,您可以使用 --allworkingmodules
。
有关所有命令行选项的功能的详细信息,请参阅我们的 配置部分。
跟踪字节码和对对象的运算¶
您可以使用简单的跟踪模式来监控字节码的解释。要启用它,请在交互式 PyPy 控制台中设置 __pytrace__ = 1
>>>> __pytrace__ = 1
Tracing enabled
>>>> x = 5
<module>: LOAD_CONST 0 (5)
<module>: STORE_NAME 0 (x)
<module>: LOAD_CONST 1 (None)
<module>: RETURN_VALUE 0
>>>> x
<module>: LOAD_NAME 0 (x)
<module>: PRINT_EXPR 0
5
<module>: LOAD_CONST 0 (None)
<module>: RETURN_VALUE 0
>>>>
演示¶
该 example-interpreter 存储库包含使用 RPython 翻译工具链编写的示例解释器。
graphviz & pygame 用于查看流程图(强烈推荐)¶
如果您想查看生成的流程图,则需要 graphviz 和 pygame。
graphviz: https://www.graphviz.org/Download.php