.. getthecode:: pep-342-example.py :language: python3 :hidden: ======================================================= PEP 342 -- Coroutines via Enhanced Generators Example ======================================================= This article provides a full Python 3 example illustrating the use of coroutines via enhanced generators as described in the `PEP 342 document `_. The PEP 342 proposes some enhancements to the API and syntax of generators, to make them usable as simple coroutines. This PEP proposal was implemented in Python 2.5. The code given as example in this document is not working as is. You will find here two real implementations of the PEP 342's example for Python 3, the first one is implemented without coroutines to serve as reference and the second one makes use of coroutines. This example implements a thumbnail pager. Common parts ------------ .. code-block:: py3 import os import tempfile We define two classes: .. code-block:: py3 class PageSize: def __init__(self, x, y): self.x = x self.y = y def __truediv__(self, other): return int(self.x / other.x), int(self.y / other.y) def __repr__(self): return "PageSize {}x{}".format(self.x, self.y) class Image: def __init__(self, name, page_size): self.name = name self.page_size = page_size def __repr__(self): return 'Image {}'.format(self.name) def paste(self, image, x, y): print("paste {} in {} at ({}, {})".format(image.name, self.name, x, y)) def create_thumbnail(self, thumb_size): print('create_thumbnail', self.name) return self.__class__('thumb_' + self.name, thumb_size) def write_image(self, filename): print('write_image', self.name, filename, '\n') And a function to run the example: .. code-block:: py3 def write_thumbnails(thumbnail_pager, jpeg_writer, page_size, thumb_size, images, output_dir): pipeline = thumbnail_pager(page_size, thumb_size, jpeg_writer(output_dir)) for image in images: print("send", image.name) pipeline.send(image) print("close") pipeline.close() def run_exemple(thumbnail_pager, jpeg_writer): with tempfile.TemporaryDirectory() as output_dir: write_thumbnails( thumbnail_pager, jpeg_writer, page_size=PageSize(200, 300), thumb_size=PageSize(100, 100), images=[Image("image{}".format(i), None) for i in range(10)], output_dir=output_dir) Basic Implementation -------------------- .. code-block:: py3 class JpegWriter: def __init__(self, dirname): self.dirname = dirname self.file_number = 1 def send(self, image): filename = os.path.join(self.dirname, "page%04d.jpg" % self.file_number) Image.write_image(image, filename) self.file_number += 1 class ThumbnailPager: def __init__(self, page_size, thumb_size, destination): self.thumb_size = thumb_size self.destination = destination self.page = Image('page', page_size) self.rows, self.columns = page_size / thumb_size self.current_row, self.current_column = 0, 0 self.pending = False def send(self, image): thumb = image.create_thumbnail(self.thumb_size) self.page.paste(thumb, self.current_column*self.thumb_size.x, self.current_row*self.thumb_size.y) self.pending = True self.current_column += 1 if self.current_column == self.columns: self.current_row += 1 if self.current_row == self.rows: self.destination.send(self.page) self.current_row, self.current_column = 0, 0 self.pending = False else: self.current_column = 0 def close(self): if self.pending: self.destination.send(self.page) run_exemple(ThumbnailPager, JpegWriter) .. code-block:: none send image0 create_thumbnail image0 paste thumb_image0 in page at (0, 0) send image1 create_thumbnail image1 paste thumb_image1 in page at (100, 0) send image2 create_thumbnail image2 paste thumb_image2 in page at (200, 0) send image3 create_thumbnail image3 paste thumb_image3 in page at (0, 100) send image4 create_thumbnail image4 paste thumb_image4 in page at (100, 100) send image5 create_thumbnail image5 paste thumb_image5 in page at (200, 100) write_image page /tmp/tmpnc5fq45t/page0001.jpg send image6 create_thumbnail image6 paste thumb_image6 in page at (0, 0) send image7 create_thumbnail image7 paste thumb_image7 in page at (100, 0) send image8 create_thumbnail image8 paste thumb_image8 in page at (200, 0) send image9 create_thumbnail image9 paste thumb_image9 in page at (0, 100) close write_image page /tmp/tmpnc5fq45t/page0002.jpg Implementation using coroutines ------------------------------- .. code-block:: py3 def consumer(func): def wrapper(*args, **kwargs): generator = func(*args, **kwargs) next(generator) return generator wrapper.__name__ = func.__name__ wrapper.__dict__ = func.__dict__ wrapper.__doc__ = func.__doc__ return wrapper @consumer def jpeg_writer(dirname): file_number = 1 while True: filename = os.path.join(dirname, "page%04d.jpg" % file_number) (yield).write_image(filename) file_number += 1 @consumer def thumbnail_pager(page_size, thumb_size, destination): while True: page = Image('page', page_size) rows, columns = page_size / thumb_size pending = False try: for row in range(rows): for column in range(columns): thumb = (yield).create_thumbnail(thumb_size) page.paste(thumb, column*thumb_size.x, row*thumb_size.y) pending = True except GeneratorExit: # close() was called, so flush any pending output if pending: destination.send(page) # then close the downstream consumer, and exit destination.close() return else: # we finished a page full of thumbnails, so send it downstream and keep on looping destination.send(page) run_exemple(thumbnail_pager, jpeg_writer) .. code-block:: none send image0 create_thumbnail image0 paste thumb_image0 in page at (0, 0) send image1 create_thumbnail image1 paste thumb_image1 in page at (100, 0) send image2 create_thumbnail image2 paste thumb_image2 in page at (200, 0) send image3 create_thumbnail image3 paste thumb_image3 in page at (0, 100) send image4 create_thumbnail image4 paste thumb_image4 in page at (100, 100) send image5 create_thumbnail image5 paste thumb_image5 in page at (200, 100) write_image page /tmp/tmpbk01q5_h/page0001.jpg send image6 create_thumbnail image6 paste thumb_image6 in page at (0, 0) send image7 create_thumbnail image7 paste thumb_image7 in page at (100, 0) send image8 create_thumbnail image8 paste thumb_image8 in page at (200, 0) send image9 create_thumbnail image9 paste thumb_image9 in page at (0, 100) close write_image page /tmp/tmpbk01q5_h/page0002.jpg