Wouldn’t you like to be able to wrap up all of your cool code in to a package, and then call your code from the command line using a user friendly command name? Sure you do!

Come on, let me show you how.

[email protected]:~$ banana
Look ma, I'm an entry point!
[email protected]:~$

The above demonstrates some python code from a package being called from the terminal command line using a user-friendly CLI command name.

I chose to name the command ‘banana‘ to make this process much clearer.

Please clone the ‘pkgexample‘ repository to play along with me.


https://github.com/RexBytes/pkgexample
0 forks.
0 stars.
0 open issues.

Recent commits:

[email protected]:~/myrepos/rex$ tree -a pkgexample/

pkgexample/
├── .gitignore
├── LICENCE
├── pyproject.toml
├── README.md
└── src
    └── pkgexample
        ├── helloworld.py
        └── __init__.py

2 directories, 6 files
[email protected]:~/myrepos/rex$

Create Your Python Console Commands

We are going to step through 3 different console command examples, building up to something you will find useful. We will start with a simple function call, then we will call a function that implements your class methods, and finally a full blown custom python module.

EXAMPLE 1: Simple Function Call

To enable console behavior of python packages we use something called ‘entry points’.

Step 1 – Add Your First Entry Point To __init__.py

Entry points are just python functions, with one important requirement. They are not able to accept arguments. If you want your python command line console to accept arguments you must use argparse within the body of your entry point function to parse user input given via sys.argv .

Entry point functions are added to __init__.py , open up the ‘./src/pkgexample/__init__.py‘ file and add the following.

def my_first_entry_point_funtion():
    print("Look ma, I'm an entry point!")

Step 2 – Add A Project Script Entry To pyproject.toml

The final step is to make a project script entry to your ‘pyproject.toml‘ file.
At the very end of your ‘pyproject.toml‘ make the following addition.

[project.scripts]
banana = "pkgexample:my_first_entry_point_funtion"

Detailed explanation of the entry point project script entry can be found here.
For our simple example, we are following this format.

{console script name} = {package name}:{entry point function name}

I have called the console script name ‘banana‘ to make this whole process clearer.

Step 3 – Build And Test Your Project

I know you are an expert at building and installing python packages, because you’ve read this guide. I am only reproducing the following commands for your convenience.

We will uninstall any version of ‘pkgexample’ we have, rebuild, and then re-install the package with the following commands run from your repository root directory.

[email protected]:~/pkgexample$ python3 -m pip uninstall pkgexample ; 
[email protected]:~/pkgexample$ rm -rf ./dist/ ; 
[email protected]:~/pkgexample$ rm -rf ./src/pkgexample.egg-info/ ;
[email protected]:~/pkgexample$ python3 -m build ;
[email protected]:~/pkgexample$ python3 -m pip install ./dist/pkgexample-1.0.1-py3-none-any.whl ;

If you now run the command ‘banana‘ your entry point function will be triggered and it will run it’s code.

[email protected]:~/pkgexample$ banana
Look ma, I'm an entry point!
[email protected]:~/pkgexample$

EXAMPLE 2: Class Method Calls

You may have noticed that the entry point we created above doesn’t use any of the classes available in the ‘pkgexample’ package. Since the whole point of console scripts is to run your code in a more convenient way – I should show you how to do this.

Step 1 – Add Your Second Entry Point To __init__.py

We are going to add a second entry point function that uses the class ‘HelloWorld‘ from ‘pkgexample‘.

Open up your ‘./src/pkgexample/__init__.py‘ file and add apply the following changes.

At the top of the file we first import our class ‘HelloWorld‘ from the python module ‘helloworld‘, from the same local directory. Please note the period before the module name (I’ve seen people forget this, and spend hours debugging).

from .helloworld import HelloWorld 

def my_first_entry_point_funtion():
    print("Look ma, I'm an entry point!")

def my_second_entry_point_funtion():
    helloworld=HelloWorld()
    helloworld.print_message()

We then add our second entry point method which calls the ‘print_message()‘ method from an instance of the class ‘HelloWorld’.

Step 2 – Add Another Project Script Entry To pyproject.toml

You can make as many project script entries to your ‘pyproject.toml‘ file as you want.

Here I name my second console script as ‘apple‘.

[project.scripts]
banana = "pkgexample:my_first_entry_point_funtion"
apple  = "pkgexample:my_second_entry_point_funtion"

Step 3 – Build And Test Your Project

Once you rebuild, and reinstall the package you should be apply to run the command ‘apple’, and also the command ‘banana’.

[email protected]:~/pkgexample$ apple
Hello World
[email protected]:~/pkgexample$ banana
Look ma, I'm an entry point!

EXAMPLE 3: Custom Python Module Calls

One last example. You can also have entry points inside their own script files.

Step 1 – Create A New Python Module

Let’s start by creating a new python module called ‘myscript.py’ in the package directory under the src directory.

[email protected]:~/myrepos/rex/$ tree -a pkgexample/
pkgexample/
├── .gitignore
├── LICENCE
├── pyproject.toml
├── README.md
└── src
    └── pkgexample
        ├── helloworld.py
        ├── __init__.py
        └── myscript.py       <--- New File

2 directories, 7 files
[email protected]:~/myrepos/rex$

Step 2 – Write Your Entry Point Function

We’re going to stick to the script and write the same entry point function we did earlier. Please add the following code to the ‘myscript.py‘ file.

from .helloworld import HelloWorld 

def main():
    helloworld=HelloWorld()
    helloworld.print_message()

Step 3 – Add Another Project Script Entry To pyproject.toml

I have decided to call this console command ‘carrot‘. The [project.scripts] table at the end of your TOML file should look like this.


[project.scripts]
banana = "pkgexample:my_first_entry_point_funtion"
apple = "pkgexample:my_second_entry_point_funtion"
carrot = "pkgexample:myscript.main"

Please note the slightly different syntax for a script entry for ‘carrot’.
We have to name the script the entry function is contained in.

{console script name} = {package name}:{scriptfilename}.{entry point function name}

Step 4 – Repackage, Reinstall, And Run Your New Commands.

Once you repackage and reinstall you should be able to run the following three terminal commands, including the new command ‘carrot‘.

[email protected]:~$ banana
Look ma, I'm an entry point!
[email protected]:~$ apple
Hello World
[email protected]:~$ carrot
Hello World
[email protected]:~$

DOWNLOAD: Code Changes Available As Package

For convenience I have made all of the above changes publicly available as a new package called ‘pkgexampleconsole‘.

GitHub Repository

You can clone a copy from the following location.


https://github.com/RexBytes/pkgexampleconsole
0 forks.
0 stars.
0 open issues.

Recent commits:

[email protected]:~/myrepos/rex$ tree -a pkgexampleconsole/
pkgexampleconsole/
├── .gitignore
├── LICENCE
├── pyproject.toml
├── README.md
└── src
    └── pkgexampleconsole
        ├── helloworld.py
        ├── __init__.py
        └── myscript.py

2 directories, 7 files
[email protected]:~/myrepos/rex$

Live Package At pypi.org

If you prefer, you can immediately install and play with the ‘banana’, ‘apple’, and ‘carrot’ commands.

https://pypi.org/project/pkgexampleconsole/
[email protected]:~$ python3 -m pip install pkgexampleconsole
Defaulting to user installation because normal site-packages is not writeable
Collecting pkgexampleconsole
  Downloading pkgexampleconsole-1.0.2-py3-none-any.whl (3.6 kB)
Installing collected packages: pkgexampleconsole
Successfully installed pkgexampleconsole-1.0.2

You should be able to immediately run the python console commands as follows…


[email protected]:~$ banana
Look ma, I'm an entry point!
[email protected]:~$ apple
Hello World
[email protected]:~$ carrot
Hello World
[email protected]:~$

WARNING! A Common Gotcha. Pay attention.

You must have your local user binary directory on your user path. From a terminal, run the following command.

export PATH=/home/ubuntu/.local/bin/:$PATH

If you do not, you will find that the following error will be given when trying to run your python console commands.

WARNING: The scripts apple, banana and carrot are installed in '/home/ubuntu/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.

Here is a full run through demonstrating what happens if your path isn’t set, and how after correcting your path the problem immediately resolved.

[email protected]:~$ python3 -m pip install pkgexampleconsole
Defaulting to user installation because normal site-packages is not writeable
Collecting pkgexampleconsole
  Using cached pkgexampleconsole-1.0.2-py3-none-any.whl (3.6 kB)
Installing collected packages: pkgexampleconsole
  WARNING: The scripts apple, banana and carrot are installed in '/home/ubuntu/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed pkgexampleconsole-1.0.2
[email protected]:~$ banana
bash: banana: command not found
[email protected]:~$ export PATH=/home/ubuntu/.local/bin/:$PATH
[email protected]:~$ banana
Look ma, I'm an entry point!
One thought on “Python Packages As Callable Console Commands”
%d bloggers like this: