Scaling Your Python CLI

How to maker your programs modular

As our programs grow we often need to go back to the drawing board

Our CLI

The test cli we will be building will contain three actions:

  • create
  • delete
  • get
  • file
  • directory

V1 Project Layout

To build our first cli version we followed a tutorial and put everything in one file. Not the best practice… but it worked! Our project looks like this.

├── main.py
├── setup.py
Our general click hierarch

V1.5 Trying To be Modular

We refactor our code and add a file.py and directory.py . We copy and paste our functions with decorators into them, but now run into a challenge, how do we structure the imports?

V2 Modular Project Layout

To help create a more scalable approach we’re going to add python modules to our directory. Our project layout will mirror our categories (nouns), with a top level folder containing the cli entry point, two modules and a setup file.

├── main.py
├── setup.py
└── src
├── directories
│ └── directory.py
└── files
└── file.py

Structuring Your Commands

Depending on which CLIs you have worked with, the natural hierarchy for your cli might be cli create file or cli file create . The first one is called a verb-noun hierarchy with clis like Powershell. These structures are great when commands have similar actions that can be taken and mirrors english more closely.

Noun First — Cli File Create

We will start off with Noun first, because it makes our setup easier. Within our main.py under the Noun First folder, you will notice how we structure our project, we import each group from our sub directories and add them as a command.

Noun First Project
cli file
Usage: cli file [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
create
delete
get

Verb First — Cli Create File

Our second approach is to have our cli verb first, such as cli create file .

def merge_commands(group, command_collection:CommandCollection):    """ Set the group's commands to those in the collection"""    new_commands = {}
command_sources = command_collection.sources
for group in command_sources:
for command_name,command in group.commands.items():
new_commands[command_name] = command

group.commands = new_commands
return group
cli create
Usage: cli create [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
directory Create directory
file Create file

Conclusion

And there we have it. In the tutorial we learned how to scale our python Click cli by breaking down our groups into modules and supporting two types of cli structures. As you continue to scale you will run into more interesting challenges, and I’d be interested to hear about them along the way

--

--

ML Architect @ Voiceflow

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store