Normalize decorator¶
This section discusses the purpose of the @normalize decorator, shows how to use it and provides the reference documentation.
Purpose (discussion)¶
When sharing code, a large amount of code is usually dedicated to processing the arguments of the functions or methods to check their value and normalize it to a standard format.
The Python language offers the ability to accept a large
range of input type on a unique function through duck typing.
This leads to better integration of the different objects
at stake, for instance using an object such as xarray.Dataset
or a pandas.DataFrame
or pandas.Serie
as input
to provide a list of dates.
CliMetLab offers predefined shortcuts to
implement this. The short API aims to address 80% of
the use cases using the @normalize
decorator.
The longer form aims to tackle specific needs.
Compare the following codes snippets:
Boilerplate code
Tedious and error-prone Python code is needed to check and normalize the values of the function arguments given by the user.
def __init__(self, date, option):
if date is None:
date = DEFAULT_DATE_LIST
if isinstance(date, tuple):
date = list(date)
if not isinstance(date, list):
date = [date]
for d in date:
check_date_is_sunday(d)
if not option in VALID_OPTIONS:
raise ValueError(f"option={option} invalid")
(...)
(more checks and transformations)
(...)
do_stuff(date, option)
Using CliMetLab short form API
The decorator @normalize provides generic default behaviour to handle domain-specific arguments (for dates, meteorological and climate parameters, bounding boxes, etc.)
from climetlab.decorators import normalize
@normalize("date","date(%Y%m%d)")
@normalize("option",["foo", "bar"])
def __init__(self, date, option):
do_suff(date, option)
How to use¶
How to ensure that the value in the function belongs to a list?
from climetlab.decorators import normalize @normalize("param", ["tp", "gh"]) def f(self, param): print(param)
How to ensure that the value in the function is a date with this format “YYYY-MM-DD”?
from climetlab.decorators import normalize @normalize("option", "date(%Y-%m-%d)") def f(option): return option assert f(option="2022-12-31") == "2022-12-31" assert f(option="20221231") == "2022-12-31" assert f(option=20221231) == "2022-12-31"
How to ensure that the value in the function is a list?
Add the keyword argument
multiple=True
. Not available forbounding-box
.How to ensure that the value in the function is a list of int?
from climetlab.decorators import normalize @normalize("option", "int", multiple=True) def f(option): return option # Alternative shorter version @normalize("option", "int-list") def g(option): return option assert f(option="2022") == [2022] assert g(option="2022") == [2022] assert f(option=[48, 72.0, "96"]) == [48, 72, 96] assert g(option=[48, 72.0, "96"]) == [48, 72, 96]
How to ensure that the value in the function is not a list?
Add the keyword argument
multiple=False
.How to accept list or non-list as input?
Add the keyword argument
multiple=None
. Not available forbounding-box
.How to add alias/shortcuts/special values to be replaced by actual predefined values?
Use the keyword argument
alias
and provide a dictionary.from climetlab.decorators import normalize @normalize("param", ["tp", "gh"]) def f(param): return param assert f(param="tp") == "tp" # f(param="t2m") # fails
from climetlab.decorators import normalize DATES = dict( april=["20210401", "20210402", "20210403"], june=["20210610", "20210611"], ) @normalize("x", "date-list(%Y%m%d)", aliases=DATES) def f(x): return x assert f("2021-06-10") == ["20210610"] assert f("june") == ["20210610", "20210611"] assert f("1999-01-01") == ["19990101"]
Reference¶
Warning
This API is experimental, things may change.
@normalize(name, values, aliases={}, multiple=None, **kwargs)
The @normalize
decorator transforms the arguments provided when calling
the decorated function, modifies it if needed, and provides a normalised
value to the function. It ensures that the value of the argument is what
is expected to be processed by the function.
- values
If values is a list, the list provides allowed values for the parameter. If values is a string, it is expected to be a shortcut similar to “type(options)” where type is one of the following:
"date"
,"date-list"
,"bounding-box"
. These shorts cut aims at providing an easy way to define many options in a more concise manner.Example:
"date-list(%Y%m%d)"
- type
Type of value expected by the function. The type should be one of the following:
"str"
,"int"
,"float"
,"date"
,"date-list"
,"str-list"
,"int-list"
,"float-list"
.- format
The keyword argument format is available for type =’date’ and ‘date-list’. It provides the expected format according to datetime.strftime. Example: format=’%Y%m%d’
- convention
Experimental. To be documented.
- aliases
Replace a value with another using a dictionary of aliases.
- multiple
The keyword argument multiple is not available for
bounding-box
.True: Ensure a list value. Turn input into a list if needed.
False: Ensure a non-list value. Turn a list input as non-list if the list has only one element. Fails with ValueError if the list has more than one element.
None: Accept list and non-list values without transformations.