action-hero

Make powerful CLIs with argparse actions that pack a punch!


Keywords
argparse, cli, command-line-interface, python
License
MIT
Install
pip install action-hero==0.7.0

Documentation

Action Hero Logo

PyPI - Python Version Code style: black codecov Build Status PEP8 PyPI - License

action_hero is a python package that helps you
manage user arguments in command line applications using argparse

Introduction · Quick Usage · Help & FAQ · Catalog · Development

Introduction

argparse
argparse is a python standard library module used to make command line applications. argparse provides ArgumentParser that parses user arguments and runs argparse.Actions on them. ⚓︎
action_hero 🔥
action_hero makes argparse more capable by providing a large number of custom actions. For example, the FileIsWritableAction automatically verifies that file path(s) accepted as arguments are writable, informing the user if they aren't. This saves you the trouble of doing that check yourself. Nice, no? Browse the catalog for many such actions.

Quick Usage

1. Installation:

pip install action_hero

2. Quick Usage: Import an action and specify it when adding an argument to your parser.

from action_hero import FileIsReadableAction
...
parser.add_argument("--file", action=FileIsReadableAction)
...

3. Full Example: CLI program that counts number of lines of a file.

# examples/line_counter.py
import argparse

from action_hero import FileIsReadableAction


if __name__ == "__main__":

    # Create parser
    parser = argparse.ArgumentParser()

    # Add user argument "--file" and assert that it will be readable
    parser.add_argument("--file", action=FileIsReadableAction)

    # Parse user arguments
    args = parser.parse_args()

    if args.file:
        # Print number of lines in file
        with open(args.file) as f:
            print("{} has {} lines".format(args.file, len(f.readlines())))
    else:
        # Print usage if no arguments were given
        parser.print_usage()

Run line_counter.py on the command line

$ ls
line_counter.py mary.md

$ python line_counter.py --file mary.md
mary.md has 39 lines

$ python line_counter.py
usage: line_counter.py [-h] [--file FILE]

$ python line_counter.py --file nofile.md
usage: line_counter.py [-h] [--file FILE]
line_counter.py: error: argument --file: File is not readable

Note: Supported Python Versions >= 3.5

Help and FAQ

Accepting action_values

There are times your action requires an additional value. For instance, when your argument accepts only filenames with md or markdown extensions. You can use the FileHasExtensionAction action for this and pass in the extensions to check for via action_values, like so —

parser.add_argument(
    "--file", 
    action=FileHasExtensionAction,
    action_values=["md", "markdown"]
)

Unless otherwise mentioned, action_values should be provided as a non-empty list of strings. e.g. action_values = ["md", "markdown"].

Pipelining multiple actions

The PipelineAction allows you to run multiple actions as a pipeline. Pass in your pipeline of actions as a list to action_values. If one of the actions you're passing in has it's own action_values, put that one as a tuple, like such: (FileHasExtensionAction, ["md", "markdown"]). Here's an example of pipelining actions for --file

  1. File has extensions md or markdown
  2. File exists
parser.add_argument(
    "--file", 
    action=PipelineAction, 
    action_values=[
        (FileHasExtensionAction, ["md", "markdown"]),
        FileExistsAction
    ]
)

Another helpful feature, this action provides is the order of error reporting. In the above example, if the supplied argument file did not have the markdown extensions, the error message would reflect that and exit. After the user redoes the entry with a valid filename the next action in the pipeline applies FileExistsAction which checks for existence. If the file does not exist, an error message about file not existing will be shown and exits allowing the user to try again.

This behavior can save you a lot of manual condition checks later on. For example, here's how to check for an existing, writable, non-empty, markdown file —

parser.add_argument(
    "--file", 
    action=PipelineAction, 
    action_values=[
        FileExistsAction, 
        FileIsWritableAction,
        FileIsNotEmptyAction,
        (FileHasExtensionAction, ["md", "markdown"])
]

Exceptions in this module

You'll come across two different exceptions in action_hero.

  1. ValueError: These are intended for you, the CLI developer. You'd want to fix any underlying issue that causes them before releasing your CLI. e.g. when action_values is an empty list.

  2. argparse.ArgumentError: These are intended for your CLI's users, so they might use the messages as hints to provide corrent command line options.

Not capturing user argument exceptions

argparse.ArgumentParser has a slightly unconventional approach to handling argparse.ArgumentErrors. Upon encountering one, it prints argument usage information, error and exits. I mention this, so you don't setup a try/except around parser.parse_args() to capture that exception.

In order to maintain consistency with the rest of your argparse code, exceptions in action_hero are also of type argparse.ArgumentError and causes system exit as well. More information can be found in PEP 389. Since this is expected behavior, I recommend you allow this exception and let it display usage information and exit.

Arguments accepting multiple values

Just like any other argparse.Action each action_hero.Action handles multiple values and provides relevant error messages.

FAQ

What do I need to know to use action_hero in my command line application?
Vanilla argparse knowledge should do it.
Where can I find information about argparse?
argparse is part of the Python standard library.
Is action_hero tied to the argparse module?
Yes (but technically no — any project that can use an argpoarse.Action should work as long as it handles the argparse.ArgumentError exception)
What type are the user argument exceptions?
argparse.ArgumentError{"helpful error message"}, just like any other argparse.Action
Why re-implement actions already provided by argparse like the choices action?
In order to include them in PipelineAction.
There was no mention of humans! Does this work for humans?
Yes, it works for humans :)

Catalog

  1. Special actions:
Action Description action_values
PipelineAction Run multiple actions as a pipeline Actions to run as a pipeline. e.g. [FileExistsAction, FileIsWritableAction]. (Read more)
DebugAction Print debug information. There can be multiple of these in a pipeline
  1. Path, Directory and File related actions:
Action Description action_values
DirectoryDoesNotExistAction Check if directory does not exist
DirectoryExistsAction Check if directory exists
DirectoryIsExecutableAction Check if directory is executable
DirectoryIsNotExecutableAction Check if directory is not executable
DirectoryIsNotReadableAction Check if directory is not readable
DirectoryIsNotWritableAction Check if directory is not writable
DirectoryIsReadableAction Check if directory is readable
DirectoryIsValidAction Check directory is valid
DirectoryIsWritableAction Check if directory is writable
EnsureDirectoryAction* Ensure directory exists and create it if it doesnt
EnsureFileAction* Ensure file exists and create it if it doesnt
FileDoesNotExistAction Check if file doesnt exist
FileExistsAction Check if file exists
FileIsEmptyAction Check if file is empty
FileIsExecutableAction Check if file is executable
FileIsNotEmptyAction Check if file is not empty
FileIsNotExecutableAction Check if file is not executable
FileIsNotReadableAction Check if file is not readable
FileIsNotWritableAction Check if file is not writable
FileIsReadableAction Check if file is readable
FileIsValidAction Check file is valid
FileIsWritableAction Check if file is writable
FileHasExtensionAction Check if file has specified extension Extensions to check against. e.g. ["md", "markdown"]
PathDoesNotExistsAction Check if path does not exist
PathExistsAction Check if path exists
PathIsExecutableAction Check if path is executable
PathIsNotExecutableAction Check if path is not executable
PathIsNotReadableAction Check if path is not writable
PathIsNotWritableAction Check if path is not writable
PathIsReadableAction Check if path is readable
PathIsValidAction Check if path is valid
PathIsWritableAction Check if path is writable
ResolvePathAction Resolves path to canonical path removing symbolic links if present
  1. Net & Email related actions:
Action Description action_values
IPIsValidIPAddressAction Check if ip is valid ipv4 or ipv6 address
IPIsValidIPv4AddressAction Check if ip address is valid ipv4 address
IPIsValidIPv6AddressAction Check if ip address is valid ipv6 address
URLIsNotReachableAction Check if URL is not reachable
URLIsReachableAction Check if URL is reachable
URLWithHTTPResponseStatusCodeAction Check if upplied URL responds with expected HTTP response status code Status codes to check against. e.g. ["200", "201", "202", "204"]
EmailIsValidAction Checks if email address is valid
  1. Type related actions:
Action Description action_values
IsConvertibleToFloatAction Check if value is convertible to float
IsConvertibleToIntAction Check if value is convertible to int
IsConvertibleToUUIDAction Checks if value is convertible to UUID
IsFalsyAction Checks if value is falsy
IsTruthyAction Checks if value is truthy
  1. Range related actions:
Action Description action_values
  1. Miscellaneous actions:
Action Description action_values
ChoicesAction Argument can only have values from provided choice(s) Choices e.g. ["red", "blue", "green"]
NotifyAndContinueAction Print provided notification message(s) Message(s) e.g. ["This command will be deprecated in the next version."]
NotifyAndExitAction Print provided notification message(s) and Exit Message(s) e.g. ["This command has been deprecated", "Try --new-command"]
ConfirmAction Print provided message and proceed with user confirmation yes or no. Message(s) e.g. ["Proceed to Installation? (Y/n)"]
GetInputAction Get user input and save to self.dest Message(s) e.g. ["Favorite color"]
GetSecretInputAction Get user input without displaying characters and save to the self.dest Message(s) e.g. ["Enter your Password"]
LoadJSONFromFileAction Return loaded JSON file(s)
LoadYAMLFromFileAction Return loaded YAML file(s)
LoadPickleFromFileAction Return unpickled file(s)
CollectIntoDictAction Collect values into a dict Delimiter(s) to split value(s) into key:value pair(s) e.g. [":", "="] (If multiple delimiters exist inside a value, only the first match is used)
CollectIntoListAction Collect values into a list
CollectIntoTupleAction Collect values into a tuple

* Actions that can make changes to disk
Actions that can make changes to self.dest

Development

Notes

  • Formatting-: PEP8 only. Please format with black using black_linelength=79
  • License: The MIT License
  • Image Attributions: Karate by Alex Auda Samora from the Noun Project

Thank you for using action_hero@kadimisetty ⭐️