Rerun code block in Python
<p>I was looking to try to figure out a way to write something like this:</p>

<pre><code>code_that_should_not_be_run_again()
with rerun_code_that_may_fail():
  another_method(x)
  run_me_again_if_i_fail(y)
code_that_should_only_be_run_if_above_succeeds()
</code></pre>

<p>Where the above would run that code, and catch an exception, if it was caught, then try to run the code, again. here would be a longer version of what I want:</p>

<pre><code>code_that_should_not_be_run_again()
try:
  another_method(x)
  run_me_again_if_i_fail(y)
catch Exception:
  try:
    another_method(x)
    run_me_again_if_i_fail(y)
  catch Exception:
    raise Exception("Couldn't run")
 code_that_should_only_be_run_if_above_succeeds()
</code></pre>

<p>I was thinking I could probably use a generator, maybe catch that yielded content in to a lambda and then run it twice, somehow, but now sure how I can code something like this.</p>

<p>Is this possible in Python? Or maybe something similar to this that can be done?</p>

<p>Here's what I've tried so far:</p>

<pre><code>from contextlib import contextmanager
@contextmanager
def run_code():
    print 'will run'
    try:
      yield
    except SomeException:
      try:
        yield
      except SomeException:
        raise SomeException('couldn't run')
</code></pre>

<p><em>Edit</em> Python wont' let you do what I want to do, so you can only use decorators on functions :(</p>

<p>I hope someone posts a better solution than this but I would use exactly that method, and perhaps with a decorator:</p>

<pre><code>def retry_if_fails(fn, exception=Exception, *args, **kwargs):
    try:
        fn(*args, **kwargs)
    except exception:
        fn(*args, **kwargs)  # if this fails again here, the exception bubbles up
</code></pre>

<p>Of course, the issue is that you are only calling one function in <code>retry_if_fails</code> and not a two-stepper like what you've done.</p>

<p>You could create a list of functions and pass that in along with a separate list of lists for the arguments to each of the functions you want handled.</p>

<pre><code>def retry_funcs(fns, all_args, all_kwargs, exception=Exception):
    # all_args is a list of lists
    # all_kwargs is a list of dicts
    try:
        for fn, args, kwargs in zip(fns, all_args, all_kwargs):
            fn(*args, **kwargs)
    except exception:
        for fn, args, kwargs in zip(fns, all_args, all_kwargs):
            fn(*args, **kwargs)
</code></pre>

<p>In this one, the list of lists for <code>args</code> and list of dicts for <code>kwargs</code> have to match up by order. An empty list or empty dict in either <code>all_args</code> or <code>all_kwargs</code> would enable you to not pass any args to a particular function, or only args, or only kwargs, or both.</p>

<pre><code>fns = [some_func, another_func]
all_args = [['a', 'b'],  # args for some_func
            []           # no args for another_func
           ]
all_kwargs = [{'param': 'something'}, # kwargs for some_func
              {}                      # no kwargs
             ]
</code></pre>

<p>And instead of the list of <code>funcs</code>, <code>args</code> and <code>kwargs</code> being different, it might be easier to just put them in together like what the result of <code>zip</code>-ing would be, since that's how you'd know code in the calls anyway:</p>

<pre><code>fns_with_args_and_kwargs = [(some_func, ['a', 'b'], {'param': 'something'}),
                            (another_func, [], {})
                           ]
# and then
for fn, args, kwargs in fns_with_args_and_kwargs:
    fn(*args, **kwargs)
</code></pre>

python python-2.7 generator yield with-statement