a declarative, modular, lightweight, and versatile python library for building cli applications
Project description
befehl
befehl (german for command) is a
- declarative
- modular (easily reuse definitions),
- lightweight (no external dependencies), and
- versatile (highly customizable behavior through custom parsers and validation)
python library for building CLI applications.
It features
- a modern, declarative API,
- QoL features like short-option grouping,
- automatic generation of help-options, and
- generation of bash-autocomplete source files.
Example
from befehl import Parser, Option, Argument, Command, Cli
# define subcommand
class MySubCommand(Command):
opt = Option("--sub-option")
arg = Argument("subarg", nargs=-1)
def run(self, args):
# run business logic on parsed input
# ...
def validate(self, args):
# perform custom validation on parsed input
# ...
# define base-command
class MyCli(Cli):
cmd = MySubCommand("subcommand")
opt0 = Option(("-o", "--option-zero"))
opt1 = Option(("-p", "--option-one"))
arg0 = Argument("arg", parser=Parser.parse_as_path)
def run(self, args):
# run business logic on parsed input
# ...
def validate(self, args):
# perform custom validation on parsed input
# ...
# validate + build entry-point
cli = MyCli("my-cli").build()
Documentation
Command declaration
One of the few limitation imposed by this library is that all CLIs have the following structure:
[command] [subcommand1 [subcommand 2 [...]]] [options] [--] [arguments]
When defining a CLI, the command-tree is built from classes inheriting from Command.
A Command-class encapsulates all (immediate) subcommands (i.e., instances of previously defined Commands), Options, and Arguments as class attributes:
class MySubCommand(Command):
...
class MyCli(Cli):
cmd = MySubCommand("subcommand")
opt0 = Option(("-o", "--option-zero"))
opt1 = Option(("-p", "--option-one"))
arg0 = Argument("arg", parser=Parser.parse_as_path)
...
(Cli is an optional alias for Command)
Business logic
A Command's business logic is defined in its run method, e.g.,
class MyCli(Cli):
...
def run(self, args):
print(args)
When being executed, this function receives a singular argument args: dict[Option | Argument, list[Any]], where values are lists of parsed values.
For example, invoking the Command from above with a call like
command -o path/to/file
results in an args-mapping of
args = {
MyCli.opt0: [],
MyCli.arg0: [Path(path/to/file)]
}
Multiple values to a single Option or Argument keep their order in the generated list.
Validation
Optionally, before entering the business-logic, a validation-step can be defined.
To this end, the empty validate method of the Command class can be overwritten.
For example, the following definition validates that, if -o is given, option -p is needed as well.
class MyCli(Cli):
...
def validate(self, args):
if self.opt0 in args and self.opt1 not in args:
return (
False,
f"option {self.opt0} also requires option {self.opt1}"
)
return True, None
Invoking the cli with command -o returns with the above message (and exit code 1) whereas command -op continues past the validation into the run-method.
Build
In order to create a callable function that can be used as an entry-point for python packages, a build-step has to be performed.
For example, for the above CLI, one can enter
...
cli = MyCli("my-cli").build(help_=True, completion=False)
The variable cli then serves as the entry-point.
Suppose, this variable is in the namespace of the module cli, then it can be used with, for example, setuptools as
setup(
...
entry_points={
"console_scripts": [
"command = cli:cli",
],
},
...
)
When building, it has to be decided, whether
- a help-option (
-h, --help) should be generated (enabled by default) - an option (
--generate-autocomplete) for generating a sourcable bash-autocomplete script should be added (disabled by default; enabled if environment sets_BEFEHL_COMPLETION). See this section for details.
Parsers
Both Options and Arguments accept keyword arguments for a parser.
A parser is a function that accepts a single string-value and returns a tuple of
- boolean (whether input is valid),
- string (message in case the input is invalid), and
- any object (parsed data in case of success).
This library provides some basic parsers to be used in Options and Arguments.
These range from parsing of primitive types like boolean or integer to more involved parsers for paths/files/directories (using pathlib.Path) or requiring values to satisfy regular expressions.
Pre-defined parsers are accessible via the abstract collection class Parser and can be accessed via its static methods like
class AnotherCommand(Command):
opt = Option("-o", parser=Parser.parse_as_int)
arg = Argument("arg", parser=Parser.parse_with_regex(r"[0-9]+"))
...
Lastly, by using the methods Parser.first or Parser.chain, multiple parsers can be applied to single values.
Other features
(Short) Option grouping
This library supports Options in short and long format:
- short corresponds to starting with single
-followed by a single character - long
Options start with--followed by at least one character
For convenience, Options with short name and nargs=0 can be grouped.
For example, when using the Command from above, the two inputs
command -o -p
and
command -op
are equivalent.
Equal-sign syntax
In order to avoid problems with Option values starting with - (could be ambiguous regarding other Options), Options can be used with the following syntax
command --delta=-1
(instead of command --delta -1 which would fail).
Separator for options and arguments
Similar to the problem described in the previous section: In order to avoid problems with Argument values starting with - (could be ambiguous regarding Options), the separator -- can be used like
command -o -- -1
(instead of command -o -1 which would fail).
Autocomplete
This library can generate shell-script files that, when sourced in bash, enable basic autocomplete functionality.
The script can be built by first setting the completion-option during build and then call the CLI with the --generate-autocomplete option (will be printed to stdout).
The auto-completion can also be sourced immediately by entering
eval "$(_BEFEHL_COMPLETION= <entry-point> --generate-autocomplete)"
(replace <entry-point> with your custom entry-point).
Tests
Automated (pytest-)tests can be run by first installing this package as well as its dev-dependencies via
pip install .
pip install -r dev-requirements.txt
Afterwards, simply enter
pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file befehl-0.1.0.tar.gz.
File metadata
- Download URL: befehl-0.1.0.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0856965af44a903626d66a7ce66e7b95dfed937df0be6ed3c7a8f065ee53d3d3
|
|
| MD5 |
610bb0310edce3954e153445f0294c85
|
|
| BLAKE2b-256 |
df8ce971f8ab81f6c20e9342e723ba93f5498738eda908d6500b3068f12f07a7
|
File details
Details for the file befehl-0.1.0-py3-none-any.whl.
File metadata
- Download URL: befehl-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd223b1c5d7663e1b8057a20c22b1e64d0973ccd43c11499d0599b56070a3670
|
|
| MD5 |
06bf47873d94eddefd28e010acdb32e1
|
|
| BLAKE2b-256 |
5111dcd4e40d796bdd36d356b747e2f7e8450381c9943dd994a6f60dcb3e8217
|