3
\$\begingroup\$

Introduction

I have written a Python class which uses the module ftplib. In this class I have created a private method called __connect(). Its goal is try to connect to a FTP Server.
Trying the connection could be raised many type of Exception. I have emulated some of these kind of errors:

  • a time out error
  • a Connection refused error because the host (that is reachable) doesn't run a FTP Server

My code

The code of my class is store in the module ftp_connect.py, and the code inside this module is the following:

import ftplib

CONNECTION_TIMEOUT = 10

class FTPExport:
    def __init__(self, server_address, username, password):
        self.__server_address = server_address
        self.__username = username
        self.__password = password
        self.__ftpserver = None

    def __connect(self):
        try:
            if self.__server_address == None or self.__server_address == "":
                return "Server address error"
            self.__ftpserver = ftplib.FTP(self.__server_address, timeout=CONNECTION_TIMEOUT)
            return "OK"
        except ConnectionRefusedError as ex:
            return ex.args[1]
        except BaseException as ex:
            if len(ex.args) > 0:
                if ex.args[0] == 'timed out':
                    return "Connection timed out"
                return "Connection error"
            else:
                return "Connection error"

if __name__ == '__main__':
    # Connection timed out (the address 191.168.127.1 is not reachable on my network)
    ftp_export = FTPExport("191.168.127.1", "frank", "123456")
    print(ftp_export._FTPExport__connect())

My question is relative to the Timed out error. If you see my code (method __connect()) I have caught the Exception raised by an occurred Timed Out by the following snippet of code:

try:
    ...
except BaseException as ex:
    if len(ex.args) > 0:
        if ex.args[0] == 'timed out':
            return "Connection timed out" 
    ...

In the previous code I do:

  • catch the BaseException because the exception raised when occurs a Timed Out has BaseException as superclass
  • check the content of args[0] and if it is equal to timed out I'm sure that it is occurred a Time Out

How to cause a time out: valid IP address but not reachable

To test my code, make sure that the address 191.168.127.1 is not reachable in your network, and use the following command line:

> python3 ftp_connect.py

If you wait at least 10 seconds you obtain the following output:

Connection timed out

My question

Is it possible to catch the Exception raised by the __init__ method of the class ftplib.FTP when occurs a Timed Out connection in an other way than by the generic BaseException and by checking the content of args?

\$\endgroup\$
2
  • \$\begingroup\$ Please do not edit the question, especially the code, after an answer has been posted. Changing the question may cause answer invalidation. Everyone needs to be able to see what the reviewer was referring to. What to do after the question has been answered. \$\endgroup\$
    – pacmaninbw
    Commented Oct 6, 2023 at 14:19
  • \$\begingroup\$ @pacmaninbw Ok thank you, but don't worry I paid attention to not invalidate anything of the answer. I modified the code just to make it more impersonal. \$\endgroup\$
    – User051209
    Commented Oct 6, 2023 at 14:39

1 Answer 1

5
\$\begingroup\$

no name mangling

        self.__username = username

I don't know what's going on with that __ dunder prefix. But name mangling is seldom what folks want. Prefer a single _ underscore to mark it _private:

        self._username = username

public API

    def __connect(self):

I was about to offer the same (_connect) advice. Until I noticed that this is no internal detail, this is the only functionality the class exports. This method is definitely public. Say so.

    def connect(self):

singleton comparison

    ... server_address == None ...

Prefer to ask about the address of that variable with is:

    ... server_address is None ...

It's just kind of a weird pythonism, based on the fact that there's exactly one None object. You'll get used to it. Or your linter will remind you.

Testing for empty string with ... == "" is perfect as-is.


raise exceptions

                return "Server address error"

First, please write a """docstring""" explaining what this method returns.

Second, shouldn't we raise rather than return the error?


catch exceptions

        except BaseException as ex:

Ok, that just seems insane. You're trapping KeyboardInterrupt and SystemExit ?!? No ctrl-C for you!

Prefer except Exception: rather than BaseException. Always. cf https://www.flake8rules.com/rules/E722.html

Is it possible to catch ... in another way ?

Oh, thank goodness! So you are aware there must be a better way, excellent.

Set a breakpoint() and then print out details of the exception:

(Pdb) p type(ex)
<class 'TimeoutError'>

Good, we have a TimeoutError.

Simply use except TimeoutError: and be done with it. Yes, you could examine its .args[0]. But that will always be a boring "timed out" message for a TimeoutError, so there's little need of that.

In general, if calling code knows how to intelligently cope with a particular error (or two), it should try to catch just those errors, quite narrowly. Avoid the temptation to go wide and trap all errors. Often it is best to let an unanticipated error bubble on up the call stack, until someone deals with it or reports it in a stack trace.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Before version 3.10, I believe it raised socket.timeout. \$\endgroup\$
    – RootTwo
    Commented Oct 5, 2023 at 5:32
  • \$\begingroup\$ @RootTwo Yes I'm using Python 3.6 so I need to catch socket.timeout. \$\endgroup\$
    – User051209
    Commented Oct 5, 2023 at 6:32
  • \$\begingroup\$ @J_H Thanks I appreciated all your advice. I can't give you more than one upvote! :) \$\endgroup\$
    – User051209
    Commented Oct 5, 2023 at 6:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.