如何以正确的方式为独立的 Python 应用程序制作 setup.py? [英] How to make setup.py for standalone python application in a right way?

查看:39
本文介绍了如何以正确的方式为独立的 Python 应用程序制作 setup.py?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have read several similar topics but haven't succeeded yet. I feel I miss or misunderstand some fundamental thing and this is the reason of my failure.
I have an 'application' written in a python which I want to deploy with help of standard setup.py. Due to complex functionality it consists of different python modules. But there is no sense in separate release of this modules as they are too specific.
Expected result is to have package installed in a system with help of pip install and be available from OS command line with simple app command.
Simplifying long story to reproducible example - I have following directory structure:

<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 时它工作正常并给出预期的输出:

When I run app.py from console it works fine and gives expected output:

APP main executed
A1 executed
B2 executed

所以,这个简单的应用程序"运行良好,我想在以下帮助下分发它
setup.py

So, this simple 'application' works fine and I want to distribute it with help of following
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', ]
    }
)

同样,一切看起来都不错,测试安装看起来也不错:

Again, everything looks good and test installation looks good:

(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 命令执行我的应用程序.确实有效.但应用程序本身失败并显示错误消息:

And now comes the problem. With aforementioned entry_points from setup.py I expect to be able execute my application with ./app command. Indeed it works. But application itself fails with error message:

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.但是如果我这样做,那么我的应用程序在开发过程中会运行出错:

I understand the reason of the error - it is because pip install put directories aaa and bbb together with app.py in one directory app. I.e. from this point of view app.py should use import app.aaa instead of import aaa. But if I do so then my app during development runs with error:

ModuleNotFoundError: No module named 'app.aaa'; 'app' is not a package

这也是合乎逻辑的,因为当时没有可用的 app 包......(它正在开发中,未安装在系统中......)

that is also logical as there are no app package available at that time... (it is under development and isn't installed in the system...)

最后.问题是 - 为由多个自己的模块组成的独立 Python 应用程序创建目录结构和 setup.py 的正确方法是什么?

Finally. The question is - what is a correct way to create directory structure and setup.py for standalone python application that consist of several own modules?

UPD
我现在最有希望的结果(但在评论中讨论后证明是错误的)经过以下更改:

UPD
The most promising result (but proved to be wrong after discussion in coments) that I have now came after following changes:

  1. 将 app.py 从 /app 移动到 本身
  2. 我在 setup.py 中通过 py_modules=['app']
  3. 引用了它
  4. 我将导入从 import aaa.method1 更改为 import app.aaa.method1
  1. moved app.py from <root>/app into <root> itself
  2. I referenced it in setup.py by py_modules=['app']
  3. I changed imports from import aaa.method1 to import app.aaa.method1 etc.

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

This way package works both in my development environment and after installation.
But I got a problem with entry_points - I see no way how to configure entry point to use main() from app.py that is not a part of app package but is a separate module....
I.e. new structure is

<root>
  ├─ app
  |   ├─ aaa
  |   |   └── module_a.py
  |   ├─ bbb
  |   |   └── module_b.py
  |   └──__init__.py
  ├─ docs
  |   └── .....
  ├─ tests
  |   └── .....
  ├─ app.py
  └─ setup.py

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

I.e. the logic here - to have 2 separate entities:

  1. 一个空包 app(仅由 init.py 组成)和子包 aaabbb 等.
  2. 使用子包app.aaaapp.bbb
  3. 中的函数的脚本app.py
  1. An empty package app (consists of init.py only) with subpackages aaa, bbb etc.
  2. A script app.py that uses functions from subpackages app.aaa, app.bbb

但正如我所写的 - 我看不出如何定义 app.py 的入口点以允许它直接从操作系统命令行运行.

But as I wrote - I see no way how to define entry point for app.py to allow it's run from OS command line directly.

推荐答案

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

With that directory (package) structure, in your app.py you should import as one of the following:

from app.aaa.module_a import method1

from .aaa.module_a import method1

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

Then make sure to call you application like one of the following:

app

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

(this should work thanks to the console entry point)

python -m app.app

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

(this should work even without the console entry point)

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

目录结构:

.
├── 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

这篇关于如何以正确的方式为独立的 Python 应用程序制作 setup.py?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆