An argument parser for Python built from functional first principles
Project description
$λ provides an alternative to argparse
based on parser combinators and functional first principles. Arguably, $λ
is way more expressive than any reasonable
person would ever need... but even if it's not the parser that we need, it's the parser we deserve.
Installation
pip install dollar-lambda
Documentation
Highlights
$λ
comes with syntactic sugar that came make building parsers completely boilerplate-free.
For complex parsing situations that exceed the expressive capacity of this syntax,
the user can also drop down to the lower-level syntax that lies behind the sugar, which can
handle any reasonable amount of logical complexity.
The @command
decorator
For the vast majority of parsing patterns, @command
is the most concise way to
define a parser:
from dollar_lambda import command
@command()
def main(x: int, dev: bool = False, prod: bool = False):
return dict(x=x, dev=dev, prod=prod)
Here is the help text generated by this parser:
main("-h") # ordinarily you provide no arguments and main gets them from sys.argv
usage: -x X --dev --prod
main(
"-x", "1", "--dev"
) # again, ordinarily these arguments would come from from sys.argv
{'x': 1, 'dev': True, 'prod': False}
Use the parsers
argument to add custom logic to this parser:
from dollar_lambda import flag
@command(parsers=dict(kwargs=(flag("dev") | flag("prod"))))
def main(x: int, **kwargs):
return dict(x=x, **kwargs)
This parser requires either a --dev
or --prod
flag and maps it to the kwargs
argument:
main("-h")
usage: -x X [--dev | --prod]
main("-x", "1", "--dev")
{'x': 1, 'dev': True}
main("-x", "1", "--prod")
{'x': 1, 'prod': True}
main("-x", "1")
usage: -x X [--dev | --prod]
The following arguments are required: --dev
CommandTree
for dynamic dispatch
For many programs, a user will want to use one entrypoint for one set of
arguments, and another for another set of arguments. Returning to our example,
let's say we wanted to execute prod_function
when the user provides the
--prod
flag, and dev_function
when the user provides the --dev
flag:
from dollar_lambda import CommandTree
tree = CommandTree()
@tree.command()
def base_function(x: int):
print("Ran base_function with arguments:", dict(x=x))
@base_function.command()
def prod_function(x: int, prod: bool):
print("Ran prod_function with arguments:", dict(x=x, prod=prod))
@base_function.command()
def dev_function(x: int, dev: bool):
print("Ran dev_function with arguments:", dict(x=x, dev=dev))
Let's see how this parser handles different inputs.
If we provide the --prod
flag, $λ
automatically invokes
prod_function
with the parsed arguments:
tree(
"-x", "1", "--prod"
) # usually you provide no arguments and tree gets them from sys.argv
Ran prod_function with arguments: {'x': 1, 'prod': True}
If we provide the --dev
flag, $λ
invokes dev_function
:
tree("-x", "1", "--dev")
Ran dev_function with arguments: {'x': 1, 'dev': True}
With this configuration, the parser will run base_function
if neither
--prod
nor --dev
are given:
tree("-x", "1")
Ran base_function with arguments: {'x': 1}
As with main
in the previous example, you would ordinarily provide tree
no arguments and it would get them
from the command line.
There are many other ways to use CommandTree
,
including some that make use of the base_function
.
To learn more, we recommend the CommandTree
tutorial.
Lower-level syntax
@command
and CommandTree
cover many use cases,
but they are both syntactic sugar for a lower-level interface that is far
more expressive.
Suppose you want to implement a parser that first tries to parse an option
(a flag that takes an argument),
-x X
and if that fails, tries to parse the input as a variadic sequence of
floats:
from dollar_lambda import argument, option
p = option("x", type=int) | argument("y", type=float).many()
We go over this syntax in greater detail in the tutorial.
For now, suffice to say that argument
defines a positional argument,
many
allows parsers to be applied
zero or more times, and |
expresses alternatives.
Here is the help text:
p.parse_args(
"-h"
) # usually you provide no arguments and parse_args gets them from sys.argv
usage: [-x X | [Y ...]]
As promised, this succeeds:
p.parse_args("-x", "1")
{'x': 1}
And this succeeds:
p.parse_args("1", "2", "3")
{'y': [1.0, 2.0, 3.0]}
Thanks
Special thanks to "Functional Pearls" by Graham Hutton and Erik Meijer for bringing these topics to life.
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
Hashes for dollar_lambda-0.3.6-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7c736d7238aa453542c4ff3ca2078a2a47770e075b7669ae89ab9bb004bbd839 |
|
MD5 | a747171dd841d07922c18f84eb706463 |
|
BLAKE2b-256 | 4497bb984fb611d7b1c3e1c689b11d6e730162e3e3aa296e87dbee1010435860 |