pytocli

A Python lib to generate CLI commands


License
BSD-3-Clause
Install
pip install pytocli==0.6

Documentation

pytocli

A Python lib to generate CLI commands

Travis Status codecov Updates Python 3

You can install it through pip:

pip install pytocli

Problem

You need generate some CLI commands and start simple as

>>> def git():
...    return 'git'
>>> git()
'git'

But you realize it has many options:`

>>> def git(p=None, v=None):
...    cmd = 'git' 
...    if p:
...        cmd += ' -p'
...    if v is not None:
...        cmd += ' -v'
...    return cmd
>>> git()
'git'
>>> git(p=True)
'git -p'
>>> git(v=True)
'git -v'
>>> git(p=True, v=True)
'git -p -v'

So the spaghetti code begins.

Lib Goal

The lib goal is provide a fluent interface to generate cli commands:

>>> from tests.test_base import Git
>>> str(Git())
'git'
>>> str(Git().start('foo.git'))
'git -C foo.git'
>>> str(Git().verbose().start('bar.git'))
'git -v -C bar.git'
>>> str(Git().verbose().start('baz.git').commit.message('great!'))
'git -v -C baz.git commit -m great!'

How to extend the framework. Use Case: git like

Step 1: inherit from CommandBuilder class

The command name must be overwritten as class attribute.

>>> from pytocli import CommandBuilder


>>> class Git(CommandBuilder):
...    _name = 'git'

Step 2: create command options

>>> from pytocli import (CommandBuilder, Option, NoValueOption, 
...                     SingleValueOption, MultiValuesOption)
>>> class Git(CommandBuilder):
...     _name = 'git'
...     # Options
...     verbose = Option(NoValueOption, '-v', doc= 'Verbose mode')
...     start = Option(SingleValueOption, '-C')
...     multi = Option(MultiValuesOption, '--mu')
...
>>>

Step 3: create a sub commands following steps 1 and 2

The only difference is using _add_subcommand decorator to add them as Git Subcommands

>>> from pytocli import SubCommandBuilder
>>> # Create base git subcommand only to acces git through property with same
>>> # name instead of parent_cmd 
>>> class GitSubCommand(SubCommandBuilder):
...     @property
...     def git(self):
...         return self.parent_cmd
...
>>> #Create Sub commands
>>> @Git._add_subcommand
... class Commit(GitSubCommand):
...    _name = 'commit'
...    message = Option(SingleValueOption, '-m', 'Message to be added on commit')
...
>>>
>>> @Git._add_subcommand
... class Revert(GitSubCommand):
...    _name = 'revert'
...
>>>

After setup, parameters can me sent to parent command

Passing parameters to parent command can be done even after subcommand creation:

>>> commit = Git().commit
>>> str(commit)
'git commit'
>>> # Same result using Commit directly 
>>> commit = Commit()
>>> str(commit)
'git commit'
>>> commit.parent_cmd
CommandBuilder git: No doc provided
>>> commit.git
CommandBuilder git: No doc provided
>>> commit.git.verbose()
CommandBuilder git: No doc provided
>>> str(commit)
'git -v commit'

Exploring CLI from Python Command Line

>>> Git._options
['verbose', 'start', 'multi']
>>> Git.verbose
Option -v: Verbose mode
>>> Git._sub_commands
['commit', 'revert']
>>> Git.commit._options
['message']

#Release Notes

Version 0.1

  • first approach with methods and regular Object Orientation

Version 0.2

  • second approach with Metaclass and Descriptors

Version 0.3

  • Fixed NoOptionValue raises error when passing parameter
  • Fixed SubCommand docs when accessing as class attr (Ex: repr(Git.commit))

Version 0.4

  • Added equal_sign and separator parameters to Single and Multi Value Options
  • Fixed trove classifiers for pypi

Version 0.6

  • Changed class attributes to avoid naming clash
  • Created class decorator to Connect Commands and SubCommand