We specify timeout as a kwarg to the ftputil.FTPHost constructor for
passthrough to ftplib.FTP, which worked through ftputil 4.0.0. However,
in 5.0.0 the constructor of ftputil.session.Session does not support the
other kwargs passed to ftplib.FTP-- it only supports host, user and
password. As a result any code relying on the acct, timeout, or
source_address kwargs breaks. I believe the solution is just to add
**kwargs
to the Session.__init__
method and pass them through the
super.__init__
method.
Stack trace:
In [11]: host = ftputil.FTPHost(creds['host'], creds['user'], creds['pwd'], timeout=10)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-11-9308d6c2abeb> in <module>
----> 1 host = ftputil.FTPHost(creds['host'], creds['user'], creds['pwd'], timeout=10)
C:\Miniconda3\envs\test_feb_2021\lib\site-packages\ftputil\host.py in __init__(self, *args, **kwargs)
82 # The time shift setting shouldn't be reset though. Make a session
83 # according to these arguments.
---> 84 self._session = self._make_session()
85 # Simulate `os.path`.
86 self.path = ftputil.path._Path(self)
C:\Miniconda3\envs\test_feb_2021\lib\site-packages\ftputil\host.py in _make_session(self)
149 f"session instance {session!r} must have an `encoding` attribute"
150 )
--> 151 self._encoding = session.encoding
152 return session
153
C:\Miniconda3\envs\test_feb_2021\lib\site-packages\ftputil\host.py in _make_session(self)
144 factory = kwargs.pop("session_factory", default_session_factory)
145 with ftputil.error.ftplib_error_to_ftp_os_error:
--> 146 session = factory(*args, **kwargs)
147 if not hasattr(session, "encoding"):
148 raise ftputil.error.NoEncodingError(
TypeError: __init__() got an unexpected keyword argument 'timeout'
Thanks for your report!
It seems you're right. ftputil 4.0.0 passed the
*args
and**kwargs
fromFTPHost.__init__
to the session factory. ftputil 5.0.0 replaces the default factoryftplib.FTP
with a factory created byftputil.session.session_factory
, which doesn't understand "arbitrary" positional and keyword arguments.I need to think a bit more about how best to resolve this. For example, what should happen if you pass an
encoding
tosession_factory
and later passencoding
also toFTPHost.__init__
? For the other arguments and how they're used in the class returned bysession_factory
there could also be tricky interactions. I guess that's a reason why the class returned bysession_factory
doesn't accept arbitrary positional or keyword arguments, i. e. to avoid handling such edge cases.
As a workaround for the time being, you can use
FTPHost(..., session_factory=ftplib.FTP)
(if you don't mind the default path encoding UTF-8).
Replying to schwa:
As a workaround for the time being, you can use
FTPHost(..., session_factory=ftplib.FTP)
(if you don't mind the default path encoding UTF-8).To be more precise, the default path encoding for
ftplib.FTP
is latin-1 under Python ≤3.8 and UTF-8 under Python ≥3.9.
Would you please replace
ftputil/host.py
with the attached file and give feedback if it works for you? Ideally, you would run it under Python 3.8 (or earlier) and Python 3.9. (The fix for Python 3.8 and earlier is relatively simple; the fix for Python 3.9 is more complicated.)
The patch seems to be working. Python 3.7 is our bread-and-butter installation, I also created a conda env for Python 3.9, and both work with the patch (and fail without), though our FTP targets all use exclusively ASCII so I can't verify the Latin-1 vs UTF-8 aspect. I verified that with the patch, timeout on the ftplib.FTP object is as set if specified, and ftplib._GLOBAL_DEFAULT_TIMEOUT if not, and connection is successful. Under Python 3.7 I was able to do an end-to-end successfully transfering data with our standard process with the patch.
Many thanks for the thorough test! Nice. :-)
I'll make a new release during the next few days. If it's very important for you, I can try to make the release today.
Fixed as of 266a8beef77208a9ee419c7ab7f0502a719b9cad.
Replying to schwa:
I'll make a new release during the next few days. If it's very important for you, I can try to make the release today.
Too late, already released. ;-)