- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我很难让我的测试框架同时适用于 Python2 和 Python3 的 C 扩展模块。我喜欢通过 doctest
运行我的文档字符串,以确保我没有向用户提供不良信息,因此我想将运行 doctest
作为我测试的一部分。
我不相信我的问题的根源是文档字符串本身,而是 doctest
模块如何尝试读取我的扩展模块。如果我用 Python2 运行 doctest
(在针对 Python2 编译的模块上),我会得到我期望的输出:
$ python -m doctest myext.so -v
...
1 items passed all tests:
98 tests in myext.so
98 tests in 1 items.
98 passed and 0 failed.
Test passed.
但是,当我使用 Python3 执行相同操作时,我得到一个 UnicodeDecodeError
:
$ python3 -m doctest myext3.so -v
Traceback (most recent call last):
...
File "/usr/local/Cellar/python3/3.3.3/Frameworks/Python.framework/Versions/3.3/lib/python3.3/doctest.py", line 223, in _load_testfile
return f.read(), filename
File "/usr/local/Cellar/python3/3.3.3/Frameworks/Python.framework/Versions/3.3/lib/python3.3/codecs.py", line 301, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xcf in position 0: invalid continuation byte
为了获得更多信息,我通过 pytest
运行它并进行完整回溯:
$ python3 -m pytest --doctest-glob "*.so" --full-trace
...
self = <encodings.utf_8.IncrementalDecoder object at 0x102ff5110>
input = b'\xcf\xfa\xed\xfe\x07\x00\x00\x01\x03\x00\x00\x00\x08\x00\x00\x00\r\x00\x00\x00\xd0\x05\x00\x00\x85\x00\x00\x00\x00\x...edString\x00_PyUnicode_FromString\x00_Py_BuildValue\x00__Py_FalseStruct\x00__Py_TrueStruct\x00dyld_stub_binder\x00\x00'
final = True
def decode(self, input, final=False):
# decode input (taking the buffer into account)
data = self.buffer + input
> (result, consumed) = self._buffer_decode(data, self.errors, final)
E UnicodeDecodeError: 'utf-8' codec can't decode byte 0xcf in position 0: invalid continuation byte
/usr/local/Cellar/python3/3.3.3/Frameworks/Python.framework/Versions/3.3/lib/python3.3/codecs.py:301: UnicodeDecodeError
看起来 doctest
实际上是读取 .so
文件来获取文档字符串(而不是导入模块),但是 Python3 没有'知道如何解码输入。我可以通过自己尝试读取 .so
文件来复制字节字符串和回溯来确认这一点:
$ python3
Python 3.3.3 (default, Dec 10 2013, 20:13:18)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> open('myext3.so').read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/Cellar/python3/3.3.3/Frameworks/Python.framework/Versions/3.3/lib/python3.3/codecs.py", line 301, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xcf in position 0: invalid continuation byte
>>> open('myext3.so', 'rb').read()
b'\xcf\xfa\xed\xfe\x07\x00\x00\x01\x03\x00\x00\x00\x08\x00\x00\x00\r\x00\x00\x00\xd0\x05...'
有没有其他人遇到过这个问题?是否有标准(或不太标准)的方法让 doctest
对 python3 上的 C 扩展模块执行测试?
更新:我还应该补充一点,我在 Travis-CI ( see here ) 上得到了相同的结果,所以它不是特定于我的本地构建。
最佳答案
我已经找到解决这个问题的方法,所以我会发布它,但我发现它相当不令人满意。我仍在为此寻找更优雅/更简单的解决方案。
doctest.py
存在三个问题,需要克服这些问题才能使其正常工作:
1) 让 doctest 将 .so 文件视为 python 模块。
如果您查看 doctest.py
源代码,您会注意到在测试运行器中有一个类似于此的 block (取决于您运行的 python 版本):
if filename.endswith(".py"):
# It is a module -- insert its dir into sys.path and try to
# import it. If it is part of a package, that possibly
# won't work because of package imports.
dirname, filename = os.path.split(filename)
sys.path.insert(0, dirname)
m = __import__(filename[:-3])
del sys.path[0]
failures, _ = testmod(m)
else:
failures, _ = testfile(filename, module_relative=False)
这里发生的事情是 doctest.py
正在检查“.py”扩展名,如果是这样,文件将作为 python 模块加载,否则文件将被读取,就好像它是文本(就像一个 README.rst 可能)。我们需要获取 doctest.py
来确认扩展名为“.so”的文件是一个 python 模块。为此,只需修改此 if
block 以读取
if filename.endswith(".py") or filename.endswith(".so"):
...
2)获取doctest识别C扩展模块中的函数
doctest.py
使用 inspect.isfunction函数以确定在模块对象中递归搜索文档字符串时哪些对象是函数。这个函数的问题是它只识别用 python 编写的函数,而不是用 C 编写的函数(python 将 C 扩展函数识别为内置函数)。因此,为了在模块中递归时识别我们的函数,我们需要使用 inspect.isbuiltin。相反。
为了纠正这个问题,我们需要在 doctest.py
中找到 DocTestFinder._find
方法并更改它查找函数的方式。我转换了
# Recurse to functions & classes.
if ((inspect.isfunction(val) or inspect.isclass(val)) and
self._from_module(module, val)):
self._find(tests, val, valname, module, source_lines,
globs, seen)
到
# Recurse to functions & classes.
if ((inspect.isbuiltin(val) or inspect.isclass(val)) and
self._from_module(module, val)):
self._find(tests, val, valname, module, source_lines,
globs, seen)
3)正确删除.so文件上的版本标签(仅限Python3)。
在 Python3 上,C 扩展可以用版本标识符标记(即“myext.cpython-3mu.so”,请参阅 PEP 3149)。我们需要知道如何在 doctest.py
测试运行器中进行初始导入时删除它。
为此,我转换了行
m = __import__(filename[:-3])
到
from sysconfig import get_config_var
m = __import__(filename[:-3] if filename.endswith(".py") else filename.replace(get_config_var("EXT_SUFFIX"), ""))
只有 Python3 才需要。
进行这些修改后,我可以让 doctest 在 Python2 和 Python3 上按预期工作。由于这些修改相当烦人,我制作了一个 patch_doctest.py
脚本,它会自动执行此操作并将修补后的 doctest.py
放在您的当前目录中。你可以得到这个文件here如果你想使用它。然后您可以像这样在扩展模块上运行测试
$ python2 patch_doctest.py
$ python2 -m doctest myext2.so
$ rm doctest.py
$ python3 patch_doctest.py
$ python3 -m doctest myext3.so
作为这有效的证据,here are the new Travis-CI results .
关于python - Doctests 在 C 扩展和 Python3 上因 UnicodeDecodeError 而失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25131630/
我有一个文本文件,发布者(美国证券交易委员会)声称该文件以 UTF-8 编码(https://www.sec.gov/files/aqfs.pdf,第 4 节)。我正在使用以下代码处理这些行: def
在 django 界面中添加元素时遇到问题。我有两个定义: # -*- coding: utf-8 -*- class VisitType(models.Model): name=models
我尝试制作一个脚本来使用 Mechanize 发布表单 剧本: # Browser br = mechanize.Browser() cj = cookielib.LWPCookieJar() br.
我收到此错误: UnicodeDecodeError: 'ascii' codec can't decode byte 0xe0 in position 4: ordinal not in range
我正在尝试使用 Google 语音 API 在 Python 中制作语音识别器。我一直在使用和改编 here 中的代码(转换为Python3)。我在计算机上使用一个音频文件,该文件已使用在线转换器从
打开一个docker实例(例如docker run -ti ubuntu:16.04),创建Python文件a.py # -*- coding: utf-8 -*- a = 'ö' 和r.py wit
当我将应用程序与Buildozer for Android打包在一起时,我会收到UnicodeDecodeError。 Log2与Buildozer一起附加 build.py 。 作业系统:UBUNT
我在 Ubuntu 终端(编码设置为 utf-8)中运行此代码段两次,一次使用 ./test.py然后用 ./test.py >out.txt : uni = u"\u001A\u0BC3\u1451
我正在尝试使用 Python 中的以下命令序列替换 Word 文件中的子字符串。代码本身工作得很好 - 即使使用完全相同的 Word 文件,但当将其嵌入到更大规模的项目结构中时,它会在确切的位置抛出错
我在 tox 中有以下配置: [tox] envlist = py37 [testenv] passenv = TRAVIS TRAVIS_* setenv = DEFAULT_FROM =
我正在获取 UnicodeDecodeError: 'ascii' codec can't decode byte 0xb0 in position 104: ordinal not in range
在执行子字符串匹配时,我收到 UnicodeDecodeError: 'ascii' codec can't Decode byte 0xc3 inposition 8: ordinal not in
我正在使用 Python 和 lxml,但遇到错误 我的代码 >>>import urllib >>>from lxml import html >>>response = urllib.urlope
我是 python 的新手,我正在尝试处理一小部分 Yelp!数据集是 JSON,但我使用 pandas 库和 NLTK 转换为 CSV。 在对数据进行预处理时,我首先尝试删除所有标点符号以及最常见的
我想不出如何一劳永逸地解决这些问题。当我尝试写“è”(我是意大利人)时,我第一次遇到这些问题。经过一些研究,我发现在最开始添加“#coding: utf-8”似乎可以解决问题....直到现在。 我编辑
我的数据存储包含值,我希望我的表单能够更新这些值。我在 jinja2 中使用 wtforms 和谷歌应用引擎。我收到一条我无法理解的错误消息: 'ascii' codec can't decode b
我们遇到了一个问题(描述为 http://wiki.python.org/moin/UnicodeDecodeError)——请阅读第二段“...自相矛盾...”。 具体来说,我们正在尝试将字符串向上
我正在尝试标记一些文档,但我遇到了这个错误 UnicodeDecodeError: 'ascii' codec can't decode byte 0xef in position 6: ordina
我想在一个文件中存储一个包含多个 numpy 数组的 Python 对象。我找到了 pickle,但在加载存储的对象时总是遇到 UnicodeDecodeError: Traceback (mos
我正在尝试制作一个 python 脚本来查找 USB 闪存驱动器中的重复文件。 我遵循的过程是创建一个文件名列表,散列每个文件,然后创建一个逆向字典。然而,在过程中的某个地方,我得到了一个 Unico
我是一名优秀的程序员,十分优秀!