python打包 缺少module_在Windows上编译和调试Python

大多数人用的Python应该是从http://python.org上下载的C语言版本,就是CPython。在Windows上是用微软的Visual Studio,或者说Visual C++编译的。纯C,所以上过大一的C语言,原则上就可以翻翻看了。这里看下怎么从CPython的源代码开始,编译并且调试Python。

下载源码

在http://python.org的Downloads-All releases页面下,有个“Looking for a specific release?”,在这里点选最新的版本,例如当前是3.8.3,就会进入这个页面

https://www.python.org/downloads/release/python-383/​www.python.org

滚轮滚到最下面,有个Files,是官方发布的编译好的各个操作系统的可执行文件,和C源代码

2ba608815b465f100c457951d7da0d77.png

前两个 xx source tarball随便下载一个就行,解压后内容是一样的,这里只是打包方式不一样。

然后需要安装Visual Studio,讲道理3.8应该对应了VS2017,不过这里发现用VS2019基本也可以。

初步准备

在编译之前,需要下载一些依赖。得益于Python的import的实现,大部分module就像插件一样,如果不import,即使不编译也没关系。这样,编译时跳过一些模块也没关系。

双击执行PCbuild/get_externals.bat,会自动下载一些依赖放在externals文件夹,可以看到里面有一堆外部库,有的是源码,有的是二进制。externals/zips文件夹下面是下载下来的原始zip包们,解压后放在了externals下面。

0ab3a662fdb0f858cacf23cb47cb455f.png

其实还应该有个openssl,有点大,本人下了几次下不下来,就放弃了。不影响基本的编译。实际上只需要libffi就可以编译python.exe了。

编译

双击PCbuild/pcbuild.sln,就会打开Visual Studio了。找到解决方案下的python项目,右击,Build。

0be1d55fa15d0e1eb5936a780ed76aa0.png

稍等上几分钟就编译好了,实际上只编译了仅仅三个项目,

  • pythoncore Python核心,包括解释器和核心对象等
  • python 一个简单的main入口
  • _ctypes 基于libffi服务于ctypes模块

现在,已经有了一个可以运行的python解释器了,这里叫python_d.exe。

可以直接Ctrl+F5,Start Without Debugging,python就开始运行了。

de5f211c36759191eabdb72aa582176f.png

CPython源码基本结构

有几个关键的源码文件夹,下面是个人理解:

  • Python/ 解释器本身,例如编译字节码的compile.c,和解释器循环ceval.c
  • Objects/ 核心对象,例如无处不在的list的listobject.c,dict的dictobject.c,还有各种数值类型,还有非常关键的类型对象typeobject.c。
  • Modules/ 一些module的纯C实现,或为了辅助Lib下一些module的C实现,例如mathmodule.c直接实现了math模块,例如_csv.c加速csv的解析,例如_ssl.c可能是openssl的封装。

调试

正常F5即可开始调试,然后就是正常流程了,打断点,单步,查看变量等等。

可以先看看自己好奇的东西,例如从dis.dis + ceval.c看起,里面有个巨大的switch,每个字节码对应一个case。举个例子吧,执行一个 a = 1,发生了什么。

先按 Ctrl + , (英文逗号),然后输入 “f ceval”打开ceval.c文件。

跑一下dis.dis('a = 1'),可以看到有个STORE_NAME,看上去就像赋值

881e9846daa3c6ba695cc298260f6cb4.png

在这里打个断点试试:

d9ac2a0a62d804c90fe2dd8dcfb6296a.png

然后执行 a = 1,果然跑到了这里来,(可惜不知道为啥不能直接鼠标指上去就显示值)。可以看到,如果ns(namespace)就是一个dict类型的话,会执行PyDict_SetItem,否则就执行更通用但是可能更慢的PyObject_SetItem。有兴趣的不妨给exec传个dict的子类,看会不会走下面那个分支。(我试了,搞个class MyDict(dict): pass确实会走下面的那个分支)

又如,报了个错,想看看是哪里,直接在源码里Ctrl + Shift + F全局搜报错的字符串(或其中一部分)。例如这样:int.a = 10,会报

9e3bcfd94e5c0dd454cb15e2dfed32cb.png

然后全局搜一下“can't set attributes of built-in/extension type”,只有一处,那就是这里了:

64d5f9979fa2494f058e9e1c0c06f175.png

看来,至少需要有个Py_TPFLAGS_HEAPTYPE才可以修改或新增属性。

总之遇到奇怪的表现,想弄清楚底层的话,应该还有其他的路子来找到源码的位置。或者,先读读官方文档的Extending and Embedding the Python Interpreter,顺便翻一下Python/C API Reference Manual,这样应该会更游刃有余了。如果想先看代码的话,翻一下Include/object.h,Objects/typeobject.c和Python/ceval.c应该是不错的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值