Typechecking Generators
Previously...
In our last lesson, we learned how to typecheck different kinds of methods. In this section, we'll talk about adding typechecking to one of Python's newer constructs, generators.
The Third Decorator: yields
from typecheck import yields
In addition to the accepts and returns decorators, the typecheck package offers an special yields decorator to handle generators.
Much like the returns decorator operators on the values provided to a function's return statements, yields operates on a generator's yield statements.
The following signature asserts that each object yielded by the generator is callable:
@yields(IsCallable()) def gen_adder_funcs(obj_list): for obj in obj_list: def return_obj(val): return obj + val yield return_obj raise StopIteration
Again, like returns, yields can be used in concert with accepts:
@accepts(IsCallable(), IsIterable()) @yields(Number) def map_to_int(make_int, obj_list): for obj in obj_list: yield make_int(obj) raise StopIteration
While returns can certainly be applied to generators, the result is almost certainly not what you want.
The following snippet, for example, raises a TypeCheckError, complaining that the function returned a instance of types.GeneratorType, rather than a Number.
@accepts(IsCallable(), IsIterable()) @returns(Number) def map_to_int(make_int, obj_list): for obj in obj_list: yield make_int(obj) raise StopIteration map_to_int(some_func, my_list)
YieldSeq
Now that we've gone over how to typecheck generators, there's another utility class to introduce: YieldSeq.
YieldSeq() was dreamed up as a solution to the problem of wanting your generator to return objects of different types on sequential yields. Without YieldSeq(), nasty, imprecise workarounds are required:
@yields(IsOneOf(Number, String)) def foo(): yield 5 yield 'foo' yield 6 yield 'bar'
With the YieldSeq() utility class in hand, however, you can express this in a much more type-safe way:
@yields(YieldSeq(Number, String, Number, String)) def foo(): yield 5 yield 'foo' yield 6 yield 'bar'
This approach is better because it retains a higher degree of precision as opposed to the IsOneOf()-based approach.
Next...
In our next lesson, we'll go over some best practices to keep in mind when adding typechecking to your projects.