Module bussilab.cli
Tools to implement the command line interface
This module is used internally to implement the command line interface. In addition, it can be used to run the commands that are available in the command line interface without the need to leave python:
from bussilab.cli import cli
cli("-h")
cli("wham -b bias") # provide the command line as a string
cli(["wham", "-b", "bias"]) # alternatively use a list
The documentation of all the commands can be found in the cli_documentation submodule.
Notice however that these commands typically have alternative python
implementations that allow you to work directly on data structures
and are thus more flexible.
For instance, wham()(bias)
, where bias
is a numpy array,
is often more convenient than bussilab.cli.cli("wham -b bias")
,
where bias
is a file.
Functions
def arg(*name, **kwargs)
-
Expand source code
def arg(*name, **kwargs): """ Decorator that adds an argument to a command line tool. Parameters are passed to the `parser.add_argument()` function. It should be written **after** the `bussilab.cli.command` decorator. """ def call(p): p[-1].add_argument(*name, **kwargs) return p def add_argument(func): func = _Argument(func) func.calls.append(call) return func return add_argument
Decorator that adds an argument to a command line tool.
Parameters are passed to the
parser.add_argument()
function. It should be written after thecommand()
decorator. def cli(arguments: str | List[str] = '',
*,
prog: str | None = '',
use_argcomplete: bool = False,
throws_on_parser_errors: bool = True) ‑> int | None-
Expand source code
def cli(arguments: Union[str, List[str]] = "", *, prog: Optional[str] = "", # None is used to imply sys.argv[0] use_argcomplete: bool = False, throws_on_parser_errors: bool = True ) -> Optional[int]: """Executes a command line tool from python. This is the main function of this module and allows to launch all the subcommands available in the command line interface directly from python. Parameters ---------- arguments : str or list Command line arguments. If a string is passed, it is first split using shlex.split() prog : str Name of the calling program. It is used to build help texts. **Mostly for internal use**. use_argcomplete : bool If True, the `autocomplete` function of `argcomplete` module is called on the parser, so as to allow autocompletion in the command line tool. If `argcomplete` module is not installed, nothing is done and no failure is reported. **Mostly for internal use**. throws_on_parser_errors : bool If True, in case of command line error it throws a TypeError exception. **Mostly for internal use**. Returns ------- None or int If an error happens while parsing, it throws a TypeError exception, unless throws_on_parser_errors is set to false, in which ase it returns the corresponding error code. If an error happens while executing the requested command, an exception is thrown. If everything goes well, it returns None. """ # allow passing a single string if isinstance(arguments, str): arguments = shlex.split(arguments) func = None if prog == "": eparser = _eparser else: eparser = _create_eparser(prog) if use_argcomplete: # optional feature: try: import argcomplete argcomplete.autocomplete(eparser.parser) except ImportError: pass # Parse options # In order to avoid python to crash when cli() is called from python # with wrong arguments, it is necessary to intercept the exception. try: args = vars(eparser.parser.parse_args(arguments)) except SystemExit as e: if e.code != 0: if throws_on_parser_errors: raise TypeError("Error parsing command line arguments," + " return code is " + str(e.code)) else: return e.code return None if '_func' in args: func = args["_func"] del args["_func"] main_args = {} for i in args.keys(): if i[0] == "_": main_args[i[1:]] = args[i] for i in main_args: del args["_"+i] if "version" in main_args and main_args["version"]: from . import __version__ print(__version__) return None remove = [] for i in args.keys(): if args[i] is None: remove.append(i) for i in remove: del args[i] if func: func(**args) else: eparser.parser.print_usage() return None
Executes a command line tool from python.
This is the main function of this module and allows to launch all the subcommands available in the command line interface directly from python.
Parameters
arguments
:str
orlist
- Command line arguments. If a string is passed, it is first split using shlex.split()
prog
:str
- Name of the calling program. It is used to build help texts. Mostly for internal use.
use_argcomplete
:bool
- If True, the
autocomplete
function ofargcomplete
module is called on the parser, so as to allow autocompletion in the command line tool. Ifargcomplete
module is not installed, nothing is done and no failure is reported. Mostly for internal use. throws_on_parser_errors
:bool
- If True, in case of command line error it throws a TypeError exception. Mostly for internal use.
Returns
None
orint
- If an error happens while parsing, it throws a TypeError exception, unless throws_on_parser_errors is set to false, in which ase it returns the corresponding error code. If an error happens while executing the requested command, an exception is thrown. If everything goes well, it returns None.
def command(name: str, help: str | None = None, description: str | None = None, **kwargs)
-
Expand source code
def command(name: str, help: Optional[str] = None, description: Optional[str] = None, **kwargs): """Decorator that registers a function as a subcommand. This decorator should be written **before** the other decorators `bussilab.cli.arg`, `bussilab.cli.group`, and `bussilab.cli.endgroup`. Parameters ---------- name : str Name of the subcommand (will be used on the command line) help : str Short help message for the subcommand (one line). description : str, optional Longer description. If not provided, it is set to a copy of `help`. kwargs Other parameters are passed as is to the `add_parser` function of `argparse`. Examples -------- Simple command line tool that accepts a single `--out` argument followed by a string and call the function `do_something` with that string as an argument. ```python from bussilab.cli import command, arg @command("subcommand") @arg("--out") def myfunc(out): do_something(out) ``` """ if description is None: description = help def add_command(func): func = _Argument(func) _commands.append((name, help, description, func, kwargs)) return func return add_command
Decorator that registers a function as a subcommand.
This decorator should be written before the other decorators
arg()
,group()
, andendgroup()
.Parameters
name
:str
- Name of the subcommand (will be used on the command line)
help
:str
- Short help message for the subcommand (one line).
description
:str
, optional- Longer description. If not provided, it is set to a copy of
help
. kwargs
- Other parameters are passed as is to the
add_parser
function ofargparse
.
Examples
Simple command line tool that accepts a single
--out
argument followed by a string and call the functiondo_something
with that string as an argument.from bussilab.cli import command, arg @command("subcommand") @arg("--out") def myfunc(out): do_something(out)
def endgroup(f: Callable | None = None)
-
Expand source code
def endgroup(f: Optional[Callable] = None): """ Decorator that ends a group of arguments for a command line tool. See `bussilab.cli.group`. """ def call(p): if len(p) < 2: msg = "Non matching group/endgroup" raise TypeError(msg) return p[:-1] def end_group(func): func = _Argument(func) func.calls.append(call) return func if f: return end_group(f) return end_group
Decorator that ends a group of arguments for a command line tool.
See
group()
. def group(title: str | Callable | None = None,
description: str | None = None,
exclusive: bool | None = None,
required: bool | None = None)-
Expand source code
def group(title: Union[str, Callable, None] = None, description: Optional[str] = None, exclusive: Optional[bool] = None, required: Optional[bool] = None): """ Decorator that adds a group of arguments for a command line tool. It should be written **after** the `bussilab.cli.command` decorator. It should be followed by a number of `bussilab.cli.arg` decorators and by a closing `bussilab.cli.endgroup` decorator. Parameters ---------- title : str The name of the group. Can only be used for non exclusive groups. description : str A description of the group. Can only be used for non exclusive groups. exclusive : bool If True, the arguments belonging to this group are mutually exclusive required : bool If True, one of the arguments at least should be passed. Can only be used for exclusive groups. Examples -------- This is a simple command line tool that accepts three arguments (`-a`, `-b`, or `-c`), mutually exclusive. When ran, it will just print booleans showing if these arguments were passed. ```python from bussilab.cli import command, group, arg, endgroup @command("doit") @group(exclusive=True) @arg("-a", action='store_true') @arg("-b", action='store_true') @arg("-c", action='store_true') @endgroup def check(a, b, c): print(a, b, c) ``` """ noarg = None # note: _Argument is callable as well if description is None and exclusive is None and required is None and callable(title): noarg = title title = None if exclusive is None: exclusive = False if not exclusive and required is not None: msg = "required can only be used for exclusive groups" raise TypeError(msg) if exclusive and title is not None: msg = "title can only be used for non exclusive groups" raise TypeError(msg) if exclusive and description is not None: msg = "description can only be used for non exclusive groups" raise TypeError(msg) if exclusive and required is None: required = False # default for add_mutually_exclusive_group def call(p): if exclusive: p.append(p[-1].add_mutually_exclusive_group(required=required)) else: p.append(p[-1].add_argument_group(title=title, description=description)) return p def begin_group(func: Union[Callable, _Argument]): func = _Argument(func) func.calls.append(call) return func if noarg: return begin_group(noarg) return begin_group
Decorator that adds a group of arguments for a command line tool.
It should be written after the
command()
decorator. It should be followed by a number ofarg()
decorators and by a closingendgroup()
decorator.Parameters
title
:str
- The name of the group. Can only be used for non exclusive groups.
description
:str
- A description of the group. Can only be used for non exclusive groups.
exclusive
:bool
- If True, the arguments belonging to this group are mutually exclusive
required
:bool
- If True, one of the arguments at least should be passed. Can only be used for exclusive groups.
Examples
This is a simple command line tool that accepts three arguments (
-a
,-b
, or-c
), mutually exclusive. When ran, it will just print booleans showing if these arguments were passed.from bussilab.cli import command, group, arg, endgroup @command("doit") @group(exclusive=True) @arg("-a", action='store_true') @arg("-b", action='store_true') @arg("-c", action='store_true') @endgroup def check(a, b, c): print(a, b, c)