当前位置: 首页 新闻详细

在python中0oa1是合法的八进制数字表示形式。Python二进制接口(ABI)的兼容性有多难?

一、在python中0oa1是合法的八进制数字表示形式。

在python中0oa1是合法的八进制数字表示形式。(错误)

在python中不同进制的数据表示方式:

二进制:python里的二进制使用的是0b开头,如0b101101101。

八进制:Python里的八进制使用的是0o开头,如0o34。

十六进制:Python里的十六进制使用的是0x开头,0x2a。

十进制:Python里默认的输出数字就是十进制,如a=31;当使用print语句打印一个数字的时候,默认也是使用十进制打印。

Python2和Python3的区别:

八进制数据的表示方式:

八进制数据的表示方式:在Python2里,0x开始的数字是八进制,以0开始的数字也是八进制。例如:032==>八进制。

Python3里,八进制里只能使用0o开头。

python中进制表示形式:

二进制:
bin1=0b101

bin2=0B101

八进制:
oct1=0o26

oct2=0O26

十六进制:
hex1=0xff或
hex2=0Xff

二、pkl是什么文件要如何打开它

**pkl文件是Python中用于存储对象的文件格式,全称为"pickle"**。它是Python标准库中的一个模块,主要用于将Python对象(如列表、字典、类实例等)序列化为二进制数据,以便于存储或在不同Python程序间传输。pkl文件以二进制形式存储数据,直接打开通常会显示一堆序列化的内容,不易于阅读。

要打开pkl文件,通常需要使用Python的pickle模块。具体步骤如下:

1.导入pickle模块:在Python脚本中,首先需要导入pickle模块,这可以通过`importpickle`实现。

2.打开文件:使用Python的内置`open`函数以二进制读取模式('rb')打开pkl文件。例如,`f=open('example.pkl','rb')`。

3.加载数据:通过pickle模块的`load`函数从文件中加载数据。这一步会将二进制数据反序列化为原始的Python对象。例如,`data=pickle.load(f)`。

4.关闭文件:最后,不要忘记使用`f.close()`关闭文件,或者更简洁地,使用`with`语句自动管理文件的打开和关闭。

Python二进制接口(ABI)的兼容性有多难?

2022-12-0818:16·CSDN摘要:知名安全机构TrailofBits近日开发了一种新的Python工具,用于检查Python包是否存在CPython应用程序二进制接口(ABI)违规,名叫abi3audit。abi3audit已经发现了数百个不一致和错误标记的包分发,每一个都是因未检测到ABI违规而导致崩溃和可利用内存损坏的潜在来源。它在开源许可证下公开可用,因此您可以立即使用它!

链接:https://blog.trailofbi
ts.com/2022/11/15/python-wheels-abi-abi3audit/

声明:本文为CSDN翻译,未经授权,禁止转载。


作者|TrailofBits编译|杨紫艳
出品|CSDN(ID:CSDNnews)Python是最受欢迎的编程语言之一,具有相应的大型程序包生态系统:超过600,000名程序员使用PyPI分发超400,000个独特的包,为世界上的许多软件提供动力。Python打包生态系统的时代也使它与众不同:在通用语言中,只有PerlCPAN模块比它早。与打包工具和大部分标准的独立开发相结合,使Python的生态系统成为主要编程语言生态系统中较为复杂的一个。这些复杂性包括:?当前两种主要的打包格式(源分布和轮子),以及少量的特定领域和遗留格式(zipapps,PythonEggs,conda自带格式等。);?一组不同的封装工具和封装规范文件:setuptools、flit、poetry和PDM,以及用于实际安装封装的pip、pipx和Pipenv;?…以及相应的封装和依赖规范文件:pyproject.toml(PEP518-style)、pyproject.toml(Poes-style)、setup.py、setup.cfg、Pipfile、requirements.txt、MANIFEST.in等。本文只介绍Python封装复杂性的一小部分:CPython稳定的ABI。将展示什么是稳定的ABI,为什么存在,如何集成到Python封装中的,以及为使ABI违规更易意外出现,每一个部分是如何严重报错。

CPython稳定的API和ABI

与许多其他参考实现不同,Python的参考实现(CPython)是用C编写的,并提供了两种本机交互机制:?C应用程序编程接口(API),允许C和C++程序员利用CPython的公共标头进行编译,并使用任何公开的功能;?应用程序二进制接口(ABI),允许任何CABI语言支持(如Rust或Golang)链接到CPython运行,并使用相同的内部构件。开发人员可以使用CPythonAPI和ABI来编写CPython扩展。这些扩展与普通Python模块完全相同,但与解释器的实现细节直接交互,而不是Python本身中公开的“高级”对象和APIs。CPython扩展是Python生态系统的基石:它们为Python中的关键性能任务提供了一个“逃生通道”,并支持本地语言(如更广泛的C、C++和Rust打包生态系统)的代码重用。然而,扩展带来了一个问题:CPythonAPIs在不同版本之间发生了变化(随着CPython实现细节的变化),这意味着默认情况下,将CPython扩展加载到不同版本的解释器中是无法预测的。换句话说:用户可能会很幸运,完全没有问题,但是可能会因为缺少函数而崩溃,甚至最糟糕的是,可能由于函数签名和结构布局的变化而导致内存损坏。为了改善这种情况,CPython的开发人员创建了stableAPI和ABI:一组宏、类型、函数和数据对象,它们可保证在小版本之间保持可用和向前兼容。换句话说:为CPython3.7stableAPI构建的CPython扩展也可在CPython3.8和更高版本上正确加载和运行,但不能保证在CPython3.6或更低版本上加载和运行。在ABI级别,这种兼容性被称为“abi3”,并且可以在扩展的文件名mymod.abi3.so中进行标记,例如,指定一个名为mymod的可加载的stableABI兼容CPython扩展模块。重要的是,Python解释器不使用此标记执行任何操作。这是第一次出现这种情,CPython不知道扩展是否与ABI兼容。接下来,将展示这种状况与Python打包状态的组合产生的问题。

CPython扩展和打包


CPython扩展本身只是一个简单的Python模块。为了对其他模块有用,它需要像所有其他模块一样打包和分发。

对于源发行版,打包CPython扩展很简单(对于一些简单的定义):源发行版的构建系统(通常是setup.py)描述了生成本机扩展所需的编译步骤,并且包安装程序会在安装期间运行这些步骤。

例如,以下是如何使用setuptools定义microx的本机扩展(microx_core):

通过源代码分发CPython扩展具有优点(?)和缺点(?):?API和ABI的稳定性没有问题:程序包可以在安装过程中构建,也可以不构建,并且在构建时,它运行的解释器与构建时使用的解释器相同。?源代码构建对用户来说是一种负担:它们需要Python软件的最终用户安装CPython开发标头文件,并维护与扩展目标语言或生态系统相对应的本地工具链。这意味着在每台部署机器上都需要一个C/C++(以及越来越多的Rust)工具链,从而增加了规模和复杂性。?源代码构建从根本上来说是脆弱的:编译器和本机依赖关系不断变化,最终用户(充其量是Python专家,而不是编译语言专家)只能调试编译器和链接器错误。Python打包生态系统解决这些问题的方法是轮子。轮子是一种二进制分发格式,这意味着它们可以(但不需要)提供预编译的二进制扩展和其他共享对象,这些对象可以按原样安装,而无需自定义构建步骤。这就是ABI兼容性绝对重要的地方:CPython解释器盲目加载二进制轮,因此实际和预期的解释器ABIs之间的任何不匹配都可能导致崩溃(甚至更糟的是,可利用的内存损坏)。因为轮子可以包含预编译的扩展,所以需要为支持的Python版本标记轮子。此标记使用PEP425-style“兼容性”标记:microx-1.4.1-cp37-cp37m-macosx_10_15_x86_64.whl指定了为macO10.15x86-64系统的CPython3.7构建的轮子,这意味着其他Python版本、主机OS和体系结构不应尝试安装它。就其本身而言,这种限制使CPython扩展的轮子包装有点麻烦:?为了支持{Python版本、主机操作系统、主机体系结构}所有有效组合,打包者必须为每个组合构建一个有效的轮子。这导致了额外的测试、构建和分发复杂性,以及随着软件包支持矩阵的扩展而呈指数级的CI增长。?因为轮子(默认情况下)会绑定到一个Python版本,所以打包人员需要在每个Python次要版本更改时生成一组新的轮子。换言之:新的Python版本一开始只能访问打包生态系统的一小部分,直到打包者及时更新。这就是稳定的ABI至关重要的原因:版本打包者可以为最低支持的Python版本构建一个“abi3”轮子,而不是为每个Python构建一个轮子。这就保证了轮子将在所有未来(次要)版本上运行,解决了构建矩阵大小问题和上述生态系统自扩展问题。构建“abi3”轮子有两步:轮子在本地构建(通常使用与源发行版相同的构建系统),然后使用abi3重新标记为ABI标记,而不是单个Python版本(如CPython3.7的cp37)。关键的是,这两个步骤都没有得到验证,因为Python的构建工具没有很好的方法来验证它们。这出现了更大的难题:为了依靠稳定的API和ABI正确构建轮子,构建时需要将Py_LIMITED_API宏设置为预期的CPython支持版本(或者,对于RustwithPyO3,使用正确的构建功能)。这可以防止Python的C标头使用不稳定的功能或潜在地内联不兼容的实现细节。例如,要将轮子构建为cp37-abi3(CPython3.7+的稳定ABI),扩展需要在其自己的源代码中#definePy_LIMITED_API0x03070000,或者使用setuptools.Extension构造的define_macros参数来配置它。这些很容易遗忘,然而遗忘时不会产生任何警告!此外,在使用setuptools时,打包者可以选择设置py_limited_api=True。但这并没有实现任何实际的API限制;它只是将.abi3标记添加到构建的扩展名的文件中。CPython解释器当前没有检查这一点,因此这实际上是一个禁忌。要为稳定的ABI标记轮子,官方轮子模块和bdist_wheel子命令的用户需要使用--py-limitedapi=cp37标志,其中37是最低CPython目标版本(此处为3.7)。此标志控制轮子的文件名组件,如下所示:关键的是,它不会影响实际的轮子构造。轮子是由底层设置工具构建的。扩展看起来很适宜:它可能完全正确,可能有点错误(稳定的ABI,但适用于错误的CPython版本),也可能完全错误。这种崩溃是因为Python打包的下放性质:构建扩展的代码在pypa/setuptools中,而构建轮子的代码在pypa/swheel中——两个完全独立的代码库。扩展构建被设计为一个黑盒子,Rust和其他语言生态系统利用了这一事实(在基于PyO3的扩展中,没有Py_LIMITED_API宏可以明智地定义——所有这些都由构建特性单独处理)。总的来说:?稳定的ABI(“abi3”)轮子是打包本地扩展的唯一可靠方式,无需大量构建矩阵。?然而,所有控制abi3兼容车轮构建的控制盘都无法相互关联:可能是构建一个abi3兼容的车轮,却没有如此标记。或者构建一个非bi3车轮并将其错误地标记为兼容;或者错误地将abi3兼容轮标记为与不合适的CPython版本兼容。?因此,当前abi3兼容轮子生态系统的正确性值得怀疑。ABI违规可能会导致崩溃,甚至是可利用的内存损坏,因此我们需要量化当前的状态。

实际有多糟糕?

这一切看起来都很糟糕,但这只是一个抽象的问题:也有可能每个Python打包者都正确地构建了轮子,并且没有发布任何错误标记(或完全无效)的abi3样式轮子。为了了解事情到底有多糟糕,开发了一个审计系统。Abi3audit的存在的理由是发现这些类型的ABI违规错误:它扫描单个扩展、Python轮子(可以包含多个扩展)和整个包历史记录,报告任何与所指定的稳定ABI版本不匹配或与稳定ABI完全不兼容的内容。为了获得一个可审计包的列表,将其输入到abi3audit中,使用PyPI的公共BigQuery数据集生成了过去21天内从PyPI下载的每个包含abi3轮子的包的列表:(此处选择了21,因为在测试时突破了BigQuery配额。尽管预计回报会逐渐减少,看到一年内的完整下载列表或PyPI的整个历史会很有趣。)从这个查询中,得到了357个包,这些包是作为GitHubGist上传的。保存了这些包后,只需一次调用即可获得来自abi3audit的JSON报告:该审计的JSON也可以作为GitHubGist提供。首先,一些高级统计数据:?在从PyPI查询的357个初始包中,有339个实际审计的轮子。有些是404s(可能是一开始创建然后删除的),而另一些是用abi3标记的,但实际上不包含任何CPython扩展模块(从技术上讲,这确实使它们与abi3兼容!)。其中有几个是ctypes风格的模块,要么有一个供应商提供的库,要么有加载主机预期所包含库的代码。?剩下的339个包裹之间总共有13650个贴有标签的轮子。最大的(以轮子计)是eclipse-zenoh-nightly,有1596个轮子(占PyPI上所有abi3标记轮子的近12%)。?13650个abi3标记的轮子总共有39544个共享对象,每个共享对象之间都有一个潜在的Python扩展。换句话说:平均每个abi3标记的轮子中有2.9个共享对象,每个对象都由abi3audit审核。?如果试图解析每个abi3标记的轮子中的每个共享对象会发现各种奇怪的结果:许多轮子包含无效的共享对象:以废话开头的ELF文件(但在文件后面包含一个有效的ELF)、未清理的临时构建工件,以及少数轮子似乎包含手动修改二进制文件的编辑器样式交换文件。不幸的是,与Moyix不同,我们没有发现任何猫耳。现在,有趣的部分:在357个有效包中,有54个(15%)包含违反ABI版本的轮子。换句话说:大约六分之一的包中有轮子声称支持特定的Python版本,但实际上使用的是较新Python版本的ABI。更严重的是:在357个有效的程序包中,11个(3.1%)包含了完全违反ABI的行为。换言之:大约三十分之一的包中有轮子声称与ABI兼容,但根本不兼容!总共有1139个(约3%)Python扩展存在版本冲突,90个(约0.02%)存在完全的ABI冲突。这说明了两件事:同一个包在多个轮子和扩展中往往会违反ABI,而同一轮子中的多个扩展往往会同时违反ABI。PyQt6和sipPyQt6和sip都是Qt项目的一部分,并且都存在ABI版本冲突:多个轮子被标记为CPython3.6(cp36-abi3),但API仅在CPython3.7中稳定。

此外,sip还有一些完全违反ABI的轮子,全部来自内部_Py_DECREFAPI:refl1drefl1d是NIST的反射软件包。NIST发布了几个标记为Python3.2的稳定ABI(绝对最低)的版本,而实际上目标是发布Python3.11的稳定ABI(绝对最高-甚至还没有发布!)hdbclihdbcli是SAPHANA的专有客户端,由SAP自己发布。它被标记为abi,这很酷!然而不幸的是,它实际上并不兼容abi3:gdp与pifacecam虽然这是两个较小的包,但很有趣的是,它们都存在稳定的ABI违规,这种违规不仅仅是引用/计数助手API:Dockerfile最令人满意的是这个,因为它是用Go编写的Python扩展,而不是、C++或Rust!维护人员的思路是正确的,但没有将Py_LIMITED_API定义为任何特定值。Python的头文件“非常有用”地解释了这一点:

前进之路


唯一的希望是列表中大多数极受欢迎的软件包都没有=存在ABI违规或版本不匹配问题。例如,加密技术和bcrypt都没有出现,这表明在这一方面有强大的开发控制。其他相对流行的软件包也存在版本冲突,但它们一般都很小(例如:期望一个仅在3.7中稳定的函数,但从3.3开始就一直存在)。然而,总的来说,这些结果并不好。这些结果表明:(1)PyPI上的“abi3”轮子的很大一部分根本不兼容abi3(或者与声称的不同版本兼容);(2)维护人员不完全理解控制abi3标记的不同旋钮(并且这些旋钮实际上不会修改开发本身)。总之,实验结果表明这需要更好的控件、更好的文档以及Python不同打包组件之间更好的互操作。然而,尽管几乎所有软件包的维护人员都试图改进,但并没有找到实际构建abi3兼容的轮子所需的额外步骤。此外,除了改进这里的包端工具之外,审核也是自动化的。设计abi3audit的部分目的是为了证实PyPI可以在这些轮子错误成为公共索引的一部分之前捕获它们。

发布人:qusun4912 发布时间:2024-08-17