我可以使用python模块的main进行测试吗?

2024-04-27 15:50:04 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在Python3.8.2上开发一个Python库,出于测试目的,我想运行一个模块作为main。当我尝试时,我得到一个ModuleNotFound错误

以下是我的库结构:

.
├── foo
│   ├── __init__.py
│   ├── bar.py
│   └── quux
│       ├── __init__.py
│       ├── corge.py
│       └── garply.py
├── main.py

bary.py:

def baz():
    print("baz")

柯基·皮

from foo.quux.garply import *


def grault():
    waldo()
    print("grault")


if __name__ == '__main__':
    grault()

garply.py

def waldo():
    print("waldo")

main.py

from foo.bar import *
from foo.quux.corge import *

if __name__ == '__main__':
    baz()
    grault()

(所有__init__.py文件均为空)

当我运行main.py时,它会工作

$ python main.py
baz
waldo
grault

如果尝试运行corge.py,则会出现以下错误:

$ python foo/quux/corge.py 
Traceback (most recent call last):
  File "foo/quux/corge.py", line 1, in <module>
    from foo.quux.garply import *
ModuleNotFoundError: No module named 'foo'

不管我当前的工作目录是什么,它总是给出这个错误

$ cd foo/quux/
$ python corge.py 
Traceback (most recent call last):
  File "corge.py", line 1, in <module>
    from foo.quux.garply import *
ModuleNotFoundError: No module named 'foo'

在进行测试时,我使用PyCharm 2020.1创建了一个新的PyCharm项目,并实现了我描述的结构。令我惊讶的是,它与默认检测到的运行配置配合使用

我尝试使用PyCharm自动创建的venv,但仍然不起作用。如果我直接复制/粘贴该命令并使用其CWD,则该命令不起作用。它不适用于PyCharm内置的终端。它与PyCharm Run按钮配合使用

我的模块结构是否有问题?如果是这样的话,PyCharm可以做些什么来实现这一目标?如果不是,为什么它不在PyCharm之外工作


Tags: frompyimportfoomain错误baz结构
2条回答

Python需要知道foo所在的目录才能导入它sys.path列出python将在其中搜索的目录

当你安装一个软件包时,安装程序会担心这样做——通常是将模块放在一个已知的目录中,或者将安装程序包路径添加到sys.path

当您运行脚本时,python会自动将该脚本的路径添加到sys.path,因此当您运行main.py时,您会发现foo

如何作为模块运行__main__

一个选项是使包可安装(setup.py、wheels等),并使用开发模式("pip install editable ./" vs "python setup.py develop"进行一些讨论)。这就是我在开发我计划提供给他人的东西时所做的

另一种方法是将您的目录添加到PYTHONPATH,甚至在您运行该程序时。在linux上,这将是

PYTHONPATH=path/to/fooproject:$PYTHONPATH python foo/quux/corge.py

还有一个,我也这么做了,就是破解模块本身的路径__file__给出相对于当前工作目录的文件名,您知道自己在包层次结构中的位置有多深。因此,您只需将__file__设为绝对值,并去掉几个目录名即可

柯基·皮

import sys
import os

if __name__ == "__main__":
    # I'm two levels deep in the package so package directory is
    packagedir = os.path.abspath(os.path.join(os.path.dirname(__file__),
        "..", ".."))
    sys.path.insert(0, packagedir)
    import foo

最后,一开始不要这样做。当您将corge.py作为脚本运行时,它会得到一个不同于作为模块导入的__main__的命名空间。它的全局变量/类/函数被加载两次,根据您是通过__main__名称空间还是foo.bar.corge调用它们,可以得到不同的变量/类/函数

最好是将任何您想放在corge.py中的内容放在一个main中,使它们成为单独的脚本。例如,您可以将def main()添加到模块中。在main.py中,您可以添加一个选项 run foo.bar.corge,告诉main导入corge.py并运行其main()^{} subcommands可用于此

main.py的调用是有效的,因为Python将在直接子目录中查找模块。任何其他位置中想要导入foo.yadda.whatever的任何模块都必须通过搜索您的PYTHONPATH来查找foo。因此,您需要将foo的父目录添加到PYTHONPATH

相关问题 更多 >