Lightweight set of tools for creating pretty-looking CLI applications in Python. This library tends to simplify and unify the approach to structuring CLI related code. At the moment it covers:
- Managing terminal output - verbosity levels, colored output, logger configuration
- Parsing arguments
- Modular application design - each module could extend argument parser with own command
- Modules discovery - scanning packages to find cli extension modules
- Module availability support - module might declare a method to verify if environment is suitable (e.g. all dependencies are present). If not, module will be automatically excluded from CLI interface
- Sync and Async execution managers
- Loading external components
- Installing optional dependencies
The module provides built-in methods to output information for the end user. It is configured to pint direct
informational messages, errors and warnings to the
stderr and execution result to
The functionality is available via special object called
CLI. Consider the following example:
from cli_rack import CLI CLI.setup() CLI.print_info("This is just a message to user") CLI.print_warn("This is a warning") CLI.print_error("This is an error message") CLI.print_error(ValueError("This is an exception")) CLI.print_data("This text comes to STDOUT, not STDERR")
CLI.setup() must be invoked in the very beginning of the program as it determines terminal capabilities and configures
logger, filters and formatters. It is also possible to configure verbosity level on this stage. See
Section Verbosity configuration for details.
CLI.print_* have very similar interface and are designed for printing information of specific type. It is
highly important to avoid simple built-in
CLI.print_* methods instead as it allows to keep
output clean, consistent and well formatted. Also, verbosity control won't work for any data written directly into
output stream. It is also possible to override formatting options for individual message see
Section Formatting capabilities for more details.
CLI.print_info(msg: str, style: Optional[ansi.AnsiCodeType] = None)
Streamlined interface for tuning configuration
Loading external libraries
If you are building modular application you might want to allow your app with external components loaded from remote
cli_rack.loader package streamlines development of this feature.
The example below illustrates the main idea:
from cli_rack import CLI, ansi from cli_rack.loader import LoaderRegistry CLI.setup() CLI.verbose_mode() CLI.print_info("Loading module using loader registry\n", ansi.Mod.BOLD) LoaderRegistry.target_dir = "generated" resource_meta = LoaderRegistry.load("github://logicify/healthcheckbot") CLI.print_info(resource_meta.to_dict())
Here we configure loader to put downloaded modules into
generated folder. Then ask to download module using the
following resource locator
LoaderRegistry tries to find loader capable to handle
this type of locator (built-in
GithubLoader in this case) and passes control to particular loader.
Loader will check if the local copy of the package is already present and it is up to date. If this is the case nothing
will be downloaded. Otherwise, it will download data and put it under
The data in the remote location might have different directory layout and it is highly likely you don't need file from
the root, but rather interested in some subdirectory. For this purpose you could create a
path_resolver function like
import os from cli_rack.loader import LoadedDataMeta, InvalidPackageStructure, LoaderRegistry def packages_dir_resolver(meta: LoadedDataMeta) -> str: if os.path.isdir(os.path.join(meta.path, "packages")): return "packages" raise InvalidPackageStructure(meta, "Folder \"packages\" must be present in directory root") LoaderRegistry.target_dir = "generated" resource_meta = LoaderRegistry.load("github://corvis/esphome-packages", packages_dir_resolver)
Extending cli_rack.loader with custom loader
It is pretty trivial to implement custom loader to fetch data from other sources (e.g. your proprietary company NAS):
cli_rack.loader.BaseLoader(you might want to use GithubLoader as an example).
- Register it in
from cli_rack.loader import LoaderRegistry LoaderRegistry.register(MyLoader)
Modular application architecture
TBD * Global options * Commands * Params * Dynamic available commands discovery * Commands availability * Automatic dependencies installation
- Dmitry Berezovsky, author
This module is licensed under MIT. This means you are free to use it in commercial projects.
The MIT license clearly explains that there is no warranty for this free software. Please see the included LICENSE file for details.