我可以使用 python 模块的 main 进行测试吗?
Posted
技术标签:
【中文标题】我可以使用 python 模块的 main 进行测试吗?【英文标题】:Can I use a python module's main for testing? 【发布时间】:2020-07-29 22:14:34 【问题描述】:我正在 Python 3.8.2 上开发一个 Python 库,我想将一个模块作为主模块运行以进行测试。当我尝试时,我收到 ModuleNotFound 错误。
这是我的库结构:
.
├── foo
│ ├── __init__.py
│ ├── bar.py
│ └── quux
│ ├── __init__.py
│ ├── corge.py
│ └── garply.py
├── main.py
bary.py:
def baz():
print("baz")
corge.py
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 运行按钮。
我的模块结构有问题吗?如果是这样,PyCharm 可以做些什么来完成这项工作?如果不是,为什么它不能在 PyCharm 之外工作?
【问题讨论】:
这能回答你的问题吗? Python ModuleNotFoundError Testsuite (Import Error) ***.com/search?q=python+modulenotfounderror 【参考方案1】: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__
设为绝对并剥离几个目录名称
corge.py
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
作为脚本运行时,它会获得与作为模块导入的foo.bar.corge
不同的命名空间__main__
。它的全局变量/类/函数被加载了两次,根据你是通过__main__
命名空间还是foo.bar.corge
调用它们,你会得到不同的变量。
最好将任何你想要放入corge.py
的主目录中的东西,将它们分开的脚本。例如,您可以将def main()
添加到您的模块中。在main.py
中,您可以添加一个选项--run foo.bar.corge
,告诉main 导入corge.py
并运行它的main()
。 argparse
subcommands 可以用于此。
【讨论】:
这很有意义,谢谢!我最初的计划是使用它来运行单元测试,在模块中定义test()
方法是否合适,或者应该以不同的方式处理?
是的,您可以将测试与代码放在一起 - 我不确定它的常见做法,但如果您这样做,它不会引起人们的注意。但是您将代码放入在正常运行时不需要存在的模块中。我倾向于使用nose
或只是 python 附带的 vanilla unittest(pytest 是另一种选择),我将它们分开到一个单独的目录中。我认为你应该为你的测试框架做任何最正常的事情。【参考方案2】:
您对main.py
的调用有效,因为Python 将在直接子目录中查找模块。想要导入foo.yadda.whatever
的任何其他位置的任何模块都必须通过搜索PYTHONPATH
来找到foo
。因此,您需要将foo
的父目录添加到您的PYTHONPATH
。
【讨论】:
以上是关于我可以使用 python 模块的 main 进行测试吗?的主要内容,如果未能解决你的问题,请参考以下文章
在标准输入上使用相对导入的Python 3脚本给出错误:没有名为'__main__.XXX'的模块; '__main__'不是包