如何使 python 包导入在 main 中工作?

How to make python package imports work in main?

我有一个这样组织的简单应用程序:

.
`-- app
    |-- __init__.py (empty)
    |-- extensions.py (just defines foo())
    `-- server.py

和 server.py 看起来像这样:

from app.extensions import foo

运行 这作为 python app/server.py 当然不起作用,因为当 运行 来自命令行时 server.py 不是 "in" 应用程序。它的包名是__main__。我知道 运行 宁它作为 python -m app.server 工作,但由于各种原因我不能这样做(其中,我正在使用重新加载器,并且 python -m<foo> "helpfully" 重写带有完整模块名称的 argv)。

我想我理解极其复杂的 Python package/module 导入内容,至少我已经阅读了很多。我认为解决这个问题的标准(?)方法是将应用程序目录添加到 sys.path,类似于 sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))。但这对我来说似乎很奇怪,而且正确地做起来很重要(请参阅有关此主题的其他类似的 SO 问题)。似乎应该有一些方法可以通过设置 __name__ and/or __package____path__ 或其他更隐藏的东西来告诉 python "this is app.server" .

有什么方法可以"fool"这样的包导入系统吗?

不要去那里,没有必要开始研究 Python 包结构。最好的方法是不要将脚本放入包

在包的外部有一个单独的server.py文件,并让它导入app.server或其他模块来完成它的工作。这可以很简单:

import sys
from app.server import main

sys.exit(main(sys.argv[1:]))

除此之外,python -m 可能已经更新 sys.argv[0] 以指向完整的文件名(它基本上等于正在执行的模块中的 __file__),但是 __spec__ module spec 对象包含原始包名称。如果您必须使用 python -m 需要将包的完整限定名称 运行 用作 __main__ 作为 main属性:

print(__spec__.name)

所以对于 package.main 模块,上面会打印 package.main 而不是 __main__:

$ mkdir package
$ touch package/__init__.py
$ echo 'if __name__ == "__main__": print("__spec__.name", __spec__.name)' > package/main.py
$ python3 -m package.main
package.main

因此,如果您的重新加载器要求 sys.argv 包含 -m package.main,您可以将 sys.argv 更新为:

import sys

sys.argv[:1] = ['-m', __spec__.name]