How should I return interesting values from a with-statement?
-
22-08-2019 - |
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
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