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

问题描述 投票:0回答:2

我正在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中的终端。它only与PyCharm Run按钮一起使用。

我的模块结构有问题吗?如果是这样,PyCharm可以做些什么?如果没有,为什么它在PyCharm之外不起作用?

python python-3.x pycharm python-module
2个回答
0
投票

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


0
投票

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作为脚本运行时,它获得的名称空间__main__与作为模块导入的foo.bar.corge不同。它的全局变量/类/函数被加载两次,并且根据是通过__main__名称空间还是通过foo.bar.corge调用它们而获得不同的变量。

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

© www.soinside.com 2019 - 2024. All rights reserved.