Getting Started¶
Entry Points¶
Create¶
Normally when creating an entry point for a script
(or larger application) one uses argparse.ArgumentParser
to accept arguments from the command line.
In marsh
this can be done with a single decorator over
a function. The decorator inspects the function arguments,
including their types and any descriptions that can be linked
to them from the docstring of the function.
# app.py
import marsh
@marsh.main
def run(
a: int,
b: float,
c: dict[str, bool],
) -> None:
"""Example of an entry point.
Arguments:
a: An integer argument.
b: A floating point argument.
c: A dictionary with string keys and
bool values. If this was python 3.8
we would instead use typing.Dict[str, bool] as
type hint as the builtin dict did not support
type annotations.
"""
print(a, type(a))
print(b, type(b))
print(c, type(c))
if __name__ == '__main__':
run()
Note
Mypy will report an error on the line run()
as no arguments
were supplied. Set the default value of arguments to
marsh.MISSING
to retain the same behavior from marsh
without mypy errors.
Note
Any arguments passed to run()
become new default values.
For the code above, if we call run(b=0)
the b
argument
becomes optional on the command line as there is now a default
value. This can also be accomplished by having a default value
in the function signature.
Run¶
When the decorated function is called without arguments
an argparse.ArgumentParser
is initialized and console
arguments are read in an attempt to populate the arguments
to our entry point function.
Calling the file on the command line using python allows us to pass arguments to the application.
$ python app.py a=1 b=5e-1 c.key1=true c.key2=false
1 <class 'int'>
0.5 <class 'float'>
{'key1': True, 'key2': False} <class 'dict'>
Argument Validation¶
When giving invalid values or when required arguments are missing an error message is printed and the application exits.
$ python app.py a=1.5 b=0 c.some_key=true
failed to unmarshal config: int: could not convert: 1.5
path: a
$ python app.py a=1 c.some_key=true
failed to unmarshal config: MissingValueError
path: b
Help¶
Using –help we can also get a help message for the arguments.
Here the output was piped to tail
to truncate the output into
displaying only the arguments of our entry point.
$ python app.py --help | tail
fields:
a: <int> An integer argument.
b: <float> A floating point argument.
c: {<str>: <bool>, ...}
A dictionary with string keys and bool values. If this
was python 3.8 we would instead use typing.Dict[str,
bool] as type hint as the builtin dict did not support
type annotations.
Marshal¶
Marshaling values simply means taking a python object and turning it into JSON-like data.
# marshal.py
import dataclasses
import marsh
@dataclasses.dataclass
class Config:
a: int
b: float
config = Config(1, 5e-1)
print(marsh.marshal(config))
$ python marshal.py
{'a': 1, 'b': 0.5}
Unmarshal¶
Unmarshaling is the opposite of marshaling. A type is instantiated using JSON-like data.
# unmarshal.py
import dataclasses
import typing
import marsh
class Range(typing.NamedTuple):
start: typing.Optional[int]
stop: int
@dataclasses.dataclass
class Config:
a: int
b: float
c: Range
config = marsh.unmarshal(
Config,
{
'a': 1,
'b': 1.5,
'c': {
'start': None,
'stop': 5,
},
}
)
print(config)
$ python umarshal.py
Config(a=1, b=1.5, c=Range(start=None, stop=5))