【发布时间】:2020-12-22 11:35:16
【问题描述】:
我已经阅读了几个类似的主题,但还没有成功。我觉得我错过或误解了一些基本的东西,这就是我失败的原因。
我有一个用 python 编写的“应用程序”,我想在标准 setup.py 的帮助下进行部署。由于功能复杂,它由不同的 python 模块组成。但是这个模块单独发布没有意义,因为它们太具体了。
预期的结果是在pip install 的帮助下将软件包安装在系统中,并且可以通过简单的app 命令从操作系统命令行获得。
将长篇故事简化为可重现的示例 - 我有以下目录结构:
<root>
├─ app
| ├─ aaa
| | └── module_a.py
| ├─ bbb
| | └── module_b.py
| └── app.py
├─ docs
| └── .....
├─ tests
| └── .....
└─ setup.py
以下是模块代码:
app.py
#!/usr/bin/python
from aaa.module_a import method1
from bbb.module_b import method2
def main():
print("APP main executed")
method1()
method2()
if __name__ == '__main__':
main()
module_a.py
def method1():
print("A1 executed")
module_b.py
def method2():
print("B2 executed")
当我从控制台运行 app.py 时,它可以正常工作并给出预期的输出:
APP main executed
A1 executed
B2 executed
所以,这个简单的“应用程序”运行良好,我想通过以下方式分发它
setup.py
from setuptools import setup
setup(
name="app",
version="1.0",
packages=['app', 'app.aaa', 'app.bbb'],
package_dir={'app': 'app'},
entry_points={
'console_scripts': ['app=app.app:main', ]
}
)
再次,一切看起来都不错,测试安装看起来也不错:
(venv) [user@test]$ pip install <root>
Processing /home/user/<root>
Using legacy 'setup.py install' for app, since package 'wheel' is not installed.
Installing collected packages: app
Running setup.py install for app ... done
Successfully installed app-1.0
(venv) [user@test]$
现在问题来了。使用 setup.py 中的上述 entry_points 我希望能够使用 ./app 命令执行我的应用程序。确实有效。但应用程序本身失败并显示错误消息:
File "/test/venv/lib/python3.9/site-packages/app/app.py", line 3, in <module>
from aaa.module_a import method1
ModuleNotFoundError: No module named 'aaa'
我理解错误的原因 - 这是因为pip install 将目录aaa 和bbb 与app.py 放在一个目录app 中。 IE。从这个角度来看,app.py 应该使用import app.aaa 而不是import aaa。但是如果我这样做了,那么我的应用程序在开发过程中会出现错误:
ModuleNotFoundError: No module named 'app.aaa'; 'app' is not a package
这也是合乎逻辑的,因为当时没有可用的app 包......(它正在开发中,尚未安装在系统中......)
最后。问题是 - 为包含几个自己的模块的独立 python 应用程序创建目录结构和 setup.py 的正确方法是什么?
UPD
最有希望的结果(但在评论中讨论后证明是错误的)是我在进行了以下更改后得出的:
- 将 app.py 从
<root>/app移动到<root>本身 - 我在
setup.pypy_modules=['app']中引用了它 - 我将导入从
import aaa.method1更改为import app.aaa.method1等。
这种方式包在我的开发环境和安装后都可以工作。
但是我遇到了entry_points 的问题 - 我看不到如何配置入口点以使用来自app.py 的main(),这不是app 包的一部分,而是一个单独的模块....
IE。新结构是
<root>
├─ app
| ├─ aaa
| | └── module_a.py
| ├─ bbb
| | └── module_b.py
| └──__init__.py
├─ docs
| └── .....
├─ tests
| └── .....
├─ app.py
└─ setup.py
即这里的逻辑 - 拥有 2 个独立的实体:
- 一个空包
app(仅包含init.py)和子包aaa、bbb等。 - 一个脚本
app.py,它使用来自子包app.aaa、app.bbb的函数
但正如我所写 - 我看不出如何为 app.py 定义入口点以允许它直接从操作系统命令行运行。
【问题讨论】:
-
看起来您不需要
setup.py中的package_dir参数。不要认为它会解决任何问题,但仍然。 -- 你说你用./app调用应用程序,这对我来说似乎很奇怪。当前目录中不应有任何app可执行文件。但是您应该可以直接致电app。 ——确实,进口似乎是错误的。也许看看这个:sinoroc.gitlab.io/kb/python/python_imports.html -
我试图删除
package_dir- 没有任何改变。 -
很抱歉与
./app和app混淆。我在安装后将其称为app,因为它在entry_points中被引用,但是当我在开发环境中时-我从IDE 或直接从命令行运行它作为./app.py在正确的目录中 -
关于您的最新编辑:您绝对不能同时拥有名为
app的顶级包和名为app的顶级模块。我看不出这怎么可能奏效。即使它确实有效,我也不建议这样做。 -
实际上它可以工作并且似乎是一个很好的前进方式,因为它将主题分成空包
app(这就是我在那里添加__init__.py的原因)和子包app.aaa,@987654376 @ 和单个模块app.py导入和使用这些子包。所以,它背后有一个逻辑,我没有看到矛盾。我只是看不到如何为不属于包的单个 python 文件设置入口点(命令的语法需要包名....)
标签: python setuptools setup.py