0

A certain function operation, an external service, and sometimes fails. I want to try it N times before giving up. For this reason I wrote the retry code as a decorator, and now I want to unit-test it.

I made a simple mockup function for the possibly faulty operation: the function fails with exception for N-1 times, and succeed the N-th time.

This is the code:

def mock(retry):
    def repl(*xs, **ks):
        if retry == 0:
            return 1
        retry -= 1
        raise error('boom')
    return repl

This is how I want to apply it:

somemodule.library.operation = mock(N - 1)
somemodule.run() # calls somemodule.library.operation

Unexpectedly, by running mock(N -1)() I get the following error on the if retry == 0 line:

UnboundLocalError: local variable 'retry' referenced before assignment

After trying to understand why, I figured out that the error disappears as soon as I get rid of the retry -= 1 line.

I'm a bit puzzled by this fact, since in my opinion the retry variable should fully belong to the mock scope, and not just when it is accessed in read-only. If anything, I should always get the error, not only if I modify it.

Why is this a problem? And what is in your opinion the correct way of doing it?

Note:

  • I can see this behavior both with Python2 and Python3
  • I also tried to define global retry just before the if retry == 0 conditional
Dacav
  • 13,590
  • 11
  • 60
  • 87
  • 1
    `retry` is a closure (`repl()` is a new scope, but `retry` comes from the parent scope). Are you using Python 2 or 3? You tagged with both, which isn't all that helpful. In Python 3, mark it `nonlocal`. In Python 2, you can't do what you want directly, [use a mutable closure instead](http://stackoverflow.com/questions/141642/what-limitations-have-closures-in-python-compared-to-language-x-closures). – Martijn Pieters Sep 07 '15 at 15:54
  • Have you read e.g. http://stackoverflow.com/q/18002794/3001761? There are plenty of questions on `UnboundLocalError`. – jonrsharpe Sep 07 '15 at 15:55
  • @MartijnPieters, with both python2 and 3. I updated the question. – Dacav Sep 07 '15 at 15:57
  • @Dacav: I've duplicated your post to one that answers both. – Martijn Pieters Sep 07 '15 at 15:57
  • @MartijnPieters, thanks – Dacav Sep 07 '15 at 16:00

0 Answers0