go get 指定版本_[译]Python和Go:第三部分打包Python代码

此系列文章:

Python和Go:第一部分-grpc

Python和Go:第二部分-使用Go扩展python

介绍

在上一篇文章中我们将Go代码编译到一个共享库中,并从Python交互式shell中使用了它。在本文中,我们将通过编写一个Python模块来完成开发过程,该模块将隐藏使用共享库的底层细节,然后将此代码打包为Python包。

回顾-体系结构和工作流程概述

图167bfbd15680d96803c1c407a2688b2be.png

图1显示了从Python到Go再返回的数据流。

我们遵循的工作流程是:

  • 编写Go代码(CheckSignature),
  • 导出到共享库(verify)
  • 在Python交互式提示中使用ctypes调用Go代码
  • 编写并打包Python代码(check_signatures)

在上一篇博客文章中,我们已经完成了前三部分,在这一部分中,我们将实现Python模块并打包。我们将执行以下步骤:

  • 编写Python模块(checksig.py)
  • 写入项目定义文件(setup.py)
  • 构建扩展

Python模块

让我们从编写Python模块开始。该模块将具有Pythonic API,并将隐藏使用Go代码构建的共享库的底层细节。

checksig.py

01 """Parallel check of files digital signature"""
02 
03 import ctypes
04 from distutils.sysconfig import get_config_var
05 from pathlib import Path
06 
07 # Location of shared library
08 here = Path(__file__).absolute().parent
09 ext_suffix = get_config_var('EXT_SUFFIX')
10 so_file = here / ('_checksig' + ext_suffix)
11 
12 # Load functions from shared library set their signatures
13 so = ctypes.cdll.LoadLibrary(so_file)
14 verify = so.verify
15 verify.argtypes = [ctypes.c_char_p]
16 verify.restype = ctypes.c_void_p
17 free = so.free
18 free.argtypes = [ctypes.c_void_p]
19 
20 
21 def check_signatures(root_dir):
22     """Check (in parallel) digital signature of all files in root_dir.
23     We assume there's a sha1sum.txt file under root_dir
24     """
25     res = verify(root_dir.encode('utf-8'))
26     if res is not None:
27         msg = ctypes.string_at(res).decode('utf-8')
28         free(res)
29         raise ValueError(msg)

第12-18行的代码与我们在交互式提示中所做的非常相似。

第7-10行处理共享库文件名-当我们在下面讨论打包时,我们将了解为什么需要它。在第21-29行,我们定义了模块的API-一个称为check_signatures的函数。ctypes会将C的NULL转换为Python的 None,因此将在第26行if执行该语句。在第29行,我们通过抛出ValueError异常来提示错误。

注意:Python的命名约定与Go不同。大多数Python代码都遵循PEP-8[1]中定义的标准。

安装和打包

在继续进行构建Python模块的最后一部分之前,我们需要先了解一下安装Python软件包的工作方式。你可以跳过此部分,直接转到下面的代码,但是我相信本节将帮助你理解我们为什么会在下面编写这些代码。

这是安装包时Python的pip[2]的简化工作流程:

图23fe0c90c3548e456fb98894fa74d630d.png

图2显示了安装Python软件包的简化流程图。如果有一个与当前OS /体系结构匹配的预构建二进制包(wheel),它将使用它。否则,它将下载源代码并构建。

我们将Python软件包大致分为“纯”和“非纯”两种。纯软件包仅用Python编写,而非纯软件包则用其他语言编写并编译到共享库中。由于非纯软件包包含二进制代码,因此需要专门针对OS /体系结构组合(例如Linux / amd64)构建它们。我们的软件包被认为是“非纯”的,因为它包含用Go编写的代码。

我们首先编写一个名为setup.py的文件,该文件定义了项目并包含有关如何构建它的说明。

setup.py

01 """Setup for checksig package"""
02 from distutils.errors import CompileError
03 from subprocess import call
04 
05 from setuptools import Extension, setup
06 from setuptools.command.build_ext import build_ext
07 
08 
09 class build_go_ext(build_ext):
10     """Custom command to build extension from Go source files"""
11     def build_extension(self, ext):
12         ext_path = self.get_ext_fullpath(ext.name)
13         cmd = ['go', 'build', '-buildmode=c-shared', '-o', ext_path]
14         cmd += ext.sources
15         out = call(cmd)
16         if out != 0:
17             raise CompileError('Go build failed')
18 
19 
20 setup(
21     name='checksig',
22     version='0.1.0',
23     py_modules=['checksig'],
24     ext_modules=[
25         Extension('_checksig', ['checksig.go', 'export.go'])
26     ],
27     cmdclass={'build_ext': build_go_ext},
28     zip_safe=False,
29 )

在第09行,我们定义了一个使用Go编译器构建扩展的命令。Python内置支持以C,C ++和SWIG[3]编写的扩展,但不支持Go。

第12-14行定义要运行的命令,第15行将此命令作为外部命令运行(Python的 subprocess类似于Go os/exec)。

在第20行,我们调用setup命令,在第21行中指定包名称,在第22行中指定版本。在第23行,我们定义Python模块名称,在第24-26行中,定义扩展模块(Go代码) 。在第27行,我们用构建Go代码的build_ext命令覆盖了内置build_ext命令。在第28行,我们指定该软件包不是zip安全的,因为它包含共享库。

我们需要创建的另一个文件是MANIFEST.in。该文件定义了所有需要打包在源代码分发中的额外文件。

MANIFEST.in

01 include README.md
02 include *.go go.mod go.sum

构建Python包

$ python setup.py bdist_wheel
$ python setup.py sdist

软件包建立在名为dist的子目录中。

dist目录内容

$ ls dist
checksig-0.1.0-cp38-cp38-linux_x86_64.whl
checksig-0.1.0.tar.gz

我们使用ls命令显示dist目录的内容。

wheel二进制软件包(带有.whl扩展名)的名称中包含平台信息:cp38表示CPython版本3.8,linux_x86_64是操作系统和体系结构-与Go的 GOOSGOARCH相同。由于wheel文件的名称根据其所基于的体系结构而变化,因此我们不得不在checksig.py的08-10行中编写一些逻辑。

现在,你可以使用Python的软件包管理器pip安装这些软件包。如果你要发布出去,则可以使用诸如twine[4]之类的工具将其上传到PythonPyPI[5]

有关完整的构建,安装和使用流程,请参见源代码存储库[6]中的example.py 和 Dockerfile.test-b

结论

你可以毫不费力地使用Go扩展Python,并发布具有Pythonic API的Python模块。打包是使你的代码可部署且可用的必要条件,请不要跳过此步骤。

如果你想从Go返回Python类型(例如list或 dict),则可以将Python的扩展C API[7]与cgo一起使用。你也可以看看go-python[8],它可以减少在Go中编写Python扩展的困难。

在下一部分中,我们将再次转换角色,我们将从Go调用Python-在相同的内存空间中几乎零序列化。

参考资料

[1]

PEP-8: https://www.python.org/dev/peps/pep-0008/

[2]

pip: https://pip.pypa.io/en/stable/

[3]

SWIG: http://www.swig.org/

[4]

twine: https://github.com/pypa/twine

[5]

PyPI: https://pypi.org/

[6]

源代码存储库: https://github.com/ardanlabs/python-go/tree/master/pyext

[7]

扩展C API: https://docs.python.org/3/extending/index.html

[8]

go-python: https://github.com/sbinet/go-python

f00eb55079b218f32a741a36bf0e1221.png你点的每个在看,都是对我最大的支持
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值