• 6
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

name Punditsdkoslkdosdkoskdo

Python with…as for custom context manager

I wrote a simple context manager in Python for handling unit tests (and to try to learn context managers):

class TestContext(object):
    test_count=1
    def __init__(self):
        self.test_number = TestContext.test_count
        TestContext.test_count += 1

    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_value, exc_traceback):
        if exc_value == None:
            print 'Test %d passed' %self.test_number
        else:
            print 'Test %d failed: %s' %(self.test_number, exc_value)
        return True

If I write a test as follows, everything works okay.

test = TestContext()
with test:
   print 'running test %d....' %test.test_number
   raise Exception('this test failed')

However, if I try to use with...as, I don't get a reference to the TestContext() object. Running this:

with TestContext() as t:
    print t.test_number

Raises the exception 'NoneType' object has no attribute 'test_number'.

Where am I going wrong?

Assuming that you need to access the context manager created in the with statement, __enter__ needs to return self. If you don't need to access it, __enter__ can return whatever you would like.

The with statement will bind this method’s return value to the target(s) specified in the as clause of the statement, if any.

This will work.

class TestContext(object):
    test_count=1
    def __init__(self):
        self.test_number = TestContext.test_count
        TestContext.test_count += 1

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, exc_traceback):
        if exc_value == None:
            print 'Test %d passed' % self.test_number
        else:
            print 'Test %d failed: %s' % (self.test_number, exc_value)
        return True
  • 34
Reply Report
      • 1
    • Note that there is no requirement for a context manager to return self. It is a very handy default, so that you can create the context manager in the with statement, but a context manager is free to return whatever it likes from __enter__. For the specific usecase of the OP, where the TestContext() is created in the with statement, returning self is indeed the way to go.
def __enter__(self):
    return self

will make it work. The value returned from this method will be assigned to the as variable.

See also the Python doc:

If a target was included in the with statement, the return value from __enter__() is assigned to it.

If you only need the number, you can even change the context manager's logic to

class TestContext(object):
    test_count=1
    def __init__(self):
        self.test_number = TestContext.test_count
        TestContext.test_count += 1

    def __enter__(self):
        return self.test_number

    def __exit__(self, exc_type, exc_value, exc_traceback):
        if exc_value == None:
            print 'Test %d passed' % self.test_number
        else:
            print 'Test %d failed: %s' % (self.test_number, exc_value)
        return True

and then do

with TestContext() as test_number:
    print test_number
  • 10
Reply Report

Trending Tags