API
API#
Defines the |
|
Defines |
|
Defines the |
|
Defines errors which can be raised by parsers. |
|
Defines parsing functions and the |
Defines Sequence
,
a strongly-typed immutable list that implements
MonadPlus.
- class dollar_lambda.data_structures.KeyValue(key, value)#
Simple dataclass for storing key-value pairs.
- class dollar_lambda.data_structures.Output(get)#
This is the wrapper class for the output of
Parser
.
- class dollar_lambda.data_structures.Sequence(get)#
This class combines the functionality of MonadPlus and
typing.Sequence
>>> from dollar_lambda.data_structures import Sequence >>> s = Sequence([1, 2]) >>> len(s) 2 >>> s[0] 1 >>> s[-1] 2 >>> s + s # sequences emulate list behavior when added Sequence(get=[1, 2, 1, 2]) >>> [x + 1 for x in s] # sequences can be iterated over [2, 3] >>> Sequence([1, 2]) >= (lambda x: Sequence([x, -x])) Sequence(get=[1, -1, 2, -2])
- bind(f)#
>>> Sequence([1, 2]) >= (lambda x: Sequence([x, -x])) Sequence(get=[1, -1, 2, -2])
- static return_(a)#
>>> Sequence.return_(1) Sequence(get=[1])
- to_colliding_dict()#
>>> from dollar_lambda import Sequence, KeyValue >>> Sequence([KeyValue("a", 1), KeyValue("b", 2), KeyValue("a", 3)]).to_colliding_dict() {'a': [1, 3], 'b': 2} >>> Sequence([KeyValue("a", [1]), KeyValue("b", 2), KeyValue("a", [3])]).to_colliding_dict() {'a': [[1], [3]], 'b': 2}
- to_dict()#
>>> from dollar_lambda import Sequence, KeyValue >>> Sequence([KeyValue("a", 1), KeyValue("b", 2), KeyValue("a", 3)]).to_dict() {'a': [1, 3], 'b': 2} >>> from dollar_lambda import Sequence, KeyValue >>> Sequence([KeyValue("a", [1]), KeyValue("b", 2), KeyValue("a", [3])]).to_dict() {'a': [[1], [3]], 'b': 2} >>> from dollar_lambda.data_structures import _TreePath >>> Sequence([KeyValue("a", _TreePath.make("b", leaf="c"))]).to_dict() {'a': {'b': 'c'}} >>> Sequence( ... [ ... KeyValue("a", "b"), ... KeyValue("a", _TreePath.make("b", leaf="c")), ... KeyValue("a", _TreePath.make("b", "c", leaf=1)), ... KeyValue("a", _TreePath.make("b", "c", leaf=2)), ... ] ... ).to_dict() {'a': ['b', {'b': ['c', {'c': [1, 2]}]}]}
Defines parsing functions and the
Parser
class that they instantiate.
- class dollar_lambda.parsers.Parse(parsed, unparsed)#
A
Parse
is the output of parsing.
- class dollar_lambda.parsers.Parser(f, usage, helps, nonoptional=None)#
Main class powering the argument parser.
- __add__(other)#
Parse two arguments in either order.
>>> from dollar_lambda import flag >>> p = flag("verbose") + flag("debug") >>> p.parse_args("--verbose", "--debug") {'verbose': True, 'debug': True} >>> p.parse_args("--debug", "--verbose") {'debug': True, 'verbose': True} >>> p.parse_args("--debug") usage: --verbose --debug Expected '--verbose'. Got '--debug'
Note that if more than two arguments are chained together with
+
, some combinations will not parse:>>> p = flag("a") + flag("b") + flag("c") >>> p.parse_args("-c", "-a", "-b") # this works {'c': True, 'a': True, 'b': True} >>> p.parse_args("-a", "-c", "-b") # this doesn't usage: -a -b -c Expected '-b'. Got '-c'
This makes more sense when one supplies the implicit parentheses:
>>> p = (flag("a") + flag("b")) + flag("c")
In order to chain together more than two arguments, use
nonpositional
:>>> from dollar_lambda import nonpositional >>> p = nonpositional(flag("a"), flag("b"), flag("c")) >>> p.parse_args("-a", "-c", "-b") {'a': True, 'c': True, 'b': True}
- __ge__(f)#
Sugar for
Parser.bind
.
- __or__(other)#
Tries apply the first parser. If it fails, tries the second. If that fails, the parser fails.
>>> from dollar_lambda import argument, option, flag >>> p = option("option") | flag("verbose") >>> p.parse_args("--option", "x") {'option': 'x'} >>> p.parse_args("--verbose") {'verbose': True}
Note that by default,
Parser.parse_args
adds>> Parser.done()
to the end of parsers, causingp
to fail when both arguments are supplied:>>> p.parse_args("--verbose", "--option", "x") usage: [--option OPTION | --verbose] Unrecognized argument: --option
To disable this behavior, use
allow_unparsed
:>>> p.parse_args("--verbose", "--option", "x", allow_unparsed=True) {'verbose': True}
- __rshift__(p)#
This applies parsers in sequence. If the first parser succeeds, the unparsed remainder gets handed off to the second parser. If either parser fails, the whole thing fails.
>>> from dollar_lambda import argument, flag >>> p = argument("first") >> argument("second") >>> p.parse_args("a", "b") {'first': 'a', 'second': 'b'} >>> p.parse_args("a") usage: FIRST SECOND The following arguments are required: second >>> p.parse_args("b") usage: FIRST SECOND The following arguments are required: second
This is how this method handles formatting of usage strings:
>>> long = nonpositional(flag("a"), flag("b"), flag("c"), flag("d")) >>> short = argument("pos") >>> (short >> short >> short).parse_args("-h") usage: POS POS POS >>> (short >> short >> long).parse_args("-h") usage: POS POS -a -b -c -d >>> (short >> (short >> long)).parse_args("-h") usage: POS POS -a -b -c -d >>> (long >> short >> short).parse_args("-h") usage: -a -b -c -d POS POS >>> (long >> (short >> short)).parse_args("-h") usage: -a -b -c -d POS POS >>> (long >> short >> long).parse_args("-h") usage: -a -b -c -d POS -a -b -c -d >>> (long >> (short >> long)).parse_args("-h") usage: -a -b -c -d POS -a -b -c -d
- __xor__(other)#
This is the same as
|
, but it succeeds only if one of the two parsers fails.>>> p = argument("int", type=int) ^ argument("div", type=lambda x: 1 / float(x)) >>> p.parse_args("inf") # succeeds because int("inf") fails {'div': 0.0} >>> p.parse_args("0") # succeeds because 1 / 0 throws an error {'int': 0} >>> p.parse_args("1") # fails because both parsers succeed Both parsers succeeded. This causes ^ to fail.
- apply(f)#
Takes the output of parser and applies
f
to it. Convert any errors that arise intoArgumentError
.>>> p1 = flag("hello") >>> p1.parse_args("--hello") {'hello': True}
This will double
p1
’s output:>>> from dollar_lambda import Result >>> p2 = p1.apply(lambda out: Result.return_(out + out)) >>> p2.parse_args("--hello") {'hello': [True, True]}
- bind(f)#
Returns a new parser that
applies
self
;if this succeeds, applies
f
to the parsed component of the result.
Parser.bind()
is one of the functions that makesParser
a Monad. But most users will avoid using it directly, preferring higher level combinators like>>
,|
and+
.Note that
>=
as a synonym forbind
(as defined in pytypeclass) and we typically prefer using the infix operator to the spelled out method.To demonstrate one use of
bind
or>=
, let’s use thematches()
parser to write a function that takes the output of a parser and fails unless the next argument is the same as the first:>>> from dollar_lambda import Output, Sequence, KeyValue, Parser, matches, argument >>> def f(out: Output[Sequence[KeyValue]]) -> Parser[Output[str]]: ... *_, kv = out.get ... return matches(kv.value) ... >>> p = argument("some_dest") >= f >>> p.parse_args("a", "a") {'a': 'a'} >>> p.parse_args("a", "b") Expected 'a'. Got 'b'
- classmethod done(a=None)#
Parser.done()
succeeds on the end of input and fails on everything else.>>> Parser.done().parse_args() {} >>> Parser.done().parse_args("arg") Unrecognized argument: arg
When
allow_unparsed=False
(the default),Parser.parse_args()
adds>> done()
to the end of the parser. If you invokeParser.parse_args()
with ``allow_unparsed=True` and withoutParser.done()
the parser will not complain about leftover (unparsed) input:>>> flag("verbose").parse_args("--verbose", "--quiet", allow_unparsed=True) {'verbose': True} >>> flag("verbose").parse_args("--verbose", "--quiet", allow_unparsed=False) usage: --verbose Unrecognized argument: --quiet >>> (flag("verbose") >> Parser.done()).parse_args("--verbose", "--quiet", allow_unparsed=True) usage: --verbose Unrecognized argument: --quiet
- classmethod empty(a=None)#
Always returns
{}
, no matter the input. Used by several other parsers.>>> Parser.empty().parse_args("any", "arguments", allow_unparsed=True) {}
- fails(a=None)#
Succeeds only if
self
fails. Does not consume any input.>>> flag("x").fails().parse_args("not x", allow_unparsed=True) # succeeds {} >>> flag("x").fails().parse_args("-x", allow_unparsed=True) # fails Parser unexpectedly succeeded.
- findall(pattern)#
This method assumes that the most recent output from applying
self
is a string. It then finds all occurrences ofpattern
in this string and binds them to the same key.>>> p = item("a").findall(r"[1-2]") >>> p.parse_args("1") {'a': '1'}
Multiple matches are returned in a list: >>> p.parse_args(“123”) {‘a’: [‘1’, ‘2’]}
Multiple matches are returned in a list: >>> (item(“a”) >> item(“b”)).findall(r”[1-2]”).parse_args(“bound-to-a”, “12”) {‘a’: ‘bound-to-a’, ‘b’: [‘1’, ‘2’]}
findall
fails when the most recent output is not a string: >>> flag(“a”).findall(r”[1-2]”).parse_args(“-a”) usage: -a An argument Output(get=Sequence(get=[KeyValue(key=’a’, value=True)])): raised exception expected string or bytes-like object
- ignore(a=None)#
Ignores the output from a parser. This is useful when you expect to give arguments to the command line that some other utility will handle.
>>> p = flag("hello").ignore()
This will not bind any value to
"hello"
:>>> p.parse_args("--hello") {}
But
--hello
is still required:>>> p.parse_args() The following arguments are required: --hello
- many(max=80)#
Applies
self
zero or more times (like*
in regexes).- Parameters
max (int) – Limits the number of times
Parser.many()
is applied in order to prevent aRecursionError
. The default for this can be increased by either settingparser.MAX_MANY
or the environment variableDOLLAR_LAMBDA_MAX_MANY
.
Examples
>>> from dollar_lambda import argument, flag >>> p = argument("as-many-as-you-like").many() >>> p.parse_args() {} >>> p = argument("as-many-as-you-like").many() >>> p.parse_args("a") {'as-many-as-you-like': 'a'} >>> p = argument("as-many-as-you-like").many() >>> p.parse_args("a", "b") {'as-many-as-you-like': ['a', 'b']}
Note that if
self
contains|
, the arguments can be heterogenous:>>> p = flag("verbose") | flag("quiet") >>> p = p.many() >>> p.parse_args("--verbose", "--quiet") # mix --verbose and --quiet {'verbose': True, 'quiet': True}
- many1(max=80)#
Applies
self
one or more times (like+
in regexes).- Parameters
max (int) – Limits the number of times
Parser.many()
is applied in order to prevent aRecursionError
. The default for this can be increased by either settingparser.MAX_MANY
or the environment variableDOLLAR_LAMBDA_MAX_MANY
.
Examples
>>> from dollar_lambda import argument, flag >>> p = argument("1-or-more").many1() >>> p.parse_args("1") {'1-or-more': '1'} >>> p.parse_args("1", "2") {'1-or-more': ['1', '2']} >>> p.parse_args() usage: 1-OR-MORE [1-OR-MORE ...] The following arguments are required: 1-or-more
- n_times(n)#
>>> argument("a").n_times(0).parse_args() {} >>> argument("a").n_times(0).parse_args("b") Unrecognized argument: b >>> argument("a").n_times(1).parse_args() usage: A The following arguments are required: a >>> argument("a").n_times(1).parse_args("b") {'a': 'b'} >>> argument("a").n_times(1).parse_args("b", "c") usage: A Unrecognized argument: c >>> argument("a").n_times(2).parse_args("b") usage: A A The following arguments are required: a >>> argument("a").n_times(2).parse_args("b", "c") {'a': ['b', 'c']} >>> argument("a").n_times(2).parse_args("b", "c", "d") usage: A A Unrecognized argument: d
- nesting()#
Breaks the output of the wrapped parser into nested outputs. See the Nesting and grouping output section of the documentation for more information.
- optional()#
Allows arguments to be optional:
>>> p1 = flag("optional") >>> p = p1.optional() >>> p.parse_args("--optional") {'optional': True} >>> p.parse_args("--misspelled", allow_unparsed=True) # succeeds with no output {} >>> p1.parse_args("--misspelled") usage: --optional Expected '--optional'. Got '--misspelled'
- parse(cs)#
Applies the parser to the input sequence
cs
.
- parse_args(*args, allow_unparsed=False, check_help=True)#
The main way the user extracts parsed results from the parser.
- Parameters
args (str) – A sequence of strings to parse. If empty, defaults to
sys.argv[1:]
.allow_unparsed (bool) – Whether to cause parser to fail if there are unparsed inputs. Note that setting this to false may cause unexpected behavior when using
nonpositional()
orArgs
.check_help (bool) – Before running the parser, checks if the input string is
--help
or-h
. If it is, returns the usage message.
Examples
>>> argument("a").parse_args("-h") usage: A >>> argument("a").parse_args("--help") usage: A
- classmethod return_(a)#
This method is required to make
Parser
a Monad. It consumes none of the input and always returns a as the result. For the most part, the user will not use this method unless building custom parsers.>>> Parser.return_(Output.from_dict(some_key="some_value")).parse_args() {'some_key': 'some_value'}
- sat(predicate, on_fail)#
Applies
parser
, appliespredicate
to the result and fails if this returns false.>>> from dollar_lambda import ArgumentError >>> p = option("x", type=int).many().sat( ... lambda out: sum(out.get.values()) > 0, ... lambda out: ArgumentError(f"The values in {list(out.get.values())} must sum to more than 0."), ... ) >>> p.parse_args("-x", "-1", "-x", "1") # fails usage: [-x X ...] The values in [-1, 1] must sum to more than 0. >>> p.parse_args("-x", "-1", "-x", "2") # succeeds {'x': [-1, 2]}
- Parameters
parser (Parser[Monoid]) – The parser to apply.
predicate (Callable[[Monoid], bool]) – The predicate to apply to the result of
parser
.Parser.sat()
fails if this predicate returns false.on_fail (Callable[[Monoid], ArgumentError]) – A function producing an
ArgumentError
to return if the predicate fails. Takes the output ofparser
as an argument.
- type(f)#
A wrapper around
Parser.apply()
that simply appliesf
to the value of the most recently parsed input.>>> p1 = option("x") >> option("y") >>> p = p1.type(int) >>> p.parse_args("-x", "1", "-y", "2") # converts "1" but not "2" {'x': '1', 'y': 2}
- wrap_help(a=None)#
This checks for the
--help
or-h
flag before applyingparser
. If either of the flags is present, returns the usage message forparser
.>>> p = flag("help", help="Print this help message.").wrap_help() >>> p.parse_args("--help", check_help=False) # true by default usage: --help help: Print this help message. >>> p.parse_args("-h", check_help=False) # true by default usage: --help help: Print this help message.
We can use
Parser.wrap_help()
to print partial usage messages, e.g. for subcommands:>>> subcommand1 = matches("subcommand1") >> option("option1").wrap_help() >>> subcommand2 = matches("subcommand2") >> option("option2") >>> p = subcommand1 | subcommand2 >>> p.parse_args("subcommand1", "-h", check_help=False) usage: --option1 OPTION1 >>> p.parse_args("subcommand2", "-h", check_help=False) usage: [subcommand1 --option1 OPTION1 | subcommand2 --option2 OPTION2] Expected 'subcommand1'. Got 'subcommand2'
- classmethod zero(error=None)#
This parser always fails. This method is necessary to make
Parser
a Monoid.- Parameters
error (Optional[ArgumentError]) – Customize the error returned by
Parser.zero()
.
Examples
>>> Parser.zero().parse_args() zero >>> Parser.zero().parse_args("a") zero >>> Parser.zero(error=ArgumentError("This is a test.")).parse_args("a") This is a test.
- exception dollar_lambda.parsers.SuccessError(usage: str, input: 'Sequence[str]', output: 'NonemptyList[Parse[A_monoid]]')#
- dollar_lambda.parsers.apply(f, description)#
A shortcut for
item(description).apply(f)
.In contrast to
Parser.apply()
, this function sparesf
the trouble of outputting aResult
object. Here is an example of usage. First we define a simpleargument()
parser:>>> p1 = argument("foo") >>> p1.parse_args("bar") {'foo': 'bar'}
Here we use
f
to directly manipulate the binding generated byargument()
:>>> from dollar_lambda import apply >>> p2 = apply(lambda bar: Output.from_dict(**{bar + "e": bar + "f"}), description="baz") >>> p2.parse_args("bar") {'bare': 'barf'}
- dollar_lambda.parsers.argument(dest, nesting=True, help=None, type=None)#
Parses a single word and binds it to
dest
. Useful for positional arguments.- Parameters
dest (str) – The name of variable to bind to:
nesting (bool) – If
True
, then the parser will split the parsed output on.
yielding nested output. See Examples for more details.help (Optional[str]) – The help message to display for the option:
type (Optional[Callable[[str], Any]]) – Use the
type
argument to convert the input to a different type:
Examples
>>> from dollar_lambda import argument >>> argument("name").parse_args("Dante") {'name': 'Dante'} >>> argument("name").parse_args() usage: NAME The following arguments are required: name
Here are some examples that take advantage of
nesting=True
:>>> argument("config.name").parse_args("-h") usage: CONFIG.NAME >>> argument("config.name").parse_args("Dante") {'config': {'name': 'Dante'}}
You can disable this by setting
nesting=False
:>>> argument("config.name", nesting=False).parse_args("Dante") {'config.name': 'Dante'} >>> (argument("config.first.name") >> argument("config.last.name")).parse_args("Dante", "Alighieri") {'config': {'first': {'name': 'Dante'}, 'last': {'name': 'Alighieri'}}}
- dollar_lambda.parsers.binary_usage(a, op, b, add_brackets=True)#
Utility for generating usage strings for binary operators.
- dollar_lambda.parsers.defaults(**kwargs)#
Useful for assigning default values to arguments. It ignore the input and always returns
kwargs
converted into aSequence
ofKeyValue
pairs.defaults()
never fails:>>> from dollar_lambda import defaults >>> defaults(a=1, b=2).parse_args() {'a': 1, 'b': 2} >>> (flag("fails") | defaults(fails="succeeds")).parse_args() {'fails': 'succeeds'}
Here’s a more complex example derived from the tutorial:
>>> from dollar_lambda import nonpositional, flag, defaults, option >>> p = nonpositional( ... ( ... flag("verbose") + defaults(quiet=False) # either --verbose and default "quiet" to False ... | flag("quiet") + defaults(verbose=False) # or --quiet and default "verbose" to False ... ), ... option("x", type=int, help="the base"), ... option("y", type=int, help="the exponent"), ... ) ... >>> p.parse_args("-x", "1", "-y", "2", "--verbose") {'x': 1, 'y': 2, 'verbose': True, 'quiet': False}
- dollar_lambda.parsers.flag(dest, default=<dataclasses._MISSING_TYPE object>, help=None, nesting=True, regex=True, replace_dash=True, replace_underscores=True, short=True, string=None)#
Binds a boolean value to a variable.
>>> p = flag("verbose") >>> p.parse_args("--verbose") {'verbose': True}
- Parameters
dest (str) – The variable to which the value will be bound.
default (bool | _MISSING_TYPE) – An optional default value.
help (Optional[str]) – An optional help string.
nesting (bool) – If
True
, then the parser will split the parsed output on.
yielding nested output. See Examples for more details.regex (bool) – If
True
, then the parser will use a regex to match the flag string.replace_dash (bool) – If
True
, then the parser will replace-
with_
in the dest string in order to make dest a valid Python identifier.replace_underscores (bool) – If
True
, then the parser will replace_
with-
in the flag string.short (bool) – Whether to check for the short form of the flag, which uses a single dash and the first character of
dest
, e.g.-f
forfoo
.string (Optional[str]) – A custom string to use for the flag. Defaults to
--{dest}
.
Examples
Here is an example using the
default
parameter:>>> p = flag("verbose", default=False) >>> p.parse_args("-h") usage: --verbose verbose: (default: False) >>> p.parse_args() {'verbose': False}
By default
flag
fails when it does not receive expected input:>>> p = flag("verbose") >>> p.parse_args() usage: --verbose The following arguments are required: --verbose
Here is an example using the
help
parameter:>>> p = flag("verbose", help="Turn on verbose output.") >>> p.parse_args("-h") usage: --verbose verbose: Turn on verbose output.
Here is an example using the
replace_underscores
parameter:>>> p = flag("hello_world", replace_underscores=False).parse_args("-h") usage: --hello_world >>> p = flag("hello_world", replace_underscores=True).parse_args("-h") usage: --hello-world
Here is an example using the
short
parameter:>>> flag("verbose", short=True).parse_args("-v") # this is the default {'verbose': True} >>> flag("verbose", short=False).parse_args("-v") # fails usage: --verbose Expected '--verbose'. Got '-v'
Here is an example using the
string
parameter:>>> flag("value", string="v").parse_args("v") # note that string does not have to start with - {'value': True} >>> flag("config.value").parse_args("--config.value") {'config': {'value': True}}
- dollar_lambda.parsers.item(name, usage_name=None)#
Parses a single word and binds it to
dest
. One of the lowest level building blocks for parsers.- Parameters
usage_name (Optional[str]) – Used for generating usage text
Examples
>>> from dollar_lambda import item >>> p = item("name", usage_name="Your first name") >>> p.parse_args("Alice") {'name': 'Alice'} >>> p.parse_args() usage: name The following arguments are required: Your first name
- dollar_lambda.parsers.matches(s, peak=False, regex=True)#
Checks if the next word is
s
.>>> from dollar_lambda import matches >>> matches("hello").parse_args("hello") {'hello': 'hello'} >>> matches("hello").parse_args("goodbye") usage: hello Expected 'hello'. Got 'goodbye'
- Parameters
s (str) – The word to that input will be checked against for equality.
peak (bool) – If
False
, then the parser will consume the word and return the remaining words asunparsed
. IfTrue
, then the parser leaves theunparsed
component unchanged.regex (bool) – Whether to treat
s
as a regular expression. IfFalse
, then the parser will only succeed on string equality.
Examples
>>> p = matches("hello") >> matches("goodbye") >>> p.parse_args("hello", "goodbye") {'hello': 'hello', 'goodbye': 'goodbye'}
Look what happens when
peak=True
:>>> p = matches("hello", peak=True) >> matches("goodbye") >>> p.parse_args("hello", "goodbye") usage: hello goodbye Expected 'goodbye'. Got 'hello'
The first parser didn’t consume the word and so
"hello"
got passed on toequals("goodbye")
. But this would work:>>> p = matches("hello", peak=True) >> matches("hello") >> matches("goodbye") >>> p.parse_args("hello", "goodbye") {'hello': ['hello', 'hello'], 'goodbye': 'goodbye'}
- dollar_lambda.parsers.nonpositional(*parsers, max=80, repeated=None)#
nonpositional()
takes a sequence of parsers as arguments and attempts all permutations of them, returning the first permutations that is successful:>>> from dollar_lambda import nonpositional, flag >>> p = nonpositional(flag("verbose"), flag("quiet")) >>> p.parse_args("--verbose", "--quiet") {'verbose': True, 'quiet': True} >>> p.parse_args("--quiet", "--verbose") # reverse order also works {'quiet': True, 'verbose': True}
- Parameters
max (int) – Limits the number of times
repeated
is applied in order to prevent aRecursionError
. The default for this can be increased by either settingparser.MAX_MANY
or the environment variableDOLLAR_LAMBDA_MAX_MANY
.repeated (Optional[Parser[Sequence[Monoid]]]) – If provided, this parser gets applied repeatedly (zero or more times) at all positions.
Examples
>>> p = nonpositional(repeated=flag("x")) >>> p.parse_args() {} >>> p.parse_args("-x") {'x': True} >>> p.parse_args("-x", "-x") {'x': [True, True]}
>>> p = nonpositional(flag("y"), repeated=flag("x")) >>> p.parse_args("-y") {'y': True} >>> p.parse_args("-y", "-x") {'y': True, 'x': True} >>> p.parse_args("-x", "-y") {'x': True, 'y': True} >>> p.parse_args("-y", "-x", "-x") {'y': True, 'x': [True, True]} >>> p.parse_args("-x", "-y", "-x") {'x': [True, True], 'y': True} >>> p.parse_args("-x", "-x", "-y") {'x': [True, True], 'y': True}
>>> p = nonpositional(flag("y"), repeated=(flag("x") | flag("z")).ignore()) >>> p.parse_args("-x", "-y", "-z") {'y': True}
Stress test:
>>> p = nonpositional( ... flag("a", default=False), ... flag("b", default=False), ... flag("c", default=False), ... flag("d", default=False), ... flag("e", default=False), ... flag("f", default=False), ... flag("g", default=False), ... ) >>> p.parse_args("-g", "-f", "-e", "-d", "-c", "-b", "-a") {'g': True, 'f': True, 'e': True, 'd': True, 'c': True, 'b': True, 'a': True} >>> p.parse_args("-f", "-e", "-d", "-c", "-b", "-a") {'f': True, 'e': True, 'd': True, 'c': True, 'b': True, 'a': True, 'g': False} >>> p.parse_args("-e", "-d", "-c", "-b", "-a") {'e': True, 'd': True, 'c': True, 'b': True, 'a': True, 'f': False, 'g': False} >>> p.parse_args("-d", "-c", "-b", "-a") {'d': True, 'c': True, 'b': True, 'a': True, 'e': False, 'f': False, 'g': False} >>> p.parse_args("-c", "-b", "-a") {'c': True, 'b': True, 'a': True, 'd': False, 'e': False, 'f': False, 'g': False} >>> p.parse_args("-b", "-a") {'b': True, 'a': True, 'c': False, 'd': False, 'e': False, 'f': False, 'g': False} >>> p.parse_args("-a") {'a': True, 'b': False, 'c': False, 'd': False, 'e': False, 'f': False, 'g': False} >>> p.parse_args() {'a': False, 'b': False, 'c': False, 'd': False, 'e': False, 'f': False, 'g': False}
- dollar_lambda.parsers.option(dest, choices=None, default=<dataclasses._MISSING_TYPE object>, flag=None, help=None, nargs=1, nesting=True, regex=True, replace_dash=True, replace_underscores=True, short=True, type=<class 'str'>)#
Parses two words, binding the second to the first.
- Parameters
dest (str) – The name of variable to bind to:
choices (Optional[List[str]]) – A list of valid values for the option.
default (Any | _MISSING_TYPE) – The default value to bind on failure:
flag (Optional[str]) – The flag to use for the option. If not provided, defaults to
--{dest}
.help (Optional[str]) – The help message to display for the option:
nargs (int) – The number of space-separated arguments to parse for the option.
nesting (bool) – If
True
, then the parser will split the parsed output on.
yielding nested output. See Examples for more details.regex (bool) – If
True
, then the parser will match the flag string as a regex.replace_dash (bool) – If
True
, then the parser will replace-
with_
in the dest string in order to make dest a valid Python identifier.replace_underscores (bool) – If
True
, then the parser will replace_
with-
in the flag string.short (bool) – Whether to check for the short form of the flag, which uses a single dash and the first character of
dest
, e.g.-c
forcount
.type (Callable[[str], Any]) – Use the
type
argument to convert the input to a different type:
Examples
>>> option("count").parse_args("--count", "1") {'count': '1'}
In this example, you can see that the
flag
parameter allows the user to specify an arbitrary lead string, including one that doesn’t start with a dash.>>> option("count", flag="ct").parse_args("ct", "1") {'count': '1'}
This example demonstrates the use of the
default
parameter:>>> p = option("count", default=2) >>> p.parse_args("-h") usage: --count COUNT count: (default: 2) >>> p.parse_args() {'count': 2}
Here we specify a help-string using the
help
parameter:>>> option("count", help="The number we should count to").parse_args("-h") usage: --count COUNT count: The number we should count to
Here is an example using the
replace_underscores
parameter:>>> p = option("hello_world", replace_underscores=False).parse_args("-h") usage: --hello_world HELLO_WORLD >>> p = option("hello_world", replace_underscores=True).parse_args("-h") usage: --hello-world HELLO_WORLD
This example demonstrates the difference between
short=True
andshort=False
:>>> option("count", short=True).parse_args("-c", "1") {'count': '1'} >>> option("count", short=False).parse_args("-c", "1") usage: --count COUNT Expected '--count'. Got '-c'
As with argparse, the
type
argument allows you to convert the input to a different type using a function that takes a single string argument:>>> option("x", type=int).parse_args("-x", "1") # converts "1" to an int {'x': 1} >>> option("x", type=lambda x: int(x) + 1).parse_args("-x", "1") {'x': 2} >>> option("config.x").parse_args("--config.x", "a") {'config': {'x': 'a'}}
You can optionally use
=
as a separator between the flag and the value: >>> option(“x”, type=int, default=1).parse_args(“-x=2”) {‘x’: 2}The option can receive multiple arguments when
nargs
is greater than 1: >>> option(“x”, nargs=2).parse_args(“-x”, “1”, “2”) {‘x’: [‘1’, ‘2’]}Outputs can be constrained to a set of values using the
choices
parameter: >>> option(“x”, choices=[“a”, “b”]).parse_args(“-x”, “a”) {‘x’: ‘a’} >>> option(“x”, choices=[“a”, “b”]).parse_args(“-x”, “c”) usage: -x {a,b} argument c: raised exception invalid choice: ‘c’. Choose from [‘a’, ‘b’].
- dollar_lambda.parsers.peak(name, description=None)#
Bind the next word to a variable but keep that word in the input (so that other parsers can still see it).
- dollar_lambda.parsers.sat(predicate, on_fail, name)#
A wrapper around
Parser.sat()
that usesitem()
to parse the argument and just appliespredicate
to the value output byitem()
.>>> from dollar_lambda import sat, ArgumentError >>> p = sat(lambda x: len(x) == 1, lambda x: ArgumentError(f"'{x}' must have exactly one character."), "x") >>> p.parse_args("a") # succeeds {'x': 'a'} >>> p.parse_args("aa") # fails usage: x 'aa' must have exactly one character.
- Parameters
predicate (Callable[[A], bool]) – The predicate to apply to the result of
item()
.sat()
fails if this predicate returns false.on_fail (Callable[[A], ArgumentError]) – A function producing an
ArgumentError
to return if the predicate fails. Takes the output ofitem()
as an argument.name (str) – The value to bind the result to.
- dollar_lambda.parsers.sat_peak(predicate, on_fail, name)#
A convenience function that peaks at the next word using
peak()
and then checks if it satisfies the predicate.
Defines the @command
decorator
and the CommandTree
class.
- class dollar_lambda.decorators.CommandTree(_children=<factory>, _can_run=True)#
Allows parsers to dynamically dispatch their results based on the input. For usage details, see the CommandTree Tutorial.
- command(can_run=True, flip_bools=True, help=None, parsers=None, repeated=None, replace_underscores=True)#
A decorator for adding a function as a child of this tree.
- Parameters
can_run (bool) – Whether the parser will permit the decorated function to run if no further arguments are supplied.
flip_bools (bool) – Whether to add
--no-<argument>
before arguments that default toTrue
.help (dict) – A dictionary of help strings for the arguments.
parsers (Dict[str, Parser | List[Parser]]) – A dictionary reserving arguments for custom parsers. If the value is a list, the key must correspond to a variadic argument. See
@command
for examples.repeated (Optional[Parser[Sequence[KeyValue[Any]]]]) – If provided, this parser gets applied repeatedly (zero or more times) at all positions.
replace_underscores (bool) – If true, underscores in argument names are replaced with dashes.
Examples
With
flip_bools
set toTrue
:>>> from dollar_lambda import CommandTree >>> tree = CommandTree() ... >>> @tree.command(flip_bools=True) ... def f1(b: bool = True): ... return dict(f1=dict(b=b)) ... >>> tree("-h") usage: --no-b b: (default: True)
With
flip_bools
set toFalse
:>>> tree = CommandTree() ... >>> @tree.command(flip_bools=False) ... def f1(b: bool = True): ... return dict(f1=dict(b=b)) ... >>> tree("-h") usage: -b b: (default: True)
With
can_run
set toTrue
(the default), we can runf1
by not passing arguments for thef1
’s children:>>> tree = CommandTree() ... >>> @tree.command(can_run=True) # <- ... def f1(b: bool): ... return dict(f1=dict(b=b)) ... >>> @f1.command() ... def g1(n: int): ... return dict(g1=dict(b=b, n=n)) ... >>> tree("-h") usage: -b -n N >>> tree("-b") {'f1': {'b': True}}
With
can_run
set toFalse
, the parser will fail if the child function arguments are not supplied:>>> tree = CommandTree() ... >>> @tree.command(can_run=False) # <- ... def f1(b: bool): ... return dict(f1=dict(b=b)) ... >>> @f1.command() ... def g1(n: int): ... return dict(g1=dict(b=b, n=n)) ... >>> tree("-h") usage: -b -n N >>> tree("f1", "-b") usage: -b -n N Expected '-b'. Got 'f1'
- subcommand(can_run=True, flip_bools=True, help=None, parsers=None, repeated=None, replace_underscores=True)#
A decorator for adding a function as a child of this tree. As a subcommand, the function’s name must be invoked on the command line for the function to be called.
- Parameters
can_run (bool) – Whether the parser will permit the decorated function to run if no further arguments are supplied.
flip_bools (bool) – Whether to add
--no-<argument>
before arguments that default toTrue
.help (Dict[str, str]) – A dictionary of help strings for the arguments.
parsers (Dict[str, Parser | List[Parser]]) – A dictionary reserving arguments for custom parsers. If the value is a list, the key must correspond to a variadic argument. See
@command
for examples.repeated (Optional[Parser[Sequence[KeyValue[Any]]]]) – If provided, this parser gets applied repeatedly (zero or more times) at all positions. See
nonpositional
for examples.replace_underscores (bool) – If true, underscores in argument names are replaced with dashes.
Examples
With
flip_bools
set toTrue
:>>> tree = CommandTree() ... >>> @tree.subcommand(flip_bools=True) ... def f1(b: bool = True): ... return dict(f1=dict(b=b)) ... >>> tree("-h") usage: f1 --no-b b: (default: True)
With
flip_bools
set toFalse
:>>> tree = CommandTree() ... >>> @tree.subcommand(flip_bools=False) ... def f1(b: bool = True): ... return dict(f1=dict(b=b)) ... >>> tree("-h") usage: f1 -b b: (default: True)
With
can_run
set toTrue
(the default), we can runf1
by not passing arguments for thef1
’s children:>>> tree = CommandTree() ... >>> @tree.subcommand(can_run=True) # <- ... def f1(b: bool): ... return dict(f1=dict(b=b)) ... >>> @f1.subcommand() ... def g1(b: bool, n: int): ... return dict(g1=dict(b=b, n=n)) ... >>> tree("-h") usage: f1 -b g1 -n N >>> tree("f1", "-b") {'f1': {'b': True}}
With
can_run
set toFalse
, the parser will fail if the child function arguments are not supplied:>>> tree = CommandTree() ... >>> @tree.subcommand(can_run=False) # <- ... def f1(b: bool): ... return dict(f1=dict(b=b)) ... >>> @f1.subcommand() ... def g1(b: bool, n: int): ... return dict(g1=dict(b=b, n=n)) ... >>> tree("-h") usage: f1 -b g1 -n N >>> tree("f1", "-b") usage: f1 -b g1 -n N The following arguments are required: g1
- dollar_lambda.decorators.command(flip_bools=True, help=None, parsers=None, repeated=None, replace_underscores=True)#
A succinct way to generate a simple
nonpositional
parser.@command
derives the component parsers from the function’s signature and automatically executes the function with the parsed arguments, if parsing succeeds:>>> from dollar_lambda import command >>> @command(help=dict(a="something about a")) ... def f(a: int = 1, b: bool = False): ... print(dict(a=a, b=b)) ... >>> f("-h") usage: -a A -b a: something about a (default: 1) b: (default: False) >>> f("-a", "2", "-b") {'a': 2, 'b': True}
If the wrapped function receives no arguments (as in
f()
), the parser will takesys.argv[1:]
as the input.- Parameters
flip_bools (bool) – For boolean arguments that default to true, this changes the flag from
--{dest}
to--no-{dest}
:help (dict[str, str]) – A dictionary of help strings for the arguments.
parsers (Dict[str, Parser | List[Parser]]) – A dictionary reserving arguments for custom parsers. If the value is a list, the key must correspond to a variadic argument.
repeated (Optional[Parser[Sequence[KeyValue[Any]]]]) – If provided, this parser gets applied repeatedly (zero or more times) at all positions.
replace_underscores (bool) – If true, underscores in argument names are replaced with dashes.
Examples
>>> @command() ... def f(cuda: bool = True): ... return dict(cuda=cuda) >>> f() {'cuda': True} >>> f("--no-cuda") # flip_bools adds --no- to the flag {'cuda': False}
As the following example demonstrates, when
flip_bools=False
output can be somewhat confusing:>>> @command(flip_bools=False) ... def f(cuda: bool = True): ... return dict(cuda=cuda) >>> f("--cuda") {'cuda': False}
Here is an example using the
help
parameter:>>> @command(help=dict(quiet="Be quiet")) ... def f(quiet: bool): ... return dict(quiet=quiet) >>> f("--help") usage: --quiet quiet: Be quiet
Here is an example using the
parsers
parameter:>>> 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 thekwargs
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
The
Literal
type can also be used to constrain arguments to a finite set of values: >>> @command() … def main(x: typing.Literal[“a”, “b”]): … print(dict(x=x))This parser requires an argument of value
"a"
or"b"
: >>> main(“-x”, “a”) {‘x’: ‘a’}# >>> main(“-x”, “b”) # {‘x’: ‘b’} # >>> main(“-x”, “c”) # usage: -x X # argument c: raised exception invalid choice: ‘c’. Choose from (‘a’, ‘b’).
- dollar_lambda.decorators.parser(prefix=None, flip_bools=True, help=None, parsers=None, repeated=None, replace_underscores=True)#
Adds a
.parser
attribute to the wrapped function which can then be used in combination with other parsers. For examples, see: Grouping with the @parser decorator.- Parameters
prefix (Optional[str]) – This causes the variables bound by this parser to nest under the given prefix, as demonstrated in Grouping with the @parser decorator.
flip_bools (bool) – For boolean arguments that default to true, this changes the flag from
--{dest}
to--no-{dest}
:help (dict[str, str]) – A dictionary of help strings for the arguments.
parsers (Dict[str, Parser | List[Parser]]) – A dictionary reserving arguments for custom parsers. If the value is a list, the key must correspond to a variadic argument.
repeated (Optional[Parser[Sequence[KeyValue[Any]]]]) – If provided, this parser gets applied repeatedly (zero or more times) at all positions.
replace_underscores (bool) – If true, underscores in argument names are replaced with dashes.
Defines the Args
dataclass and associated functions.
- class dollar_lambda.args.Args#
Args
is sugar for thenonpositional
function and removes much of the boilerplate from defining parsers with many arguments.>>> from dataclasses import dataclass >>> from dollar_lambda import Args >>> @dataclass ... class MyArgs(Args): ... verbose: bool ... count: int >>> MyArgs.parse_args("--verbose", "--count", "1") {'verbose': True, 'count': 1}
MyArgs
will accept these arguments in any order:>>> MyArgs.parse_args("--count", "1", "--verbose") {'count': 1, 'verbose': True}
Note that when the default value of an argument is
True
,Args
will, by default add--no-
to the front of the flag (while still assigning the value to the original key):>>> @dataclass ... class MyArgs(Args): ... tests: bool = True >>> MyArgs.parse_args("--no-tests") {'tests': False} >>> MyArgs.parse_args() {'tests': True}
To suppress this behavior, set
flip_bools=False
:>>> MyArgs.parse_args("--tests", flip_bools=False) {'tests': False}
By using the
Args.parser()
method,Args
can take advantage of all the same combinators as other parsers:>>> from dollar_lambda import argument >>> p = MyArgs.parser() >>> p1 = p >> argument("a") >>> p1.parse_args("--no-tests", "hello") {'tests': False, 'a': 'hello'}
To supply other metadata, like
help
text or custom parsers, usefield()
:>>> from dollar_lambda import field, option >>> @dataclass ... class MyArgs(Args): ... x: int = field(default=0, help="a number") ... y: int = field( ... default=1, ... parser=option("y", type=lambda s: int(s) + 1, help="a number to increment"), ... ) >>> MyArgs.parse_args("-h") usage: -x X -y Y x: a number (default: 0) y: a number to increment
This supplies defaults for
y
when omitted:>>> MyArgs.parse_args("-x", "10") {'x': 10, 'y': 1}
It also applies the custom type to
y
when"-y"
is given>>> MyArgs.parse_args() {'y': 1, 'x': 0}
- classmethod parse_args(*args, flip_bools=True, repeated=None)#
Parses the arguments and returns a dictionary of the parsed values.
- classmethod parser(flip_bools=True, repeated=None, replace_underscores=True)#
Returns a parser for the dataclass. Converts each field to a parser (
option
orflag
depending on its type). Combines these parsers usingnonpositional
.- Parameters
Examples
>>> @dataclass ... class MyArgs(Args): ... tests: bool = True
Note the leading
--no-
:>>> MyArgs.parse_args("--no-tests") {'tests': False} >>> MyArgs.parse_args() {'tests': True}
To suppress this behavior, set
flip_bools=False
:>>> MyArgs.parse_args("--tests", flip_bools=False) {'tests': False}
- dollar_lambda.args.field(help=None, metadata=None, parser=None, **kwargs)#
This is a thin wrapper around
dataclasses.field()
.- Parameters
help (str) – An optional help string for the argument.
metadata (str) – Identical to the metadata argument for
dataclasses.field()
.type (Optional[type | Callable[[str], Any]]) – A function that takes a string and returns a value just like the
type
argument forargparse.ArgumentParser.add_argument()
.
- Return type
A
dataclasses.Field
object that can be used in place of a default argument as described in thedataclasses.Field
documentation.
Defines errors which can be raised by parsers.
- exception dollar_lambda.errors.BinaryError(usage: str, error1: dollar_lambda.errors.ArgumentError, error2: dollar_lambda.errors.ArgumentError)#