- Published on
Python如何给二进制模块生成stubs
- Authors
- Name
- ttyS3
English title: Python How to Generate Stubs for Binary Module
stubs是什么
stubs主要是给IDE自动提示和语法检测使用的,比如JetBrains PyCharm.
stubs有个规范的名称,叫PEP 561 Typing Stubs
https://www.python.org/dev/peps/pep-0561/#type-checker-module-resolution-order
- Stub packages - these packages SHOULD supersede any installed inline package. They can be found at
foopkg-stubs
for packagefoopkg
.
所以 gi
包(PyGTK包的顶层目录名是gi
)的 stubs包是 gi-stubs
.
但是使用 JetBrains generator3
生成的包,并不是一个真正的stub文件(.pyi
后缀的才是stub文件), 它生成的所有的代码都是.py
后缀的。但是,PyCharm 能正确地将它当做stubs识别。
我们使用gi-stubs
,而不是直接使用gi
, 一是防止user级别的包屏蔽了系统的 gi (位于/usr/lib64/python3.8/site-packages/gi/
), 而这个包实际是个stubs,不能真正工作, 这样就会导致正常的代码无法运行,因此顶层目录名一定是gi-stubs
而不是gi
. 二是,根据Stub packages的规范( foopkg-stubs
for package foopkg
)
PyGObject-stubs 的不足
最近尝试用py3写一点nautilus scripts, 因此肯定是要用到 PyGTK模块。但是这个模块是GTK lib的一个python绑定,因此实际的代码并不在python这边。 这给IDE的语法检测和自动提示都带来了麻烦。PyCharm 完全不知道from gi.repository import Gtk
导入的Gtk
模块是个什么东西。但是它很智能,会机智地提示你缺少相应的stubs:
Type hints are not installed They could make code insight better. Install 'PyGObject-stubs==0.0.2'
尝试执行一下pip install --user PyGObject-stubs
安装好这个stubs, 发现 PyCharm 已经可以识别Gtk
模块了。
然后这个stubs, 如果你去看它的源码,就会发现,它能做的仅仅是导入一些基本的类型定义,没有任何参数提示。
这其实没有太多作用,它能做的仅仅是能不让编辑器误认为你有语法错误。
尝试自已动手生成
如果不想自己手动生成的,可以直接使用老灯生成好的 https://github.com/ttys3/pygobject-stubs
使用方法:
git clone https://github.com/ttys3/pygobject-stubs.git /tmp/ && mv /tmp/pygobject-stubs/gi-stubs ~/.local/lib/python3.8/site-packages/
这里的
python3.8
根据自己的实际情况修改
Google了一下,好像早期的PyCharm有Generate Stubs for Binary Module
这种自动生成stubs的功能,不知道为什么现在没有了。
然后在PyGObject-stubs
的github repo发现一个有用的issue( https://github.com/pygobject/pygobject-stubs/issues/5 , 里面提到了使用intellij-community
的python/helpers/generator3.py
脚本自动生成stubs.
不过这个issue是2018年的,有点老了。新版的JetBrains IDE已经将这个generator3.py
脚本改写成了一个generator3
模块了。
另外老灯发现,JetBrains PyCharm
也自带了这个generator3
模块。
所以,将前面发现的那个issue里的脚本稍做修改,就能适用于最新的IDE和库:
mkdir /tmp/out
for typelib in Atk GLib GModule GObject Gdk GdkPixbuf Gio Gtk Pango; do
PYTHONPATH=$HOME/Apps/pycharm-community/plugins/python-ce/helpers python3 -m generator3 -d /tmp/out -p gi.repository.$typelib $(python3 -c "import gi; gi.require_versions({'Atk': '1.0', 'GModule': '2.0', 'Gdk': '3.0', 'GdkPixbuf': '2.0', 'Gtk': '3.0', 'Pango': '1.0'}); from gi.repository import $typelib; print($typelib.__path__[-1])")
done
# 让stubs能为PyCharm所用:
mv /tmp/out/gi ~/.local/lib/python3.8/site-packages/gi-stubs
# 或者你可以将 gi-stubs 目录放到任意地址,但是要记得添加到你的项目
# 具体可以参考 https://www.jetbrains.com/help/pycharm/stubs.html#reuse-stubs
# 注意是添加父目录,比如你将 gi-stubs 放到了 `~/repo/python/stubs/gi-stubs`, 那么
# 在PyCharm里添加的时候是添加`~/repo/python/stubs`这个目录
$HOME/Apps/pycharm-community
是本机 PyCharm 安装路径, 请修改为你自己的。
生成命令解释
我们从上面的for
里单独拿一条命令出来,以给 Gtk
模块生成的为例。
python3 -c "import gi; gi.require_versions({'Gtk': '3.0'}); from gi.repository import Gtk; print(Gtk.__path__[-1])"
用于找到本机Gtk模块的typelib
文件路径:
/usr/lib64/girepository-1.0/Gtk-3.0.typelib
因此我们的生成命令其实是像这样:
PYTHONPATH=$HOME/Apps/pycharm-community/plugins/python-ce/helpers python3 -m generator3 -d /tmp/out -p gi.repository.Gtk /usr/lib64/girepository-1.0/Gtk-3.0.typelib
这个命令会使用 JetBrains generator3
通过解析/usr/lib64/girepository-1.0/Gtk-3.0.typelib
这个 G-IR 格式的二进制数据库文件,生成gi.repository.Gtk
模块的stubs.
使用效果
自动生成的有些地方会有问题(但是基本上不影响代码自动完成),比如Gdk.SELECTION_CLIPBOARD
没有成功解析出它的值(给弄成了None
)
SELECTION_CLIPBOARD = None # (!) real value is 'Gdk.Atom.intern("CLIPBOARD", False)'
总体来说效果还是比PyGObject-stubs
好太多: