Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ethanabrooks/dollar-lambda
An argument parsing library based on function first principles
https://github.com/ethanabrooks/dollar-lambda
argument-parser functional-programming parser parser-combinators python
Last synced: 1 day ago
JSON representation
An argument parsing library based on function first principles
- Host: GitHub
- URL: https://github.com/ethanabrooks/dollar-lambda
- Owner: ethanabrooks
- License: mit
- Created: 2020-08-01T12:36:44.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2022-07-30T17:39:59.000Z (over 2 years ago)
- Last Synced: 2024-04-25T16:02:49.360Z (7 months ago)
- Topics: argument-parser, functional-programming, parser, parser-combinators, python
- Language: Python
- Homepage:
- Size: 2.05 MB
- Stars: 3
- Watchers: 4
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[$λ](https://dollar-lambda.readthedocs.io/) provides an alternative to [`argparse`](https://docs.python.org/3/library/argparse.html)
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](https://dollar-lambda.readthedocs.io/)
# Highlights
`$λ` comes with syntactic sugar that can 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`](https://dollar-lambda.readthedocs.io/en/latest/api.html?highlight=command#dollar_lambda.decorators.command)
decorator
For the vast majority of parsing patterns,
[`@command`](https://dollar-lambda.readthedocs.io/en/latest/api.html?highlight=command#dollar_lambda.decorators.command)
is the most concise way to define a parser:```python
from dollar_lambda import command@command()
def main(x: int, dev: bool = False, prod: bool = False):
print(dict(x=x, dev=dev, prod=prod))
```Here is the help text generated by this parser:
```python
main("-h")
```usage: -x X --dev --prod
dev: (default: False)
prod: (default: False)Ordinarily you provide no arguments to `main` and it would get them from the command line.
The explicit arguments in this Readme are for demonstration purposes only.
Here is how the main function handles input:```python
main("-x", "1", "--dev")
```{'x': 1, 'dev': True, 'prod': False}
Use the `parsers` argument to add custom logic using the lower-level syntax:
```python
from dollar_lambda import flag@command(parsers=dict(kwargs=flag("dev") | flag("prod")))
def main(x: int, **kwargs):
print(dict(x=x, **kwargs))
```This parser requires either a `--dev` or `--prod` flag and maps it to the `kwargs` argument:
```python
main("-h")
```usage: -x X [--dev | --prod]
This assigns `{'dev': True}` to the `kwargs` argument:
```python
main("-x", "1", "--dev")
```{'x': 1, 'dev': True}
This assigns `{'prod': True}` to the `kwargs` argument:
```python
main("-x", "1", "--prod")
```{'x': 1, 'prod': True}
This fails because the parser requires one or the other:
```python
main("-x", "1")
```usage: -x X [--dev | --prod]
The following arguments are required: --dev## [`CommandTree`](https://dollar-lambda.readthedocs.io/en/latest/commandtree.html) 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:```python
from dollar_lambda import CommandTreetree = 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:```python
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`:
```python
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:```python
tree("-x", "1")
```Ran base_function with arguments: {'x': 1}
There are many other ways to use [`CommandTree`](https://dollar-lambda.readthedocs.io/en/latest/commandtree.html).
To learn more, we recommend the [`CommandTree` tutorial](https://dollar-lambda.readthedocs.io/en/latest/command_tree.html).## Lower-level syntax
[`@command`](https://dollar-lambda.readthedocs.io/en/latest/api.html?highlight=command#dollar_lambda.decorators.command)
and [`CommandTree`](https://dollar-lambda.readthedocs.io/en/latest/api.html#dollar_lambda.decorators.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:```python
from dollar_lambda import argument, optionp = option("x", type=int) | argument("y", type=float).many()
```We go over this syntax in greater detail in the [tutorial](https://dollar-lambda.readthedocs.io/en/latest/tutorial.html).
For now, suffice to say that [`argument`](https://dollar-lambda.readthedocs.io/en/latest/api.html?highlight=argument#dollar_lambda.parsers.argument)
defines a positional argument,
[`many`](https://dollar-lambda.readthedocs.io/en/latest/variations.html?highlight=many#many) allows parsers to be applied
zero or more times, and [`|`](https://dollar-lambda.readthedocs.io/en/latest/api.html?highlight=__or__#dollar_lambda.parsers.Parser.__or__) expresses alternatives.Here is the help text:
```python
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:
```python
p.parse_args("-x", "1")
```{'x': 1}
And this succeeds:
```python
p.parse_args("1", "2", "3")
```{'y': [1.0, 2.0, 3.0]}
### Thanks
Special thanks to ["Functional Pearls"](https://www.cs.nott.ac.uk/~pszgmh/pearl.pdf) by Graham Hutton and Erik Meijer for bringing these topics to life.