From Kitchen Import Dishwasher: How to Use Context Managers Outside of Python

rocky

Posted by rocky

django

When dealing with files, it is important to close your file descriptor after you use them, otherwise the system might run out. In a lame language, it might look a little like this:


  $fh = fopen('foo.txt', 'r');

  // ... do stuff ...

  fclose($fh);

The problem this creates is that it is easy to forget to close a file handle. Depending on how much stuff you do to the file, the code that opens the file can get far away from the code that closes it. Despite needing to put close a file every time you open one, most languages make us do it in two steps.

Python is awesome, however, and provides us with a wonderful little idiom that means we never forget to close a file after we open one.

with open('foo.txt', 'r') as f:
      # ... do stuff ...

When you leave the with block, f is automatically closed for you. No more forgetting to close file handles ever again. Thanks Python!

What about the dishwasher?

In his keynote, Raymond Hettinger said that the with-statement is as revolutionary as the subroutine. He said that context managers are like taking the bread from the sandwich and making it reusable. So that got me thinking. If with-statements are so great, what can I do with them outside of Python, which led me to the following premise:

If we use the open context manager to clean up our files, we should be able to use them to clean up our dishes.


from contextlib import contextmanager
from kitchen import dishwasher, cabinet, sink


@contextmanager
def get_plate(size='big'):
    """
    Get a plate from the cabinet.  When you are done with it, put it in the
    dishwasher.

    Example usage:

        from kitchen import microwave
        from kitchen.fridge import leftover_pizza

        with get_plate() as plate:
            plate.add(leftover_pizza)
            microwave.nuke(plate, seconds=60)

            # eat pizza off of plate
    """
    plate = cabinet.plates[size].pop()

    yield plate

    if dishwasher.is_full and not dishwasher.is_running:
        dishwasher.run()
    elif dishwasher.is_done:
        dishwasher.empty()

    if dishwasher.is_running:
        sink.handwash(plate)
    else:
        dishwasher.add(plate)

Obviously this is a limited implementation—what if I wanted to get a fork or a glass? But I’ll leave that as an exercise for the reader. If we can get everyone to use this, then the system will never run out of clean dish descriptors.

Return to Articles & Guides