Using setuptools to package your Python app

Kelvin Wangonya - Aug 5 '19 - - Dev Community

In our previous post, we made a Hello World! app to get us started with Click. You might have noticed though that we needed to do python hello-world.py to run it. Since we're building CLI apps, that's just not cool 😅 and it's not how CLI apps work anyway. We need to be able to run a single command, something like:

(venv) $ hello
Hello World!
Enter fullscreen mode Exit fullscreen mode

That's where setuptools comes in. Setuptools helps us bundle our script so we can install it and run it like an app ($ hello), instead of like a script ($ python hello-world.py).

Setuptools is a collection of enhancements to the Python distutils that allow developers to more easily build and distribute Python packages, especially ones that have dependencies on other packages. - Docs

Ok, enough chit-chat. Let's get this working. The first thing we'll need to do is add a new file to our project and call it setup.py. We'll also rename hello-world.py to helloworld.py (remove the dash). I'll explain why this is necessary when we get to the setup() section. Our project structure should now look like this:

hello-world-cli
    |- helloworld.py  # renamed from hello-world.py
    |- setup.py  # new file
Enter fullscreen mode Exit fullscreen mode

In the helloworld.py, remove the if __name__ == '__main__': section. The code should now look like this:

import click

@click.command()
def hello():
    click.echo('Hello World!')
Enter fullscreen mode Exit fullscreen mode

In the setup.py file, add this code:

from setuptools import setup

setup(
    name='hello-world-cli',
    py_modules=['helloworld'],
    install_requires=[
        'Click',
    ],
    entry_points='''
        [console_scripts]
        hello=helloworld:hello
    ''',
)
Enter fullscreen mode Exit fullscreen mode

Let's examine what each line of code does.

from setuptools import setup
Enter fullscreen mode Exit fullscreen mode

We need to import setup from setuptools in order to use it.

Next, we call the setup() function and pass in a number of parameters:

name='hello-world-cli'
Enter fullscreen mode Exit fullscreen mode

This'll be the name of our CLI app.

py_modules=['helloworld']
Enter fullscreen mode Exit fullscreen mode

This tells the setup() where to find our main module to execute. Our main file here is helloworld.py so we set helloworld as the module. We can keep it that simple since we only have one file for now, but as our app grows we'll replace py_modules with packages=find_packages(). More on that later.

install_requires=['Click']
Enter fullscreen mode Exit fullscreen mode

Our app has Click as a dependancy, so it needs to be installed for it to work. setup() needs to know about this so it can include it as it packages our project. install_requires accepts a list of values, so if we had more dependencies, we would list all of them here.

entry_points='''
          [console_scripts]
          hello=helloworld:hello
      '''
Enter fullscreen mode Exit fullscreen mode

This is the part that saves us the hassle of having to run the app with python ... everytime, and enables us to just execute hello to run it.

entry_points-image

It's important to note that if we maintained our previous name (hello-world.py), the setup would have failed at entry_points. This is because python module names shouldn't have dashes.

We can now install our app in our environment and run it. To install it, run:

(venv) $ pip install --editable .
Enter fullscreen mode Exit fullscreen mode

Then execute the created command:

(venv) $ hello
Hello World!
Enter fullscreen mode Exit fullscreen mode

Pretty neat, right? 😃

In the next post, we'll take a closer look at Click Commands, Options & Arguments, and how we can use them to make our app even better.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .