微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何以正确的方式为独立的 Python 应用程序制作 setup.py?

如何解决如何以正确的方式为独立的 Python 应用程序制作 setup.py?

我已经阅读了几个类似的主题,但还没有成功。我觉得我错过或误解了一些基本的东西,这就是我失败的原因。
我有一个用 python 编写的“应用程序”,我想在标准 setup.py 的帮助下进行部署。由于功能复杂,它由不同的 python 模块组成。但是单独发布这个模块是没有意义的,因为它们太具体了。
预期结果是在 pip install 的帮助下将软件包安装在系统中,并且可以通过简单的 app 命令从 OS 命令行获得。
将长篇故事简化为可重现的示例 - 我有以下目录结构:

<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 将目录 aaabbbapp.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
我现在最有希望的结果(但在评论中讨论后证明是错误的)经过以下更改:

  1. 将 app.py 从 <root>/app 移动到 <root> 本身
  2. 我在setup.py的{​​{1}}中引用了它
  3. 我将导入从 py_modules=['app'] 更改为 import aaa.method1

这种方式包在我的开发环境和安装后都有效。
但是我遇到了 import app.aaa.method1 的问题 - 我看不出如何配置入口点以使用 entry_points 中的 main(),它不是 app.py 包的一部分,而是一个单独的包模块....
IE。新结构是

app

即这里的逻辑 - 有 2 个独立的实体:

  1. 一个空包 <root> ├─ app | ├─ aaa | | └── module_a.py | ├─ bbb | | └── module_b.py | └──__init__.py ├─ docs | └── ..... ├─ tests | └── ..... ├─ app.py └─ setup.py (仅由 init.py 组成)和子包 appaaa
  2. 使用子包 bbbapp.py 中的函数的脚本 app.aaa

但正如我所写的 - 我看不出如何定义 app.bbb 的入口点以允许它直接从 OS 命令行运行。

解决方法

使用该目录(包)结构,您应该在您的 app.py 中导入以下内容之一:

from app.aaa.module_a import method1
from .aaa.module_a import method1

然后确保按照以下方式之一调用您的应用程序:

app

(由于控制台入口点,这应该可以工作)

python -m app.app

(即使没有控制台入口点也应该可以工作)


我尝试在这里重新创建完整的项目

目录结构:

.
├── app
│   ├── aaa
│   │   └── module_a.py
│   ├── app.py
│   └── bbb
│       └── module_b.py
└── setup.py

setup.py

import setuptools

setuptools.setup(
    name="app",version="1.0",packages=['app','app.aaa','app.bbb'],entry_points={
        'console_scripts': ['app=app.app:main',]
    },)

app/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()

app/aaa/module_a.py

def method1():
    print("A1 executed")

app/bbb/module_b.py

def method2():
    print("B2 executed")

然后我运行以下命令:

$ python3 -V
Python 3.6.9
$ python3 -m venv .venv
$ .venv/bin/python -m pip install -U pip setuptools wheel
# [...]
$ .venv/bin/python -m pip list
Package       Version
------------- -------------------
pip           20.3.3
pkg-resources 0.0.0
setuptools    51.1.0.post20201221
wheel         0.36.2
$ .venv/bin/python -m pip install .
# [...]
$ .venv/bin/python -m app.app
APP main executed
A1 executed
B2 executed
$ .venv/bin/app
APP main executed
A1 executed
B2 executed
$ .venv/bin/python -m pip uninstall app
# [...]
$ .venv/bin/python -m pip install --editable .
# [...]
$ .venv/bin/python -m app.app
APP main executed
A1 executed
B2 executed
$ .venv/bin/app
APP main executed
A1 executed
B2 executed
,

sinoroc 的回答大部分是正确的 - 他执行了一个正确的例子,突出显示了所有选项。它显示了构建python包的正确方法。但是在任何运行之前,这个包应该首先安装到 venv 中。然后需要 pip install --editable 选项才能在 IDE 中继续开发/调试包(它在 venv 中安装包,但将源文件保留在原始位置)。

经过长时间的评论讨论后,我来到了页面 An Overview of Packaging for Python,该页面解释了所有选项,并强调了 Python 的本机打包主要是为分发可重用代码(称为库)而构建的。我认为这是我误解和最初问题的原因。

作为替代解决方案,我计划使用 PEP 441 -- Improving Python ZIP Application Support,这对于我来说似乎是一种正确的方法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。