A Couple Handy Context Managers

Context managers can be used in a number of ways – the classic is to manage resources - that is, close files and the like. But they are also useful for other handy things when you want to run some code before and after a block of code, or handle exceptions in special way.

Timing Context Manager

This is an example of running some code before and after the enclosed block.

Create a context manager that will print the elapsed time taken to run all the code inside the context:

In [3]: with Timer() as t:
   ...:     for i in range(100000):
   ...:         i = i ** 20
   ...:
This code took 0.206805 seconds

NOTE: the time module has what you need:

import time

start = time.clock()
# some code here
elapsed = time.clock() - start

time.clock() returns the number of seconds that this process has been running. You can also use time.time(), which gives the “wall time”, rather than the process time. time() will vary more depending on how busy the system is. But you may want to use it if you want to measure how long it takes to download something, for instance.

Extra Credit

Allow the Timer context manager to take a file-like object as an argument (the default should be sys.stdout). The results of the timing should be printed to the file-like object. You could also pass in a name for this particular context, so the message in the file-like object is labeled – kind of a poor man’s logging system.

Extra Extra Credit

Implement this as a generator, wrapped by the:

contextlib.contextmanager

decorator.

The pytest error handler

pytest come with a nifty context manager for testing for error conditions:

with pytest.raises(ZeroDivisionError)
    5 / 0

The test should pass

But:

with pytest.raises(ZeroDivisionError)
    5 / 2

This test should fail – no Exception occurred.

And so should this one:

with pytest.raises(ValueError)
    5 / 0

the wrong Exception occurred.

You task is to write a similar context manager (yes, it’s already written, but this should help you understand how to handle exceptions in context managers…)

Extra Credit

The pytest version has a few other features:

[https://docs.pytest.org/en/latest/assert.html#assertions-about-expected-exceptions]

See if you can implement a few of them….

Hints:

tests fail when an assert fails:

assert some_expression, "a message"

you get a failure when some_expression evaluates as false.

This is more-or-less the same as this code:

if some_expression:
    raise AssertionError("a message")

The reason it exists is not so much to save a bit of typing (though that’s nice), but that assertions are designed for tests, and thus can be turned off for an entire python process – and, indeed are turned off when you turn on optimization.

So in your context manager, you can raise an AssertionError, or force one with an assert:

assert False, "a message"

either will work fine.

See: raising_an_assert.py