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.Annotation
subclasses- 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-
str
sources. 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.
bool
is converted tohoretu.annotations.Boolean
.bytes
is converted tohoretu.annotations.Bytes
.decimal.Decimal
is converted tohoretu.annotations.Decimal
.float
is converted tohoretu.annotations.Float
.int
is converted tohoretu.annotations.Integer
.str
is 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
.