Metadata-Version: 2.1
Name: dollar-lambda
Version: 0.4.0
Summary: An argument parser for Python built from functional first principles
Home-page: https://dollar-lambda.readthedocs.io
Keywords: argument-parser,parser,parser-combinators,functional-programming
Author: Ethan Brooks
Author-email: ethanabrooks@gmail.com
Maintainer: Ethan Brooks
Maintainer-email: ethanabrooks@gmail.com
Requires-Python: >=3.8,<4.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Utilities
Requires-Dist: pytypeclass (>=0.1.1,<0.2.0)
Project-URL: Repository, https://github.com/ethanabrooks/dollar-lambda
Description-Content-Type: text/markdown

<p align="center">
  <img height="300" src="https://ethanabrooks.github.io/dollar-lambda/logo.png">
</p>

[$λ](https://ethanabrooks.github.io/dollar-lambda/) 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://ethanabrooks.github.io/dollar-lambda/dollar_lambda/)

# 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://ethanabrooks.github.io/dollar-lambda/dollar_lambda/#dollar_lambda.command) decorator
For the vast majority of parsing patterns, `@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


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` 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 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:


```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`,
including some that make use of the `base_function`.
To learn more, we recommend the [`CommandTree` tutorial](https://ethanabrooks.github.io/dollar-lambda/dollar_lambda/#commandtree-tutorial).

## Lower-level syntax
[`@command`](https://ethanabrooks.github.io/dollar-lambda/dollar_lambda/#dollar_lambda.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:


```python
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](https://ethanabrooks.github.io/dollar-lambda/dollar_lambda/#tutorial).
For now, suffice to say that [`argument`](https://ethanabrooks.github.io/dollar-lambda/dollar_lambda/#dollar_lambda.argument) defines a positional argument,
[`many`](https://ethanabrooks.github.io/dollar-lambda/dollar_lambda/#dollar_lambda.Parser.many) allows parsers to be applied
zero or more times, and [`|`](https://ethanabrooks.github.io/dollar-lambda/dollar_lambda/#dollar_lambda.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.

