Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reassign variable in with-block

Tags:

Is it possible to reassign the object of a with-block prior to exiting the block?

Specific use case

I interact with an FTP server that occasionally drops connections mid-transfer, and IT is not willing to do anything about it. As a workaround for my own tools, I'm using a wrapper that will retry transfers a few times before giving up:

def retry(conn, max_tries=3, **kwargs):
    this_try = 1
    while (this_try <= max_tries):
        try:
            # upload / download / whatever
            return conn
        except ftplib.all_errors:
            conn.quit()
            time.sleep(60)
            conn = ftplib.FTP(**kwargs)
            this_try += 1

This wrapper works fine, but doesn't doesn't appear to be usable inside a with block, like normal FTP connections can be used. If the except clause is ever hit, the connection will be re-established, but on exiting the with block, python will attempt to close the original conn, not the new one:

with ftplib.FTP(**kwargs) as conn:
    conn = retry(conn, **kwargs)

This can be demonstrated with a custom context manager, showing that python calls __exit__() from the original object, even if the variable is reassigned mid-block:

>>> class Echo(object):
...     def __enter__(self):
...             print('entering ' + repr(self))
...             return self
...     def __exit__(self, *args):
...             print('exiting ' + repr(self))
...
>>> with Echo() as e:
...     e = Echo()
...
entering <__main__.Echo object at 0x026C14F0>
exiting <__main__.Echo object at 0x026C14F0>
>>> e
<__main__.Echo object at 0x026C1410>

How can I reassign conn inside the with block so that python calls the __exit__() method on the latest object, instead of the original one? Is such a thing even possible, or am I forced to go without with blocks and have to remember to call conn.quit() everywhere?

If it matters, I'd like something compatible with both python 2 and 3. If a solution is not compatible with both, then I would prefer a python 3-specific solution over a python 2-specific solution

like image 588
Steve Avatar asked Apr 11 '16 20:04

Steve


1 Answers

Answering your general question, no you cannot, as shown in PEP 343 "Specification: The with statement". The variable e in your context is saved into an internal variable which is used at tear-down time. For the specific FTP connection, some other options have been proposed in the other answers.

like image 173
Cyb3rFly3r Avatar answered Sep 28 '22 03:09

Cyb3rFly3r