Lazyparser¶
Lazyparser is a small module that automates the creation of command-line interfaces. For this purpose, it uses argparse developped by Steven J. Bethard.
Examples¶
Without docstring¶
Let’s say you have a function print_word
that prints two words. To create a command line interface, you can simply type this in a file example.py
import lazyparser as lp
@lp.parse
def print_word(a, b):
print(a)
print(b)
if __name__ == "__main__":
print_word()
Then you can display the help of example.py
by typing:
python example.py -h # to display the help of your program
This will print the following message :
usage: example.py [-h] -a STR -b STR
Optional arguments:
-h, --help show this help message and exit
Required arguments:
-a, --a STR param a
-b, --b STR param b
If there is no docstring in the decorated print_word
function, the type of every parameters is set to str
. In addition, the full names of the parser arguments (defined with --
) correspond to the parameter names of the decorated function. The short names (called with -
) are computed on the fly and correspond to the first letter of the parameter to which they refer.
A default help message is generated for every parameter of the decorated function but it doesn’t explain what the parameter corresponds to.
To better control what the help message will display, you can write a docstring in the decorated function.
With docstring¶
Lazyparser automates the creation of command-line interfaces by taking advantage of the docstring in the decorated function. By default, lazyparser parse the docstring in PyCharm (A Python IDE) format.
Example: (file example.py
)
import lazyparser as lp
@lp.parse
def multiplication(a, b):
"""Multiply a by b
:param a: (float) a number a
:param b: (float) a number b
"""
return a * b
if __name__ == "__main__":
v = multiplication()
print(v)
Then, you can display the help of example.py
by typing:
python example.py -h # to display the help of your program
This displays the following message:
usage: example.py [-h] -a FLOAT -b FLOAT
Multiply a by b
Optional arguments:
-h, --help show this help message and exit
Required arguments:
-a, --a FLOAT a number a
-b, --b FLOAT a number b
Customize the docstring environment¶
If you are not a fan of Pycharm docstrings, you can set your own docstring environment by using the function set_env
the function set_env
takes 4 arguments :
delim1
: the string preceding the definition of a parameter. :param is the default value. This parameter can be an empty docstring if nothing precedes the parameter name in the docstring of the decorated function.
delim2
: the string that comes right after the name of the parameter. It MUST be defined and can’t be an empty string or a space, tabulation, etc…
hd
: the header preceding the argument names. By default, corresponds to an empty string.
tb
: the number of spaces at the beginning of each line in the docstring. By default, it is equal to 4.
Note
The text set before parameters definition (or the parameters definition header) is considered as being a part of the description of the function.
Warning
The type of parameters in the docstring must be surrounded by parentheses so that lazyparser can interpret them.
Here is an example of how using set_env
# code in example.py file
import lazyparser as lp
lp.set_env('', ':', "KeywordArgument")
@lp.parse
def multiplication(a, b):
"""
Multiply a by b
KeywordArgument
a : (float) a number a
b : (float) a number b
"""
return a * b
if __name__ == "__main__":
v = multiplication()
print(v)
Define the type of parameters¶
In the function docstring¶
Lazyparser can handle different types of parameters:
int
float
bool
str
: default type if nothing is specified in the function docstring.
List
: A list object used to handle lists.
The List
takes two parameters :
size
: The size of the list
vtype
: The type of the list. It must be one of the following types :
int
float
bool
str
List
don’t handle List
subtype !
Warning
The type of parameters can’t be tuple
or list
. Use the type List
for that.
An example of List
usage :¶
# code in example.py file
import lazyparser as lp
@lp.parse
def multiplication(a):
"""
Sum up the numbers given in a
:param a : (List(vtype=float)) a list of numbers
"""
return sum(a)
if __name__ == "__main__":
v = multiplication()
print(v)
Defining a list without any size allows you to give as many data as you want after the -a
in the command line interface. Those data must be separated by a space
python example.py -a 1 2 3 20
# 26.0
In the function signature¶
Lazyparser can interpret the type of parameters given in function signature. If the type of a parameter is given both in the docstring and in the signature, the type given in the signature will be used.
Example with the multiply function:
import lazyparser as lp
@lp.parse
def mutliplication(a : float, b : float):
"""
Mutiply a by b
:param a: (number) a number a
:param b: (str) a number b
"""
return a * b
if __name__ == "__main__":
v = mutliplication()
print(v)
python example.py -a 10 -b "lol"
# usage: example.py [-h] -a FLOAT -b FLOAT
# example.py: error: argument -b/--b: invalid float value: 'lol'
Lazyparser handle the type given in the function signature first. If a type is given in the function signature for a parameter, no type is needed in the docstring for this parameter.
It also works with List
objects.
It is possible to use the List
type of the typing
module! Lazyparser will automatically transform a typing.List
object into a lazyparser.List
object. With the typing.List
class, you won’t be able to limit the size of the list as it can be done with lazyparser.List(vtype=str, size=5)
or simply List(5, str)
. Note that you can use the notation of the typing package in the docstring of the decorated function. Example :param a: (List[str]) my param
. With this method, it is also not possible to limit the length of the list.
import lazyparser as lp
from typing import List
@lp.parse
def make_sum(values : List[float]): # typing typo
"""
make the sum
:param values: list of float
"""
return sum(values)
if __name__ == "__main__":
print(make_sum())
python example.py -v 10 20 30 40
Constraints¶
You can constrain the values that a parameter can take with:
@lazyparser.parse(a=[1, 2]) # the parameter a must be equal to 1 or 2
@lazyparser.parse(a=["a", "b"]) # the parameter a must be equal to "a" or "b"
@lazyparser.parse(a="file") # the parameter a must be an existing file
@lazyparser.parse(a="dir") # the parameter a must be an existing dir
@lazyparser.parse(a="2 < a < 5") # a must be greater than 2 and lower than 5
@lazyparser.parse(a="a%2 == 0") # a must be even
Note
Those constraints also apply to parameters having a List
type. For example, a constrain of a=[1, 2]
in a list a
will ensure that every element given in the command-line interface for a
is 1 or 2.
Example:¶
import lazyparser as lp
from lazyparser import List
@lp.parse(values=range(5))
def apply_sum(values : List(vtype=float)):
"""
sum every values in ``values`` parameter.
:param values: list of float
"""
return sum(values)
if __name__ == "__main__":
v = apply_sum()
print(v)
python example.py -v 10 20 30 40
# usage: example.py [-h] -v LIST[FLOAT]
# example.py: error: argument -v/--values: invalid choice: 10.0 (choose from 0, 1, 2, 3, 4)
python example.py -v 1 2 3 4
# 10.0
from lazyparser import List
import lazyparser as lp
@lp.parse(values="values % 2 == 0")
def apply_sum(values: List(vtype=float)):
"""
sum every values in ``values`` parameter.
:param values: list of float
"""
return sum(values)
if __name__ == "__main__":
v = apply_sum()
print(v)
python example.py -v 10 20 31
# usage: example.py [-h] -v LIST[FLOAT]
# example.py: error: argument -v/--values: invalid choice 31.0: it must respect : values % 2 == 0
Flag¶
Sometimes, you only want to call an argument without giving it a value when calling your program. For example, if we want to multiply a
by b
if -t (or --time)
is present in the command line or add them otherwise.
This can be done using the decorator named flag.
Here is an example :
import lazyparser as lp
@lp.flag(times=True)
@lp.parse
def flag_func(a: float, b: float, times: bool = False):
"""
:param a: a number a
:param b: a number b
"""
if times:
return a * b
else:
return a + b
if __name__ == "__main__":
v = flag_func()
print(v)
python example.py -a 10 -b 2 -t
# 20.0
python example.py -a 10 -b 2
# 12.0
Warning
If we want to use a parameter as a flag, you must give it a default value and a flag value.
Create an epilog¶
To add an epilog in the help of the parser simply use the function set_epilog
. This function must be called before the decorator parse
.
lp.set_epilog("my epilog")
Argument groups¶
By default, Lazyparser creates two groups of arguments:
Optional arguments
Required arguments
But, you may want to create argument groups with custom names.
This can be done with the function set_groups
that can takes the following arguments:
arg_groups : A dictionary having group names as keys and lists of argument names as values
order : A list of group names. Those names must be defined in
arg_groups
add_help : A boolean to indicate if you want a parameter named
help
that will display an help message in the command line interface.
This function must be called before the decorator parse
.
Note
If set_groups(add_help=False)
written in your script, then you won’t be able to display an help message in you shell.
Example¶
Below, in an file named example.py
. You can see a function that prints the name and the first name of a user and also multiply two numbers:
import lazyparser as lp
lp.set_groups(arg_groups={"User_name": ["first_name", "name"],
"Numbers": ["x", "y"]})
@lp.parse
def multiply(first_name, name, x, y):
"""Say hello name fist_name and multiply x by y.
:param first_name: (str) your first name
:param name: (str) your name
:param x: (float) a number x
:param y: (float) a number y
"""
print("Hello %s %s !" % (first_name, name))
print("%s x %s = %s" % (x, y, x * y))
if __name__ == "__main__":
multiply()
If you run:
python example.py -h
It displays:
usage: example.py [-h] -f STR -n STR -x FLOAT -y FLOAT
Say hello name fist_name and multiply x by y.
Optional arguments:
-h, --help show this help message and exit
User_name:
-f, --first_name STR your first name
-n, --name STR your name
Numbers:
-x, --x FLOAT a number x
-y, --y FLOAT a number y
If you want to change the name of Optional arguments
group, just call set_groups
like this:
lp.set_groups(arg_groups={"User_name": ["first_name", "name"],
"Numbers": ["x", "y"],
"Group_help": ["help"]})
If you want the help
argument to be in the user name groups, just call set_groups
like this:
lp.set_groups(arg_groups={"User_name": ["help", "first_name", "name"],
"Numbers": ["x", "y"]})
Note
Arguments in each group are displayed in the order of the decorated function.