~sschwarzer/ftputil#76: 
Error message related to OS

 python test.py 

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    with ftputil.FTPHost('195.0.0.1:595', 'User1', 'test1') as host:
  File "/home/nick/Desktop/ftp_testing_python/ftputil/host.py", line 69, in __init__
    self._session = self._make_session()
  File "/home/nick/Desktop/ftp_testing_python/ftputil/host.py", line 129, in _make_session
    return factory(*args, **kwargs)
  File "/home/nick/Desktop/ftp_testing_python/ftputil/error.py", line 133, in __exit__
    raise FTPOSError(*exc_value.args)
ftputil.error.FTPOSError: -2
Debugging info: ftputil 3.0, Python 2.7.3 (linux2)

The following code worked on a different version of ubuntu

import ftputil 

# Download some files from the login directory.
with ftputil.FTPHost('195.0.0.1:595', 'User1', 'test1') as host:
    names = host.listdir(host.curdir)
    for name in names:
        print(name)
Status
RESOLVED INVALID
Submitter
ftputiluser (unverified)
Assigned to
No-one
Submitted
9 years ago
Updated
9 years ago
Labels
bug library

schwa (unverified) 9 years ago · edit

Added code markup to ticket description.

schwa (unverified) 9 years ago · edit

Hi,

Thanks for the report.

Is "the following code" the one that gives the traceback shown at the start of the description (i. e. test.py)?

One thing I notice in the code is the use of "195.0.0.1:595" for host and port. ftputil passes this string as the host to ftplib.FTP. It will then depend on the ftplib module in the Python standard library whether it will strip off the part after the colon and use that for the port when connecting. From the ​Python documentation on `ftplib` it doesn't look like the form "host:port" is allowed. I can imagine that the code that was working on another Ubuntu version actually used only a host without port and hence implicitly the default port.

When I do something similar in the interpreter like in your code (note: no ftputil involved here), I also get an error code -2:

>>> import ftplib
>>> f = ftplib.FTP("ftp.gnome.org:21", "anonymous", "")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.7/ftplib.py", line 117, in __init__
    self.connect(host)
  File "/usr/lib64/python2.7/ftplib.py", line 132, in connect
    self.sock = socket.create_connection((self.host, self.port), self.timeout)
  File "/usr/lib64/python2.7/socket.py", line 553, in create_connection
    for res in getaddrinfo(host, port, 0, SOCK_STREAM):
socket.gaierror: [Errno -2] Name or service not known

If you actually want to connect to a non-default port, I recommend you read this section in the ftputil documentation.

schwa (unverified) 9 years ago · edit

Things to find out and probably use to improve ftputil:

Why does ftputil convert the socket.gaierror to an FTPOSError?

This code in ftputil.error.FtplibErrorToFTPOSError.__exit__ is responsible for the conversion:

        elif isinstance(exc_value, ftplib.all_errors):
            raise FTPOSError(*exc_value.args)

In the interpreter:

In [9]: try:
   ...:     f = ftplib.FTP("ftp.gnome.org:21", "", "")
   ...: except Exception, exc:
   ...:     socket_exc = exc
   ...:     

In [11]: isinstance(socket_exc, ftplib.all_errors)
Out[11]: True

In [13]: ftplib.all_errors
Out[13]: (ftplib.Error, IOError, EOFError, ssl.SSLError)

In [14]: for exc in ftplib.all_errors:
   ....:     if isinstance(socket_exc, exc):
   ....:         print exc
   ....:         
<type 'exceptions.IOError'>

In [17]: socket_exc.__class__.mro()
Out[17]: 
[socket.gaierror,
 socket.error,
 IOError,
 EnvironmentError,
 StandardError,
 Exception,
 BaseException,
 object]

So socket.gaierror has IOError in its baseclasses and IOError is also in the ftplib.all_errors tuple.

Why is the error message from ftputil only "-2"?

In [18]: socket_exc.args
Out[18]: (-2, 'Name or service not known')

ftputil tries to be helpful and shows the error message from the original exception. However, ftputil uses only args[0] as source of the error message because, as far as I know, there's no guarantee or even a common idiom that args[1:] contains anything that should be part of an error message.

I still may use heuristics like:

  • If the exception inherits from socket.error, assume args[1] is the actual message and combine args[:2]. (The error code might be useful in some cases. Don't set the ftputil exception's errno to args[0] though because this might violate callers' expectations.)

  • If args[0] is an integer, assume that args[1] is the actual message and combine them.

schwa (unverified) 9 years ago · edit

Replying to schwa:

Why is the error message from ftputil only "-2"?

In [18]: socket_exc.args
Out[18]: (-2, 'Name or service not known')

ftputil tries to be helpful and shows the error message from the original exception. However, ftputil uses only args[0] as source of the error message because, as far as I know, there's no guarantee or even a common idiom that args[1:] contains anything that should be part of an error message.

Why does the message from FTPOSError only contain the "-2" part even though the exception is constructed with FTPOSError(*exc_value.args) (see above)?

This is the responsible code from ftputil.error:

class FTPError(Exception):
    """General ftputil error class."""

    def __init__(self, *args):
        super(FTPError, self).__init__(*args)
        # Don't use `args[0]` directly because `args` may be empty.
        if args:
            self.strerror = self.args[0]
        else:
            self.strerror = ""
        try:
            self.errno = int(self.strerror[:3])
        except (TypeError, IndexError, ValueError):
            self.errno = None
        self.file_name = None

    def __str__(self):
        return "{0}\nDebugging info: {1}".format(self.strerror,
                                                 ftputil.version.version_info)

So by default, ftputil uses only args[0] for the FTPOSError.strerror attribute.

schwa (unverified) 9 years ago · edit

I checked in [cecbb557b3c201b38b96956bd9406848bbba28ea](https://git.sr.ht/~sschwarzer/ftputil/commit/cecbb557b3c201b38b96956bd9406848bbba28ea "Use original exception message in FTPError (ticket #76). If we ...") to include the full string representation of a previously raised exception. So if you use the "host*:port*" form now, you get a slightly more informative error message:

>>> host = ftputil.FTPHost("ftp.gnome.org:21", "", "")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ftputil/host.py", line 69, in __init__
    self._session = self._make_session()
  File "ftputil/host.py", line 129, in _make_session
    return factory(*args, **kwargs)
  File "ftputil/error.py", line 139, in __exit__
    raise FTPOSError(*exc_value.args, original_exception=exc_value)
ftputil.error.FTPOSError: [Errno -2] Name or service not known
Debugging info: ftputil 3.0, Python 2.7.5 (linux2)

schwa (unverified) 9 years ago · edit

I change the resolution to "invalid" since in my opinion the "lacking" support for the "host:port" syntax is no defect.

The "fixed" referred to the improvement in the error message.

Register here or Log in to comment, or comment via email.