Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make setup.py for standalone python application in a right way?

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

Below is code of modules:
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")

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

APP main executed
A1 executed
B2 executed

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]$ 

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'

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

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...)

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
The most promising result (but proved to be wrong after discussion in coments) that I have now came after following changes:

  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.

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

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

  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

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.

like image 214
StarterKit Avatar asked Dec 22 '20 11:12

StarterKit


People also ask

What is setup () in Python?

The setup script is the centre of all activity in building, distributing, and installing modules using the Distutils. The main purpose of the setup script is to describe your module distribution to the Distutils, so that the various commands that operate on your modules do the right thing.

What should setup py contain?

The setup.py file may be the most significant file that should be placed at the root of the Python project directory. It primarily serves two purposes: It includes choices and metadata about the program, such as the package name, version, author, license, minimal dependencies, entry points, data files, and so on.

Is setup py outdated?

py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools. I found a very detailed write-up explaining this issue: "Why you shouldn't invoke setup.py directly" (October 2021).

What is setup Py in Python?

In simpler words, setup.py is a script contained within the packages or libraries to ensure their full and correct installation. The setup.py script is written and uses Python programming language. If the package that we extracted into a directory contains a setup.py file, we can directly install that package by running the following command.

How to create a standalone Windows installer for Python?

Create a standalone Windows installer for your Python application Step 1: customize your WinPython distribution Step 2: create the installer Step 3: create the shortcuts Step 4: customize the installation process Python code to install packages Conclusion

How do I set up a Python project?

on their machine. The key to setting up your project is the setup.py file. In this blog I'll go into the details of this file. Here I assume that you already have a package that you want to set up. setup.py long before your project is finished. It could even be an empty package; and contains a file named init.py (which may be empty).

How do I install a Python program on Windows?

Activate the virtual environment. Install pyinstaller a python package. change the current directory in terminal / cmd prompt to the directory where you saved your .py file. Open the dist folder created in the working directory. Run the cui_exe.exe file. Congratulations you have just created your first distributable Windows software.


1 Answers

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)


I try to recreate the complete project here

Directory structure:

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

Then I run following commands:

$ 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
like image 140
sinoroc Avatar answered Oct 16 '22 09:10

sinoroc