How to implement retrying a lock tidily in Python?

T

tinnews

I'm writing some code that writes to a mbox file and want to retry
locking the mbox file a few times before giving up. I can't see a
really tidy way to implement this.

Currently I have something like:-

dest = mailbox.mbox(mbName, factory=None)

for tries in xrange(3):
try:
dest.lock()
#
#
# Do some stuff to the mbox file
#
dest.unlock()
break # done what we need, carry on

except mailbox.ExternalClashError:
log("Destination locked, try " + str(tries))
time.sleep(1)
# and try again

.... but this doesn't really work 'nicely' because the break after
dest.unlock() takes me to the same place as running out of the number
of tries in the for loop. I need a way to handle the case where we
run out of tries (and *haven't* done what we needed to do) separately
from the case where it worked OK.

I can see all sorts of messy ways to handle this with a flag of some
sort but is there a proper elegant way of doing it?
 
M

Matteo Landi

You can use the 'else' keyword outside the for loop:

for <condition>:
if <condition>:
break
else
<some operations>

The execution will step inside the else branch if the for loop ends
normally, i.e. without encountering a break keyword.
Hope it helps.

Regards,
Matteo
 
C

Chris Torek

I'm writing some code that writes to a mbox file and want to retry
locking the mbox file a few times before giving up. ...
dest = mailbox.mbox(mbName, factory=None)
for tries in xrange(3):
try:
dest.lock()
#
#
# Do some stuff to the mbox file
#
dest.unlock()
break # done what we need, carry on

except mailbox.ExternalClashError:
log("Destination locked, try " + str(tries))
time.sleep(1)
# and try again

... but this doesn't really work 'nicely' because the break after
dest.unlock() takes me to the same place as running out of the number
of tries in the for loop.

Seems to me the "right place" for this is a little wrapper lock as
it were:

def retried_lock(max_attempts=3):
for tries in xrange(max_attempts):
try:
self.lock()
return # got the lock
except mailbox.ExternalClashError:
log and sleep here
raise mailbox.ExternalClashError # or whatever

and now instead of dest.lock() you just do a dest.retried_lock().

Plumbing (including fitting this in as a context manager so
that you can just do "with dest" or some such) is left as an
exercise, :)
 
T

tinnews

Matteo Landi said:
You can use the 'else' keyword outside the for loop:

for <condition>:
if <condition>:
break
else
<some operations>

The execution will step inside the else branch if the for loop ends
normally, i.e. without encountering a break keyword.
Hope it helps.
Thank you! I'd looked at 'else' inside the 'try' but hadn't thought
of looking at the 'else' that goes with 'for'. That does just what I
need.
 
D

Dave Angel

I'm writing some code that writes to a mbox file and want to retry
locking the mbox file a few times before giving up. I can't see a
really tidy way to implement this.

Currently I have something like:-

dest = mailbox.mbox(mbName, factory=None)

for tries in xrange(3):
try:
dest.lock()
#
#
# Do some stuff to the mbox file
#
dest.unlock()
break # done what we need, carry on

except mailbox.ExternalClashError:
log("Destination locked, try " + str(tries))
time.sleep(1)
# and try again

... but this doesn't really work 'nicely' because the break after
dest.unlock() takes me to the same place as running out of the number
of tries in the for loop. I need a way to handle the case where we
run out of tries (and *haven't* done what we needed to do) separately
from the case where it worked OK.

I can see all sorts of messy ways to handle this with a flag of some
sort but is there a proper elegant way of doing it?
The usual idiom for telling whether you ever finished a loop is to use
the else: clause. Else is only executed if break was never executed.

for ....
if something
do...success
break
else
do ... one failure
(continue)
else:
...None of the loops succeeded

In your case you're using try/except, rather than if/else, but the outer
mechanism still holds.

DaveA
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,999
Messages
2,570,246
Members
46,839
Latest member
MartinaBur

Latest Threads

Top