Captain#

Examples#

Single-command CLI#

example.py file:

#! /usr/bin/env python3

from libjam import captain

# Creating the CLI
@captain()
def shout(text: str, *, world=False):
  """Shouts the given text back."""
  if world:
    text += ' world'
  print(text + '!')
  return 'anything'

# Adding an option to the CLI
shout.add_option(
  'world', "Adds ' world' before the exclamation mark.", 'w',
)

# Running the CLI
returned = shout()

# This assertion will succeed
assert returned == 'anything'

Here is what the user will see when running this CLI:

$ ./example.py
shout: missing argument <TEXT>
Try 'shout --help' for more information.

$ ./example.py Hello
Hello!

$ ./example.py Hello --world
Hello world!

$ ./example.py --help
Usage:
  shout [OPTION]... <TEXT>
Description:
  Shouts the given text back.
Options:
  -w, --world - Adds ' world' before the exclamation mark.
  -h, --help  - Prints this page.

Multi-command CLI#

example.py file:

#! /usr/bin/env python3

# Imports
from libjam import captain
import sys


# Creating the CLI
@captain('very-smart-ai')
class cli:
  "Trust me, it's the smartest one out there."

  def shout(text, suffix=None):
    """I will be loud!"""
    text += '!'
    if suffix:
      text += suffix
    print(text)

  def whisper(*lines):
    """Shhhh! You don't want them to hear you..."""
    lines = [l + '...' for l in lines]
    text = '\n'.join(lines)
    print(text)

  def wonder(**opts):
    """Where's my copy of My Weekend in Stevenage by Filthy Henderson?"""
    if opts['mcbeth']:
      print("I just want to be a fish.")
    elif opts['quiet']:
      print('I REFUSE!')
    else:
      print('Thanks for staying quiet.')


# Adding options to the wonder command
cli.wonder_command.add_option(
  'mcbeth', 'Ponder whether to be or not to be.',
)
cli.wonder_command.add_option('quiet', 'Be quiet.', 'q')

# Running the CLI
if __name__ == '__main__':
  sys.exit(cli())

Here is what the user will see when running this CLI:

$ ./example.py
very-smart-ai: no command specified.
Try 'very-smart-ai --help' for more information.

$ ./example.py shout "I like crisps"
I like crisps!

$ ./example.py wonder -h
Usage:
   very-smart-ai wonder
Description:
   Where's my copy of My weekend in Stevenage by Filthy Henderson?
Options:
      --mcbeth - Ponder whether to be or not to be.
   -q --quiet   - Be quiet.
   -h --help    - Prints this page.

$ ./example.py wonder --mcbeth
I just want to be a fish.

$ ./example.py --help
Trust me, it's the smartest one out there.

Synopsis:
   very-smart-ai <COMMAND> ...

Commands:
   shout   - I will be loud!
   whisper - Shhhh! You don't want them to hear you...
   wonder  - Where's my copy of My weekend in Stevenage by Filthy Henderson?

Usage:
   shout <TEXT> [SUFFIX]
   whisper [LINES]...
   wonder

Options:
   -h --help - Prints this page.

API#

class libjam.Captain(ship: callable, name: str = None, pass_self: bool = False, add_help: bool = True, compact_help: bool = None, child_kwargs: dict = {})#

Creates a CLI around a given function or class.

The ship can be either a function, to make a single-command CLI, or a class, to make a multi-command CLI. Its docstring will be used as the CLI’s description, shown on the help page.

If the ship is a function, then its parameters will be used to create the CLI’s arguments. Supports required, optional and arbitrary parameters.

If the ship is a class, then its attributes will be used to create the CLI’s commands. The ship’s attributes can be either other Captains, functions or classes. If the attribute is a function or a class, then a new Captain will be created using that attribute; the name of this new captain will be set to the parent Captain’s name plus the name of the attribute, as it appears within the class, with a space in the middle. For example, if the parent Captain’s name is “hello” and the function’s name, as it appears in the class, is “world”, then the name for the new Captain created from that function will be “hello world”. However, if the attribute is already a Captain then it’s name will remain unchanged. All new Captains created by this Captain, will be added to this Captain, under the name of the attribute, as it appeared in the class, plus “_command”. This is done to improve ease of access, for example to enabled adding options to these created Captains. This is not done for attributes which were already Captains.

If the name is not specified, then sys.argv[0] will be used to determine the name of the program.

The compact_help parameter decides whether the help page sections should be separated by one or two newlines. If not specified, then it will be set to False if the specified ship is a function, and True if it’s a class.

The child_kwargs dictionary is passed as keyword arguments to children Captains created by this Captain (only applies if the given ship is a class).

The pass_self parameter can be set to True if you want your function to get the Captain that was created using it as its first argument when running.

To run the CLI, call it.

Example single-command CLI:

from libjam import Captain
import sys

def echo(text, **options):
  if options['world']:
    text += ' world!'
  print(echo)

cli = Captain(echo)
cli.add_option('world', 'Appends " world!"', 'w')

if __name__ == '__main__':
  sys.exit(cli())

Example multi-command CLI:

from libjam import Captain
import sys

class express:
  def sadness(**opts):
    print("I'm sad")

  def happiness(*, help=False):
    print("I'm happy")

cli = Captain(express)

if __name__ == '__main__':
  sys.exit(cli())
add_option(name: str, description: str = None, shorthand: str = None, call: callable = None)#

Adds an option to the CLI.

The name parameter will used as the key in the dict that the parse method returns and to create the long flag for the CLI.

The description parameter, if specified, will be shown in the help page, after the option’s flags.

The shorthand parameter, if specified, will be used to create the short flag for the CLI. It must be 1 a character-long string.

The call parameter, if specified, will be called during parsing. If the option was set by the user. The default help option sets it to the print_help_and_exit method of the Captain.

When running the CLI, the option will be passed as a keyword argument to the appropriate function, if it can accept it.

error(exit_code: int, *lines: str)#

Prints an error message to stderr and calls sys.exit with the given exit code.

usage_error(*lines: str)#

Prints an error message to stderr and calls sys.exit with an appropriate exit code.

build_help() str#

Builds the help page.

print_help()#

Prints the help page.

print_help_and_exit()#

Prints the help page and calls sys.exit with the appropriate exit code.

libjam.captain(name: str = None, pass_self: bool = False, add_help: bool = True, compact_help: bool = None, child_kwargs: dict = {}) callable#

Returns a decorator that takes either a function or a class and returns a Captain.

Example usage:

@captain(name='echo')
def cli(text):
  print(text)