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)
Added code markup to ticket description.
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 toftplib.FTP
. It will then depend on theftplib
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.
Things to find out and probably use to improve ftputil:
Why does ftputil convert the
socket.gaierror
to anFTPOSError
?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
hasIOError
in its baseclasses andIOError
is also in theftplib.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 thatargs[1:]
contains anything that should be part of an error message.I still may use heuristics like:
If the exception inherits from
socket.error
, assumeargs[1]
is the actual message and combineargs[:2]
. (The error code might be useful in some cases. Don't set the ftputil exception'serrno
toargs[0]
though because this might violate callers' expectations.)If
args[0]
is an integer, assume thatargs[1]
is the actual message and combine them.
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 thatargs[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 withFTPOSError(*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 theFTPOSError.strerror
attribute.
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)
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.