【问题标题】:How to import file in Python with changed entry point (sys.path)?如何使用更改的入口点(sys.path)在 Python 中导入文件?
【发布时间】:2021-11-16 21:50:31
【问题描述】:

我有这个结构:

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?

【问题讨论】:

    标签: python import relative-path sys.path relative-import


    【解决方案1】:

    因为您当前的工作目录与您的路径不同,并且您使用了相对路径 (open("sets.json") == open("./sets.json")),所以 python 在您的顶级目录中寻找 sets.json,而不是在 @ 内部987654324@.

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

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

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

    【讨论】:

      【解决方案2】:

      /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:
              ...
      

      【讨论】:

      • 注:如果使用/api 调用api.py,这将失败。但根据 OP 的用例,这可能确实足够了。
      • @vtasca 这个选项对我来说是错误的,因为我也有 Docker,它从 api/ 文件而不是从根目录运行 app.py。那么,有没有办法在代码中更改我的当前目录?
      • @PolozAlexey 是的,您可以使用 os.chdir(path) 更改它,但更强大的方法是让您的函数知道它从哪里运行并相应地调整文件路径 - 我已经修改了答案考虑到这种情况。
      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

        运行测试的正确方法是从 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      
        

        【讨论】:

          猜你喜欢
          • 2018-11-02
          • 2016-08-18
          • 1970-01-01
          • 2020-10-19
          • 1970-01-01
          • 1970-01-01
          • 2013-12-16
          • 2012-09-30
          • 1970-01-01
          相关资源
          最近更新 更多