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!
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
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!')
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
''',
)
Let's examine what each line of code does.
from setuptools import setup
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'
This'll be the name of our CLI app.
py_modules=['helloworld']
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']
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
'''
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.
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 .
Then execute the created command:
(venv) $ hello
Hello World!
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.
Top comments (3)
I strongly recommend checking out poetry. You define your dependencies, metadata, and scripts in one file and then one tool manages your virtualenv, deps, building, publishing, etc. Plus you get deterministic dependencies with a lock file (for managing which versions of Click are compatible for example).
Also I wrote s post about it comparing it to setuptools/pipenv:
Productive Python with Poetry
Dylan Anthony ・ May 18 ・ 3 min read
Thanks! I didn't know about poetry. I'll definitely check it out.
Awesome!