3

I have seen code like this (Python 3 code):

import ftplib
from contextlib import closing

with closing(ftplib.FTP()) as ftp:

Is the usage of the closing method necessary? In an interesting answer, we can read that in database connection objects the context manager's __exit__ method does not close the connection (at least not for SQLite), but commits a transaction instead. Therefore, using the closing method is necessary.

How is it with the Python's FTP class?

Looking at the sources of the Python ftplib, we can find this:

# Context management protocol: try to quit() if active
def __exit__(self, *args):
    if self.sock is not None:
        try:
            self.quit()
        except (OSError, EOFError):
            pass
        finally:
            if self.sock is not None:
                self.close()

The quit method is called hence I think that we don't have to use the closing method for Python 3. So we can use just:

with (ftplib.FTP()) as ftp:

Since __exit__ method is missing in Python 2, closing is necessary for Python 2 code.

Is this correct?

Jan Bodnar
  • 10,969
  • 6
  • 68
  • 77
  • 1
    You've answered your own question :) In Python2, just as you stated. There's neither a `__enter__` function or a `__exit__` function, there for the context handle won't do anything upon entering or exiting except assign the variable `ftp` in your case. So this is a inheritance and backwards compatible compliance thing. You can skip it if you're not interested in being a backwards ninja. – Torxed Sep 26 '17 at 14:37
  • @Torxed Since Internet is littered with incorrect code examples (I understand that in many one-time small scripts you don't need to bother with such details) one is easily confused, so I wanted to have confirmation. – Jan Bodnar Sep 26 '17 at 14:43
  • It's a totally fine question to ask :) I wrote up a little longer answer in case people stumbles across this question. – Torxed Sep 26 '17 at 14:48

1 Answers1

1

You did answer your own question for the most part.
And as per discussed in the comments, it's a legit question since the internet is "littered with incorrect code examples".

To clarify my short answer, this is a heritage issue where Python3 code won't perform as expected on Python2. So (good) examples on the internet will hopefully either include a explanation of the dangers of running the code in Python2.. Or they'll make the code backwards compatible with a small modification.

This is such a instance where closing() is used to emulate the new functions from Python3 when the code is run on Python2.

Python2 (ftplib) doesn't have either a __enter__ or a __exit__ function, thus rendering the context handle "useless" (except making it forward compatible, easier to read IMO and also it assigned the call to a variable, in this case ftp).
To make it un-useless the writer used closing() around the call to get the automatic closure as per Python3.

If you're souly using Python3, this is redundant and probably slows down your code (extremely little). And there for you can skip that particular piece of code if you're not looking to be backwards compatible.

Torxed
  • 22,866
  • 14
  • 82
  • 131