我可以使用 python 模块的 main 进行测试吗?
Can I use a python module's main for testing?
我正在 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,但还是不行。如果我直接 copy/paste 命令并使用他们的 CWD,它不起作用。它不适用于 PyCharm 内置的终端。它 仅 与 PyCharm 运行 按钮一起使用。
我的模块结构有问题吗?如果是这样,PyCharm 可以做什么来使这项工作成功?如果不是,为什么它在 PyCharm 之外不起作用?
您对 main.py
的调用有效,因为 Python 将在直接子目录中查找模块。任何其他位置的任何模块想要导入 foo.yadda.whatever
都必须通过搜索您的 PYTHONPATH
找到 foo
。因此,您需要将 foo
的父目录添加到您的 PYTHONPATH
.
Python 需要知道 foo
所在的目录才能导入它。 sys.path
列出 python 将搜索的目录。
当你安装一个包时,安装程序会担心这样做 - 通常是将模块放在一个众所周知的目录中或将安装包路径添加到 sys.path
。
当您 运行 一个脚本时,python 会自动将该脚本的路径添加到 sys.path
,因此当您 运行 main.py
时,您会发现 foo
。
如何运行作为模块作为__main__
一个选项是使软件包可安装(setup.py、wheels 等....)并使用开发模式( 进行一些讨论)。这就是我在开发计划提供给其他人的东西时所做的事情。
另一种是将你的目录添加到 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
作为脚本时,它获得的命名空间 __main__
与作为模块导入的 foo.bar.corge
不同。它的全局变量 / 类 / 函数被加载两次,根据你是通过 __main__
命名空间还是 foo.bar.corge
调用它们,你会得到不同的变量。
最好将任何您想放入 corge.py
中的主脚本,让它们成为独立的脚本。例如,您可以将 def main()
添加到您的模块中。在 main.py
中,您可以添加一个选项 --run foo.bar.corge
告诉 main 导入 corge.py
和 运行 它的 main()
。 argparse
subcommands 可用于此。
我正在 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,但还是不行。如果我直接 copy/paste 命令并使用他们的 CWD,它不起作用。它不适用于 PyCharm 内置的终端。它 仅 与 PyCharm 运行 按钮一起使用。
我的模块结构有问题吗?如果是这样,PyCharm 可以做什么来使这项工作成功?如果不是,为什么它在 PyCharm 之外不起作用?
您对 main.py
的调用有效,因为 Python 将在直接子目录中查找模块。任何其他位置的任何模块想要导入 foo.yadda.whatever
都必须通过搜索您的 PYTHONPATH
找到 foo
。因此,您需要将 foo
的父目录添加到您的 PYTHONPATH
.
Python 需要知道 foo
所在的目录才能导入它。 sys.path
列出 python 将搜索的目录。
当你安装一个包时,安装程序会担心这样做 - 通常是将模块放在一个众所周知的目录中或将安装包路径添加到 sys.path
。
当您 运行 一个脚本时,python 会自动将该脚本的路径添加到 sys.path
,因此当您 运行 main.py
时,您会发现 foo
。
如何运行作为模块作为__main__
一个选项是使软件包可安装(setup.py、wheels 等....)并使用开发模式(
另一种是将你的目录添加到 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
作为脚本时,它获得的命名空间 __main__
与作为模块导入的 foo.bar.corge
不同。它的全局变量 / 类 / 函数被加载两次,根据你是通过 __main__
命名空间还是 foo.bar.corge
调用它们,你会得到不同的变量。
最好将任何您想放入 corge.py
中的主脚本,让它们成为独立的脚本。例如,您可以将 def main()
添加到您的模块中。在 main.py
中,您可以添加一个选项 --run foo.bar.corge
告诉 main 导入 corge.py
和 运行 它的 main()
。 argparse
subcommands 可用于此。