如何在更改入口点 (sys.path) 的 Python 中导入文件?

How to import file in Python with changed entry point (sys.path)?

我有这样的结构:

web/   
   api/
       app.py
       sets.json
   tests/
        test.py

app.py:

def func():
    with open('sets.json', 'r') as file:
        ...

test.py:

import sys
import os
sys.path.append(os.getcwd()+'/api/')

from app import func
...

我想 运行 从根目录 (web/) 进行测试。导入成功。但是当调用 func 时,出现错误:FileNotFoundError: [Errno 2] No such file or directory: 'sets.json'.

为什么? Sys.path 已更改,并且可以导入。为什么我无法导入 sets.json?

因为你当前的工作目录和你的路径不一样,而且你用的是相对路径(open("sets.json") == open("./sets.json"))所以python正在寻找sets.json 在您的顶级目录中,而不是在 api.

乱用 sys.path 无论如何都不是导入的可靠解决方案。我不太确定你的 test.py 中有什么,但你最好使用合适的测试运行器(这取决于你如何设置它可能会解决问题),或者你可能想将你的应用程序变成一个 并将其安装在 'development mode' 中,这样就可以通过杂耍路径找到 app

在任何情况下,您都需要一个更强大的解决方案来查找您的数据,例如使用包路径(如果您将使用包分发数据),或者像 ~/.cache/my-app/my-data.[=18 这样的特定路径=]

一些流行的网络框架支持解析到内置包中已部署资源的路径,这可能是您正在寻找的(但您没有说您是否正在使用)。

/api/ 添加到您的 sys.path 不会更改您当前的工作目录,因此为了访问 sets.json 您必须修改您的代码:

def func():
    with open('/api/sets.json', 'r') as file:
        ...

或者,更强大的解决方案涉及使用 pathlib 通过检查路径的最后一部分并更新 .json 相应的文件:

from pathlib import Path
def func():
    if Path.cwd().parts[-1] == 'api':
        set_location = 'sets.json'
    elif Path.cwd().parts[-1] == 'web':
        set_location = 'api/sets.json'
    with open(set_location, 'r') as file:
        ...

因此,我得到了这样的解决方案: 我使用代码创建 tests/conftest.py(对于 Pytest,它允许对所有文件应用我的更改):

import sys
import os
os.chdir(os.getcwd()+'/api/')
sys.path.append(os.getcwd())

使用此解决方案,我可以从 web/api/(在 Docker 中)和从 web/(在 PyTest 中)进行导入,并在 [= 中访问 sets.json 12=].

运行测试的正确方法是 运行 从 web 目录(希望不直接包含 python 文件)作为:

$ python -m tests.test # note no py

您应该删除所有 sys.path hack 并重写相对路径 - 切勿使用 chdir,这可能会导致导入系统中出现非常细微的错误,包括但不限于在不同命名空间中双重导入模块 - 下面将做:

sets_json = os.path.join(os.path.dirnname(__file__), 'sets.json')
def func():
    with open(sets_json, 'r') as ins:
        ... # do not name your variable 'file', shadows a builtin