33

在我的一个 Python 测试脚本中,我多次使用这种模式:

sys.path.insert(0, "somedir")
mod =  __import__(mymod)
sys.path.pop(0)

有没有更简洁的方法来临时修改搜索路径?

4

4 回答 4

33

您可以使用简单的上下文管理器

import sys

class add_path():
    def __init__(self, path):
        self.path = path

    def __enter__(self):
        sys.path.insert(0, self.path)

    def __exit__(self, exc_type, exc_value, traceback):
        try:
            sys.path.remove(self.path)
        except ValueError:
            pass

然后导入一个模块,你可以这样做:

with add_path('/path/to/dir'):
    mod = __import__('mymodule')

退出时with语句体sys.path将恢复到原始状态。如果您只在该块中使用该模块,您可能还想从以下位置删除它的引用sys.modules

del sys.modules['mymodule']
于 2016-10-04T15:04:14.980 回答
20

附加一个值sys.path仅临时修改它,即仅用于该会话。

永久修改是通过更改PYTHONPATH默认安装目录来完成的。

因此,如果您暂时只针对当前会话,那么您的方法是可以的,但是如果没有隐藏任何预期在当前目录或默认安装目录中找到的重要模块,您可以删除该pop部分。somedirPYTHONPATH

http://docs.python.org/2/tutorial/modules.html#the-module-search-path

于 2013-06-20T10:19:08.713 回答
5

这是来自 Eugene Yarmash 的 contextmanager 实现的替代实现(使用contextlibpathlib.Path兼容):

import os
import sys
import contextlib
from typing import Iterator, Union

@contextlib.contextmanager
def add_sys_path(path: Union[str, os.PathLike]) -> Iterator[None]:
    """Temporarily add the given path to `sys.path`."""
    path = os.fspath(path)
    try:
        sys.path.insert(0, path)
        yield
    finally:
        sys.path.remove(path)


with add_sys_path('/path/to/dir'):
    mymodule = importlib.import_module('mymodule')
于 2020-11-11T15:10:32.327 回答
1

如果您正在测试 uses pytest,它们有一个很好的夹具,除其他特征外,它可以处理这种确切的情况:

monkeypatch 夹具可帮助您安全地设置/删除属性、字典项或环境变量,或修改 sys.path 以进行导入...所有修改将在请求的测试功能或夹具完成后撤消。raise 参数确定如果设置/删除操作的目标不存在,是否会引发 KeyError 或 AttributeError

在描述syspath_prepend

使用 monkeypatch.syspath_prepend 修改 sys.path,它还将调用 pkg_resources.fixup_namespace_packages 和 importlib.invalidate_caches()。

样品用途:

def test_myfunc(monkeypatch):
  with monkeypatch.context() as m:
    m.syspath_prepend('my/module/path')
    mod =  __import__(mymod)
  # Out here, the context manager expires and syspath is reset
于 2021-05-15T18:08:38.900 回答