Question

Is there a better way than using globals to get interesting values from a context manager?

@contextmanager
def transaction():
    global successCount
    global errorCount
    try:
        yield
    except:
        storage.store.rollback()
        errorCount += 1
    else:
        storage.store.commit()
        successCount += 1

Other possibilities:

  • singletons

    sort of globals...

  • tuple as an argument to the context manager

    makes the function more specific to a problem /less reusable

  • instance that holds the specific attributes as an argument to the context manager

    same problems as the tuple, but more legible

  • raise an exception at the end of the context manager holding the values.

    really bad idea

Was it helpful?

Solution

See http://docs.python.org/reference/datamodel.html#context-managers

Create a class which holds the success and error counts, and which implements the __enter__ and __exit__ methods.

OTHER TIPS

I still think you should be creating a class to hold you error/success counts, as I said in you last question. I'm guessing you have your own class, so just add something like this to it:

class transaction:
    def __init__(self):
        self.errorCount = 0
        self.successCount = 0  

    def __enter__(*args):
        pass  

    def __exit__(self, type, value, traceback):
        if type:
            storage.store.rollback()
            self.errorCount += 1
        else:
            storage.store.commit()
            self.successCount += 1

(type is None if there are no exceptions once invoking the contextmanager)

And then you probably are already using this somewhere, which will invoke the contextmanager and run your __exit__() code. Edit: As Eli commented, only create a new transaction instance when you want to reset the coutners.

t = transaction()
for q in queries:
    with t:
        t.execute(q)

"tuple as an argument to the context manager

makes the function more specific to a problem /less reusable"

False.

This makes the context manager retain state.

If you don't implement anything more than this, it will be reusable.

However, you can't actually use a tuple because it's immutable. You need some mutable collection. Dictionaries and class definitions come to mind.

Consequently, the recommended implementation is

"instance that holds the specific attributes as an argument to the context manager"

A simple class definition with two attributes is all you need. However, your transaction status is stateful and you need to retain state somewhere.

class Counters(dict):
    SUCCEED= 0
    FAIL= 1
    def __init__( self ):
        self[ self.SUCCEED ]= 0
        self[ self.FAIL ]= 0 
    def increment( self, status ):
        self[status] += 1

class Transaction(object):
    def __init__( self, worker, counters ):
        self.worker= worker
        self.counters= counters
    def __enter__( self ):
        self.counters.status= None
    def process( self, *args, **kw ):
        status= self.worker.execute( *args, **kw )
        self.counters.increment( status )
    def __exit__( self ):
        pass

counts= Counters()
for q in queryList:
    with Transaction(execQuery,counts) as t:
        t.process( q )
print counts
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top