python - 我可以使用 python 模块的 main 进行测试吗?
问题描述
我正在 Python 3.8.2 上开发一个 Python 库,并且我想将一个模块作为主要模块运行以进行测试。当我尝试时,我收到 ModuleNotFound 错误。
这是我的库结构:
.
├── foo
│ ├── __init__.py
│ ├── bar.py
│ └── quux
│ ├── __init__.py
│ ├── corge.py
│ └── garply.py
├── main.py
巴里.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")
主文件
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 需要知道所在的目录foo
才能导入它。 sys.path
列出 python 将搜索的目录。
当您安装一个包时,安装程序会担心这样做——通常是将模块放在一个众所周知的目录中或将安装包路径添加到sys.path
.
当你运行一个脚本时,python 会自动将该脚本的路径添加到sys.path
,所以当你运行时main.py
,你会发现foo
.
如何作为模块运行__main__
一种选择是使软件包可安装(setup.py、wheels 等)并使用开发模式(“pip install --editable ./”与“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
作为脚本运行时,它获得的命名空间与作为模块导入的命名空间__main__
不同foo.bar.corge
。它的全局变量/类/函数被加载了两次,你会得到不同的变量,这取决于你是通过__main__
命名空间还是foo.bar.corge
.
最好将您想要放入 main in 的任何内容corge.py
,使它们成为单独的脚本。例如,您可以添加def main()
到您的模块中。您可以在其中main.py
添加一个选项--run foo.bar.corge
,告诉 main 导入corge.py
并运行其main()
. argparse
子命令可用于此。
推荐阅读
- ios - SwiftUI:对同一个变量使用不同的属性包装器
- node.js - 如何在 nodejs 中检查设备系统信息(如 RAM 大小和实际使用情况)
- vue.js - vue 开发工具不适用于 vue-cli 项目
- asynchronous - Vala 产量未返回
- javascript - 使用 Greensocks GSAP 在桌面上为 BrowserWindow 设置动画
- python - 如何设置后台计时器,并在功能完成时停止功能,同时仍在等待反应
- c++ - 代码块在执行代码时给我以下错误(甚至奇偶汉明代码)
- python-3.x - PDFMiner:如何仅提取正文
- node.js - heroku 上的 Angular 构建失败找不到模块,但在本地工作正常
- java - 非解析 POM。.m2 文件夹中的 POM 已损坏