[Previously, on context managers](/post/deconstructing-context-managers): ```python @contextlib.contextmanager def context_manager(): # Setup. try: # Do stuff. finally: # Teardown. ``` That's the template. Let's fill it with mischief. ![pause](/media/images/pause.png) The first example is simple, useful, and I have no idea why it isn't part of the standard library. You have the ``mkstemp`` function to create temporary files and the ``mkdtemp`` function to create temporary directories, right? But temporary things need to be cleaned up, so you also have the ``TemporaryFile`` and ``TemporaryDirectory`` context managers. Except you don't have the ``TemporaryDirectory`` context manager. And that's a pity: not only because it would be useful, but also because it kind of breaks Python's style: good languages are intuitive, and intuition is often about symmetry. If we have ``foo_add`` and ``bar_add``, and then we have ``foo_sub``, we'd expect to have ``bar_sub`` too, right? We'd *extrapolate*, and in this case our extrapolation is wrong, our intuition fails us, and we end up frustrated and sad. Also, we need a temporary directory. ```python @contextlib.contextmanager def TemporaryDirectory(): temporary_directory_path = tempfile.mkdtemp() try: yield temporary_directory_path finally: shutil.rmtree(temporary_directory_path) ``` And the usage: ```python with TemporaryDirectory() as tempdir: # Create subdirectory... d = os.path.join(tempdir, 'dir') # Create file... f = os.path.join(tempdir, 'file.txt') # Do stuff # And that's it. assert not os.path.exists(tempdir) ``` Remember the symmetry I was going on about before? I usually like to call my functions (and therefore my context managers) with lowercase underscored names, like ``temporary_directory``. But what would you expected to go with ``TemporaryFile``? The code's on [GitHub](https://github.com/dan-gittik/temporarydirectory). ![pause](/media/images/pause.png) Sometimes we want to suppress exceptions, but we don't *really* want to suppress exceptions. Like, we want to know they happened, we just don't want them to propagate. And sometimes we don't want to know they happened. And sometimes we do want them to propagate. We're fickle like that. ```python @contextlib.contextmanager def as_warning(message, warning_class): try: yield except: warnings.warn(message + os.linesep + traceback.format_exc(), warning_class) ``` The ``aswarning`` context manager turns exceptions into warnings; we can even chose a custom message and the warning class, and get a nice traceback along with it. With exceptions as warnings, we get notified they happen, but they don't propagate. And if we don't want to get notified, we call ``warnings.simplefilter('ignore')``; and if we do want it to propagate, we call ``warnings.simplefilter('error')``. The Usage: ```python >>> class MyWarning(Warning): ... pass >>> with as_warning('something went wrong', MyWarning): ... raise ValueError('foo') ... aswarning.py:16: MyWarning: something went wrong Traceback (most recent call last): File "aswarning.py", line 14, in as_warning yield File "<stdin>", line 2, in <module> ValueError: foo ``` And the [code](https://github.com/dan-gittik/aswarning). ![pause](/media/images/pause.png) Let's say we're scraping a website that does *not* want to be scraped, so after several attempts it blocks our IP. "Game on," we say, and start routing each request through a different TOR node, so each request has a different source IP address (at least, as far as the website is concerned). But it's quite some work, and if it's the second time we're doing this already, we should probably make a script of it. First, let's start with a simple proxy. The [PySocks](https://github.com/Anorov/PySocks) package makes this a breeze: ```python @contextlib.contextmanager def proxy(host, port): old_socket = socket.socket try: socks.set_default_proxy(socks.SOCKS5, host, port) socket.socket = socks.socksocket yield finally: socket.socket = old_socket ``` This replaces the Python's standard ``socket`` with PySock's socket, which is routed via the proxy at the specified host and port. And we want that proxy to be TOR. Luckily, there's a package for it as well: [Stem](https://stem.torproject.org/). It's somewhat cumbersome, but it gets the job done, and we can even throw in our country of choice, just for fun. Here: ```python @contextlib.contextmanager def tor_proxy(country, port): tor_process = stem.process.launch_tor_with_config( config = { 'SocksPort': str(port), 'ExitNodes': '{%s}' % country, }, ) try: with proxy(host='127.0.0.1', port=port): yield finally: tor_process.kill() ``` You can add ``init_msg_handler = print`` to the ``launch_tor_with_config`` function call so everything is printed to screen, so you can feel more like an ├╝ber 7331 h4x0r. Just look at how pretty this code is: ```python with tor_proxy('ru', 7000): response = requests.get('http://ip-api.com/json') assert response.json()['country'] == 'Russia' ``` Bam! [Code](https://github.com/dan-gittik/proxy).