Context manager

From Helpful
Jump to navigation Jump to search


Context managers bolt hook in some implied extra code, onto a code blocks, via syntactic sugar.

This is often used for some predicable, always-necessary cleanup, like doing resource management more automatically.



Python

This article/section is a stub — some half-sorted notes, not necessarily checked, not necessarily correct. Feel free to ignore, or tell me about it.


For example:

with open(filename) as file:
   file_data = file.read()

is roughly equivalent to

try:
   file = open(filename)
   file_data = file.read()
finally:
   file.close()


At its core, the with statement takes an object; that object's its

  • __enter__() will be called at the start of the block
and returns the thing that will be assignd
  • __exit__() will be called afterwards
also if an exception is raised (so is much like a try-finally)


That example above works as-is because the built-in open() already implements this (and does a close in its __exit__). (what it is actually equivalent to is down to the implementation of open)



If it didn't do that, we might add it like in the following example (as it is, the example is redundant and pointless. A better example would be a database connection, but it'd take a few more lines to actually be runnable code)

You can write this more manually like:

class Test(object):
    def __init__(self, filename):  # __init__ isn't part of concept, but is how you get parameters in 
        self.filename = filename   #  (without which you'll find it hard to get __enter__ to return something useful to you)

    def __enter__(self):
        print('__enter__')
        self.fob = open(self.filename)
        return self.fob

    def __exit__(self, exc_type,exc_value, exc_traceback):
        print('__exit__')
        self.fob.close()

with Test('textfile') as t:
    print( t )


You may prefer to use the @contextlib.contextmanager decorator, which is mild trickery where you write a generator, everything before the yield is effectively run as part of __enter__, and that exactly-one yield is what gets assigned

@contextlib.contextmanager
def test(filename): # 
    f = open(filename)
    try:
        yield f
    finally:
        f.close()

with test('textfile') as t:
    print( t )


Notes:

  • there is also a way to ease creating a mixin

Javascript

This article/section is a stub — some half-sorted notes, not necessarily checked, not necessarily correct. Feel free to ignore, or tell me about it.

Nothing built in, but because ES6 has generators(good support since ~2017) you can insert both some before and after code, much like that second python example.

To copy-paste from [1]:

function* usingGroup() {
   console.group()
   try {
     yield;
   } finally {
     console.groupEnd();
   }
}

for(const _ of usingGroup()) {
   console.log('inside a group')
}

The same page also mentions a library to make this a little easier, imitating python.

C#

This article/section is a stub — some half-sorted notes, not necessarily checked, not necessarily correct. Feel free to ignore, or tell me about it.