HPY现在已经过了一段时间了。初步讨论在2019年期间开始,在好的旧时代,我们仍然可以去会议和有现实生活会议。从那时起,从实际代码的角度来看,HPE展开了很多,但我们有点太静音了。沟通我们在外部世界和广阔的社区所做的事情。希望,现在这篇博客在线我们将在定期传达HPY状态时进行更好的ejob,因此请务必订阅RSS源。
HPY为在C中扩展Python提供了一个新的API。换句话说,您使用#include< hpy.h>而不是#include< python.h>
官方Python / C API对CPython的当前实施方式:它公开了许多人的细节,这使得它很难:
试验CPython本身内的新事物:例如使用GC而不是Refcounting,或删除GIL。
要正确检查像Refcount Handling这样的内容:外部API会混合使用应隐藏的实现细节。
多年来,明显显然,以有效的方式模仿Python / C API是具有挑战性的,如果不是不可能的话。 HPY的主要目标是公开一个C API,它以有效的方式在许多非常多样化的Pythonimplation上以有效的方式实现。
可以在所有支持的Python实现和版本上编译单个二进制文件,该二进制文件在所有支持的Python实现和版本上运行
它提供了改进的调试体验:IN"调试模式&#34 ;,HPE主动检查许多常见错误,例如参考泄漏和无效的对象在删除后的使用情况。可以转动"调试模式和#34;在启动时,不需要重新编译Python或扩展本身
#include< python.h>静态pyobject *添加(pyobject * self,pyobject * args){long a,b; if(!pyarg_parsetuple(args," ll"& a,& b))返回null;返回Pylong_Fromlong(A + B);静态pymethoddef hellomethods [] = {{"添加" ,(pycfunction)添加,meth_varargs,"添加两个整数" },{null,null,0,null}}; static struct pymoduledef moduledef = {pymoduledef_head_init," hello_old" ,"你好,使用旧的python / c api" ,-1,hellomethods,}; pymodinit_func pyinit_hello_old(void){return pymodule_create(& moduledef); }
#include< hpy.h> hpydef_meth(添加,"添加",add_impl,hpyfunc_varargs,。doc ="添加两个整数");静态HPY Add_impl(HPyContext CTX,HPY SELF,HPY * ARGS,HPY_SSIZE_T NARGS){LONG A,B; if(!hpyarg_parse(ctx,null,args,nargs," ll"& a,& b))返回hpy_null;返回氟氯龙_FROMLONG(CTX,A + B); }
前蟒蛇*现在是HPY,我们调用"把手"句柄类似于pyobject *,但完全不透明:有关更多信息,请参阅官方文档。
还有一个附加参数hpycontext ctx。旧API的问题之一是通常它隐含地依赖于存在每线程或每个子际状态的存在。 HPyContext使此状态显式。这使得整个API更加规则并且可以开发新的有趣功能,例如通用ABI和调试模式。
HPY介绍了HPYDEF的概念。 HPyDef_Meth是一种宏,它生成HPyDef静态常量命名添加的定义,它表示由C函数add_impl实现的python方法的定义。在这个具体的例子中,HPYDEF_METH包含或多或少地包含与旧Pymethoddef相同的信息,但HPYDEF更普遍。例如,当定义自定义类型时,您可以使用像HpyDef_slot,hpydef_getset等的内容使用。
请注意,我们不再需要演员(Pycfunction)。 hpydef_meth的最大优势之一是因为它' s宏,它可以自动为add_impl生成前向声明,并具有正确的签名。这意味着如果使用错误的数字和/或参数类型,则会获得一个很好的编译时间错误,而不是在运行时崩溃。
与HpyFunc_Varargs对应的签名与旧的meth_varargs略有不同:我们将位置参数传递为C阵列而不是Python元组。这意味着可以呼叫该功能而无需分配Python元组,例如HPy的Pypy实现利用了。这与CPython' s vectorcall协议非常相似。
在这篇文章中,我们正在使用稍旧的HPY版本。如果你尝试使用hpycontext * ctx而不是hpycontext ctx.see也会发出#150和Pr#182。
static hpydef * hello_defines [] = {&添加,null};静态HPYModuledef Moduledef = {hpymoduledef_head_init,。 m_name =" hello_old" 。 m_doc ="你好,使用新的hpe api" 。 m_size = -1,。定义= hello_defines,}; hpy_modinit(hello_new)静态hpe init_hello_new_impl(hpycontext ctx){return hpymodule_create(ctx,& moduledef); }
这与旧代码相似。最大的变化是,而不是指定一系列Pymethoddef,我们创建了上面的HPyDef asdiscussed数组。
最后,我们需要修改setup.py。编译HPY扩展名为Easyas添加setup_requires = [' hpy.devel']并使用hpy_ext_modules:
来自Setuptools Import Setup,Extension Setup(Name =" Hello",ext_modules = [扩展名(' hello_old',[' hello_old.c'],],],],hpy_ext_modules = [扩展(' hello_new',[' hello_new.c'],setup_requires = [' hpy.devel'],)
在此演示中,我们将展示如何设置一个环境,以在CPython和Pypy上尝试HPY和CompileExtensions。
目前,HPY仍处于早期阶段,API仍然需要进行差异,所以我们还没有完成任何官方发布。出于同样的原因,如果您希望在PYPY上使用HPY,则需要手动确保安装支持版本。这只是暂时的,这一旦开始推出官方版本,这种事情会自动讨厌。
因此,我们需要从GitHub repo安装HPY。此外,罗比亚内的HPYIMPLENTIONDATION在一点点后滞后,所以我们将安装浅旧版本:
要安装夜间的小型,它足以解开tarball并运行-M的保证。我们可以查看Pypy bycalling hpy.universal.get_version()支持的HPY版本是什么:
$ curl -o http://buildbot.pypy.org/teartly/hpy/pypy-c-jit-101860-a2f7c80062e8-linux64.tar.bz2 / tar xf pypy-c-jit-101860-a2f7c80062e8-linux64.tar。 bz2 $ ./pypy-c-jit-101860-a2f7c80062e8-linux64/bin/pypy -m searnpip $ ./pypy-c-jit-101860-a2f7c80062e8-linux64/bin/pypy&g ;> ;> ;>进口hpy.universal>>>>> hpy.universal.get_version()(' 0.1.dev959 + geb07982'' eb07982')
$ cd / path / to / miffer / $。 / path / to / to-to / toyhpy / bin / sectivate#激活venv $ python setup.py build_ext --inplace [...] $ ls -1 * .sohello_new.cpython-38-x86_64-linux-gnu.sohello_old.cpython- 38-x86_64-linux-gnu.so $ python>>>进口hello_old,hello_new>>> hello_old.add(10,20)30>>>> hello_new.add(30,40)70>>
有效!要注意的一个重要事项是hello_new的文件名:.cpython-38-x86_64-linux-gnu.so是cpython 3.8extension模块的标准文件名。这发生了,因为默认情况下,hpy_ext_modules toundsthe cpython abi。 assuch,从CPython Hello_New的角度来看,与Hello_old无法区分。这也意味着HPY是只需要编译它而是稍后才能加入它。最后,我们希望性能与使用旧API的extensions相同。
但是,我们也可以明确要求HPY生产AN"普遍二进制",它针对HPY Universal ABI:顾名思义,可以通过CPython进口通用二进制文件,但诸如PYPY等Alsoby替代实现。我们可以构建通用的binariesby try - hpy-abi = posencal to setup.py:
$#clean the以前的build $ rm -rf build / * .so $ python setup.py - hpy-abi = henseral build_ext --inplace $ ls -1 * .sohello_new.hpy.sohello_old.cpython-38-x86_64-linux -gnu.so.
注意文件名:hello_old仍然是一个特定于cpython的扩展,但hello_new.hpy.so是一个通用二进制。编译后,您可以导入热点:
在写作的那一刻,因为问题#191如果你尝试翻页hello_new的'pyrint,你会看到这样的东西(注意.pyextension):
请注意,在其自己的情况下,CPython不知道如何导入.hpy.sofiles。魔术由hello_new.py完成,由setup.py自动启动:
$ cat hello_new。 py [...] def __bootstrap__():从hpy.universal import load_from_spec ext_filepath = pkg_resources。 resource_filename(__name__,' hello_new.hpy.so')m = load_from_spec(spec(' hello_new',ext_filepath)[...] sys。模块[__name__] = m __bootstrap__()
所有你需要从HPY开始的所有&#39。我们今天呈现的是基础知识,当然:在下一篇文章中,我们将更多地挖掘技术特征,并显示出比Hello World的更有趣的功能。