1

我正在重构一个库以将 importlib.resources 用于 python 3.7+。我正在使用 importlib_resources backport 来实现 python 3.6 的兼容性。该代码适用于 python 3.6-3.8。但是,使用 pyfakefs 的 pytest 测试在 3.6 中失败。在测试条件下,使用 importlib_resources 返回的路径被破坏(但在“真实世界”条件下,它们正确返回)。

一个最小的例子:我有以下库结构:

mypackage
├── mypackage
│   ├── __init__.py
│   ├── bin
│   │   └── __init__.py
│   └── find_bin.py
└── tests
    └── test_find_bin.py

在实际库中, bin 文件夹包含二进制文件(加上一个空的__init__)。库中其他地方的代码需要一个路径。find_bin.py演示一个将返回路径的函数:

import sys
if sys.version_info >= (3, 7):
    from importlib import resources
else:
    import importlib_resources as resources

import mypackage.bin


def find_bin():
    init_path_context = resources.path(mypackage.bin, '__init__.py')
    with init_path_context as p:
        init_path = p
    bin_path = init_path.parent
    return bin_path

pytest 测试test_find_bin.py

import pathlib

from mypackage.find_bin import find_bin


def test_findbin(fs):
    test_bin = (pathlib.Path(__file__)
                .resolve()
                .parent.parent
                .joinpath('mypackage', 'bin'))
    print('test bin is ', test_bin)
    expected_bin = find_bin()
    print('expected bin is ', expected_bin)
    assert not expected_bin.exists()
    print('test creating fakefs ', test_bin)
    fs.create_dir(test_bin)
    assert expected_bin.exists()

Python 3.7+ 按预期工作。在 python 3.6 中,expected_bin 路径被破坏:

test bin is  /Users/geoffreysametz/Documents/mypackage/mypackage/bin  # correct
expected bin is  /var/folders/bv/m5cg5cp15t38sh8rxx244hp00000gn/T  # ?!?!?!

我试图跟踪 find_bin 函数的执行,它很长而且很复杂。但是,我看到它importlib_resources使用了 python 的 FakeFilesystem 类。我的假设是问题出在importlib_resourcespytest 和同时使用假文件系统。

我的假设正确吗?是否有解决方法让 pytest 测试使用 importlib_resources 的代码?

4

1 回答 1

1

pyfakefs 中的假文件系统在测试开始时为空(模块所需的临时文件路径除外tempfile),因此如果您想在测试中访问真实文件系统中的任何文件,您必须将它们映射到假文件系统中

def test_findbin(fs):
    test_bin = (pathlib.Path(__file__)
                .resolve()
                .parent.parent
                .joinpath('mypackage', 'bin'))
    fs.add_real_directory(test_bin)
...

在您的真实测试中,您事先并不知道路径,因此您必须添加一些您知道的父路径来包含您的资源。子目录和文件将在访问时被读取并复制到假文件系统。

至于您在 Python 3.6 中的测试行为:第一个代码contextlib_package检查(正确的)路径是否存在,如果不存在(因为它检查假文件系统,它不会存在),它会创建一个它尝试复制一些数据的临时文件 - 因此输出中的临时路径。

在 Python 3.8 中,它似乎不检查路径是否存在,它只是Path从正确的路径创建一个对象。当您尝试访问该路径中的某些资源时,无论如何这都会失败,因为它们不存在于假文件系统中,因此在这种情况下您也必须映射真实文件系统。

(复制并稍微改编自pyfakefs 问题中的答案)

于 2020-02-05T19:28:21.280 回答