lc_task.cli module

lc_task.cli.CliParserConfig

Command line parser generator config type (see gen_cli_parser()).

alias of Dict[Union[Type[Task], Tuple[Union[str, Tuple[str, …]], str]], Optional[CliParserConfig]]

class lc_task.cli.CliTask(raise_exceptions: bool = False, args: Namespace | None = None)

Bases: Task

Base task for tasks instantiated via command line.

Integrates with ArgumentParser for defining command line parsers.

Examples:

Create a CLI task to count the number of lines in a file:

>>> import tempfile
>>> from argparse import ArgumentParser
>>> from pathlib import Path
>>> from lc_task import TaskResult, taskclass
>>> from lc_task.cli import CliTask
>>> @taskclass
... class CountLinesTask(Task):
...     input_path: typing.Optional[Path] = None
...     def _perform_task(self) -> None:
...         if self.input_path is None or not self.input_path.is_file():
...             raise FileNotFoundError(self.input_path)
...         num_lines = 0
...         with self.input_path.open() as input_file:
...             for _ in input_file:
...                 num_lines += 1
...         print(num_lines)
...
>>> @taskclass
... class CountLinesCliTask(CliTask):
...     _task_cls = CountLinesTask
...     command = "linecount"
...     description = "Count the number of lines in a file"
...     @classmethod
...     def gen_command_parser(cls, parser: typing.Optional[ArgumentParser] = None) -> ArgumentParser:
...         parser = super(CountLinesCliTask, cls).gen_command_parser(parser)
...         parser.add_argument("input_path", type=Path, help="Path to input file")
...         return parser
...
>>> with tempfile.NamedTemporaryFile() as tmp_file:
...     # Write ten lines to file
...     for _ in range(10):
...         __ = tmp_file.write(b"\n")
...     # Flush writes
...     tmp_file.flush()
...     _ = CountLinesCliTask.run_command(argv=[tmp_file.name])
10
_gen_task() Task | None

Generate new task.

Default implementation merges CliTask.args with instance of CliTask._task_cls if it’s not None.

_perform_task() None

Perform the task.

Child classes should set attributes on Task.result in this function.

_task_cls: ClassVar[Type[Task] | None] = None

Task class to run (see CliTask._gen_task()).

aliases: ClassVar[List[str] | None] = None

Aliases of the CLI command for this task

args: Namespace | None

Command line arguments

command: ClassVar[str] = ''

Name of the CLI command

description: ClassVar[str] = ''

Description of CLI command

classmethod gen_command_parser(parser: ArgumentParser | None = None) ArgumentParser

Generate command line parser.

Parameters:

parser – parser to add arguments to.

Returns:

Updated command line parser.

Raises:

ValueError – if class variables CliTask.command and CliTask.description are not set.

classmethod run_command(argv: List[str] | None = None, known_args: bool = False) TaskResult

Parse command line arguments and run task.

Parameters:
  • argv – command line arguments to parse.

  • known_args – only parse known args (see ArgumentParser.parse_known_args()).

lc_task.cli.gen_cli_parser(root_parser: ArgumentParser, parser_config: lc_task.cli.CliParserConfig) ArgumentParser

Generate commmand line parser from configuration.

The configuration is a dict that maps subcommands to CliTask classes. Uses the configuration to generate a subcommand-style CLI like git (e.g. git clone, git pull, etc).

Parameters:
  • root_parser – root parser to which all other parsers will be attached.

  • parser_config – mapping of subcommands to CliTask defining the CLI hierarchy.

Returns:

root_parser with the subcommands defined in parser_config.

Raises:

TypeError – if subcommand isn’t of type CliParserConfig.

Examples:

Given the parser config:

{
    ("amphibians", "Amphibian animals"): {
        FrogsTask: {},
    },
    (("invertebrates", "i"), "Invertebrate animals"): {
        WormsTask: {},
        ArthropodsTask: {},
    },
    ("reptiles", "Reptile animals"): {
        SnakesTask: {},
    },
    (("vertebrates", "v"), "Vertebrate animals"): {
        FishTask: {},
        BirdsTask: {},
    },
}
The following command line paths would be valid (assuming animals.py is the root Python file):
  • animals.py amphibians

  • animals.py amphibians frogs

  • animals.py invertebrates

  • animals.py i

  • animals.py invertebrates worms

  • animals.py i worms

  • animals.py invertebrates arthropods

  • animals.py i arthropods

  • animals.py reptiles

  • animals.py reptiles snakes

  • animals.py vertebrates

  • animals.py v

  • animals.py vertebrates fish

  • animals.py v fish

  • animals.py vertebrates birds

  • animals.py v birds