After reading Eli Bendersky's article on implementing state machines via Python coroutines I wanted to...

see his example run under Python3

and also add the appropriate type annotations for the generators

I succeeded in doing the first part (but without using async def s or yield from s, I basically just ported the code - so any improvements there are most welcome).

But I need some help with the type annotations of the coroutines:

#!/usr/bin/env python3 from typing import Callable, Generator def unwrap_protocol(header: int=0x61, footer: int=0x62, dle: int=0xAB, after_dle_func: Callable[[int], int]=lambda x: x, target: Generator=None) -> Generator: """ Simplified protocol unwrapping co-routine.""" # # Outer loop looking for a frame header # while True: byte = (yield) frame = [] # type: List[int] if byte == header: # # Capture the full frame # while True: byte = (yield) if byte == footer: target.send(frame) break elif byte == dle: byte = (yield) frame.append(after_dle_func(byte)) else: frame.append(byte) def frame_receiver() -> Generator: """ A simple co-routine "sink" for receiving full frames.""" while True: frame = (yield) print('Got frame:', ''.join('%02x' % x for x in frame)) bytestream = bytes( bytearray((0x70, 0x24, 0x61, 0x99, 0xAF, 0xD1, 0x62, 0x56, 0x62, 0x61, 0xAB, 0xAB, 0x14, 0x62, 0x7))) frame_consumer = frame_receiver() next(frame_consumer) # Get to the yield unwrapper = unwrap_protocol(target=frame_consumer) next(unwrapper) # Get to the yield for byte in bytestream: unwrapper.send(byte)

This runs properly...

$ ./decoder.py Got frame: 99afd1 Got frame: ab14

...and also typechecks:

$ mypy --disallow-untyped-defs decoder.py $

But I am pretty sure I can do better than just use the Generator base class in the type specs (just as I did for the Callable ). I know it takes 3 type parameters ( Generator[A,B,C] ), but I am not sure how exactly they'd be specified here.

Any help most welcome.