14

我已经开始使用pathlib.Path一段时间了,我喜欢使用它。现在我已经习惯了,我变得马虎了,忘记向str.

tox将+py.test与基于tmpdir(即 a )的临时目录一起使用时,通常会发生这种情况py._path.local.LocalPath

from pathlib import Path
import pytest

def test_tmpdir(tmpdir):
    p = Path(tmpdir) / 'testfile.csv'

str()我不是每次都插入,而是更一般地解决这个问题,但不能。

首先,我尝试制作自己的 Path 类,该类具有以下特性_parse_args

import pytest
from py._path.local import LocalPath
from pathlib import Path, PurePath

def Path(Path):
    @classmethod
    def _parse_args(cls, args):
        parts = []
        for a in args:
            if isinstance(a, PurePath):
                parts += a._parts
            elif isinstance(a, str):
                # Force-cast str subclasses to str (issue #21127)
                parts.append(str(a))
            elif isinstance(a, LocalPath):
                parts.append(str(a))
            else:
                raise TypeError(
                    "argument should be a path or str object, not %r"
                    % type(a))
        return cls._flavour.parse_parts(parts)

def test_subclass(tmpdir):
    p = Path(tmpdir) / 'testfile.csv'

这会抛出一个TypeError: unsupported operand type(s) for /: 'NoneType' and 'str'(也尝试过PosixPath,同样的结果,不希望特定于 Linux)。

我试图猴子补丁Path

import pytest
from pathlib import Path

def add_tmpdir():
    from py._path.local import LocalPath

    org_attr = '_parse_args'
    stow_attr = '_org_parse_args'

    def parse_args_localpath(cls, args):
        args = list(args)
        for idx, a in enumerate(args):
            if isinstance(a, LocalPath):
                args[idx] = str(a)
        return getattr(cls, stow_attr)(args)

    if hasattr(Path, stow_attr):
        return  # already done
    setattr(Path, stow_attr, getattr(Path, org_attr))
    setattr(Path, org_attr, parse_args_localpath)

add_tmpdir()

def test_monkeypatch_path(tmpdir):
    p = Path(tmpdir) / 'testfile.csv'

这会抛出一个AttributeError: type object 'Path' has no attribute '_flavour'(也在猴子修补 PurePath 时)。

最后我尝试包装Path

import pytest
import pathlib

def Path(*args):
    from py._path.local import LocalPath
    args = list(args)
    for idx, a in enumerate(args):
        if isinstance(a, LocalPath):
            args[idx] = str(a)
    return pathlib.Path(*args)

def test_tmpdir_path(tmpdir):
    p = Path(tmpdir) / 'testfile.csv'

这也给出了AttributeError: type object 'Path' has no attribute '_flavour'

我认为在某些时候最后一个有效,但我无法重现。
难道我做错了什么?为什么这么难?

4

3 回答 3

12

如果其他人正在研究 pytest 的tmpdir路径是否可以很好地使用pathlib.Path

使用python 3.6.5and pytest 3.2.1,问题中发布的代码可以很好地工作,而无需显式转换为str

from pathlib import Path

def test_tmpdir(tmpdir):
    p = Path(tmpdir) / 'testfile.csv'
于 2018-07-06T06:44:11.097 回答
3

最后一个(包装)应该可以工作,我怀疑您实际上在一次py.test/tox运行中测试了所有这些,并且猴子补丁仍然有效(这可能解释了为什么它在某些时候有效,测试文件的顺序等很重要,如果你开始改变全局类的东西)。

这很难,因为Path本质上是一个生成器,它会即时决定你是在 Windows 还是 Linux 上,并创建一个WindowsPath响应。PosixPath因此。

BDFL Guido van Rossum 已在 2015 年 5 月指出

听起来确实应该使子类化 Path 变得更容易。

但什么也没发生。对pathlib3.6 中其他标准库的支持有所增加,但 pathlib 本身仍然存在同样的问题。

于 2016-11-24T11:29:03.880 回答
1

为方便起见,您可以创建一个夹具来自动进行包装:

@pytest.fixture
def tmppath(tmpdir):
    return Path(tmpdir)

然后你可以在你的测试中使用tmppath而不是。tmpdir

于 2021-04-09T20:26:41.540 回答