Input parameter annotations¶
Concept¶
If you call a horetu interface on a function with input annotations, the input annotations are be treated as types.
You can usually think of the annotation as a callable that takes one argument.
(It is in fact more complicated, as discussed in Kinds of annotations.)
horetu calls the annotation on the raw input value, usually a str,
and passes the returned value to as an input to the function whose parameter was
annotated.
Consider this function with an input annotation.
def logarithm(number: float):
return math.log(number, 10)
When run with horetu, it works very similarly to this next function.
def logarithm(number):
return math.log(float(number), 10)
The difference is only in the exception handling. If the number is converted to
a float without error, the resulting programs will work the same. If calling
float on the number raises an error, however, the first version will
print the ordinary Python traceback, and the second version will produce an
error that is appropriate for the specified interface.
Kinds of annotations¶
Let us group annotations into three kinds, which are all converted internally
into horetu.annotations.Annotation subclasses.
- Special objects and the empty annotation
horetu.annotations.Annotationsubclasses- Other callables
I discuss these in reverse order, because I think that is easiest.
Other callables¶
I have pretty much explained how other callables work already (Concept):
- They parse raw input values.
- The parsed values are used as function inputs.
- Errors are printed nicely.
Annotations¶
horetu.annotations.Annotation subclasses are conceptually similar,
with only extra capabilities.
- They can dump function inputs back to raw input values, which is necessary for
some interfaces. For example,
horetu.config_default()uses this to generate default configuration files. - They can load and dump from non-
strsources. For example,horetu.wsgi_form()uses this to create web form widgets, parse load uploaded files, and populate default values for the web form.
Here are some horetu.annotations.Annotation subclasses.
-
class
horetu.annotations.Bytes(loads=<function Bytes.<lambda>>, dumps=<function Bytes.<lambda>>, encoding='ascii')[source]¶
-
horetu.annotations.Decimal¶
-
horetu.annotations.Integer¶
-
horetu.annotations.Float¶
-
class
horetu.annotations.Range(minimum, maximum, loads=<class 'float'>, dumps=<class 'str'>, step='any')[source]¶
-
horetu.annotations.InputFile¶
-
horetu.annotations.InputBinaryFile¶
-
horetu.annotations.OutputFile¶
-
horetu.annotations.OutputBinaryFile¶
Special objects and the empty annotation¶
Special objects are all shorthand for
horetu.annotations.Annotation subclasses.
Some are direct replacements.
boolis converted tohoretu.annotations.Boolean.bytesis converted tohoretu.annotations.Bytes.decimal.Decimalis converted tohoretu.annotations.Decimal.floatis converted tohoretu.annotations.Float.intis converted tohoretu.annotations.Integer.stris converted tohoretu.annotations.Identity.
And if no annotation is set, horetu.annotations.Identity
is used.
Passing a tuple of str elements as an annotation (an
instance of tuple, not the tuple class itself) indicates
that only certain values are allowed as input. It is internally converted to a
horetu.annotations.Factor object.
def validate(x: ('elephant', 'giraffe')):
return x
A dict with str keys is converted to a
horetu.annotations.FactorMapping object, which works similarly to
a horetu.annotations.Factor object: Only the keys in the dictionary
are allowed as inputs, and the corresponding dictionary value (rather than the
original input) is used as the function input.
def validate(x: {'elephant': 1, 'giraffe': 2}):
return x*10
The annotation can be a list of one element, with the one element being a valid
annotation itself. This is treated like if the element were the annotation,
except that many values for the parameter may be specified and that these values
will be combined into one list and passed as a single input to the function.
For example, if the horetu.wsgi_form() interface were called on this
function, the resulting form would allow for the selection of values and would
enforce that each be either “elephant” or “giraffe”.
def validate(xs: [{'elephant': 1, 'giraffe': 2}]):
for x in xs:
yield x*10
Annotations with dumps and loads or dump and load methods
are used as horetu.annotations.Encoder type.
import json
def read_thingy(thingy: json):
print(thingy['documents'])
I still need to document horetu.annotations.Help and
horetu.annotations.Config.
Other argument characteristics sometimes matter¶
Other characteristics of arguments sometimes matter in determining the
annotation. They usually only imposes constraints. For example,
horetu.annotations.Flag annotations can only be set on keyword
arguments, and the default value must be False.
The main place where it affects the annotation is when a parameter has a default
of False and no annotation; in this case, the annotation is set to
horetu.annotations.Boolean.