What is the best way to modify ftputil code to allow upload/download
from/to BytesIO variable?(The upload/download functions allow only
string path variables as inputs). I would like to avoid monkey patching
the LocalFile
or RemoteFile
classes.
If I understand your question correctly, I would do it like this:
import io import ftputil my_file = io.BytesIO() with ftputil.FTPHost(hostname, user, password) as host: # Download with host.open(remote_path, "rb") as fobj: remote_data = fobj.read() my_file.write(remote_data) # or `my_file = io.BytesIO(remote_data)` # Upload with host.open(another_remote_path, "wb") as fobj: future_remote_data = my_file.read() # or `.getvalue()` fobj.write(future_remote_data)
See https://ftputil.sschwarzer.net/trac/wiki/Documentation#file-like-objects
If you want a conditional download or upload, you can check the last modification time on the server with
host.path.getmtime
(correspondingos.path.getmtime
). If you do this, make sure you have a time zone correction applied if needed (see https://ftputil.sschwarzer.net/trac/wiki/Documentation , section "Time zone correction").Does this answer your question? If not, please explain a bit more what you have in mind, maybe with some hypothetical example code.
For the record, it seems there's a lot of overlap with ticket #118. But let's continue the discussion here now.
Sorry if I was a bit vague. What I actually wanted was to implement the same api as the
def upload(self, source, target, callback=None):
method, which behavior will depend on the type of source parameter. If it is an instanceof str than the method will be the same as of today but if the it's of type BytesIO than LocalFile? class obj method will not try to treat it as path to a file and open it... Should I just inherit from FtpHost? and override the above mentioned methods?
- The option you described above which uses the file like objects would work of course, but it is too verbose and I would like to be able to use the upload/download methods with a BytesIO type parameters.
Thanks for your feedback.
Replying to ftputiluser:
Sorry if I was a bit vague. What I actually wanted was to implement the same api as the
def upload(self, source, target, callback=None):
method, which behavior will depend on the type of source parameter. If it is an instanceof str than the method will be the same as of today but if the it's of type
BytesIO
thanLocalFile
class obj method will not try to treat it as path to a file and open it...I'm thinking now of doing something similar and allowing a file-like object as an alternative to
source
in theupload*
methods andtarget
in thedownload*
methods. This would of course includeBytesIO
objects. However, before making any promises I'll need to check how this would fit into the*File
API in thefile_transfer
module.Should I just inherit from
FtpHost
and override the above mentioned methods?
- The option you described above which uses the file like objects would work of course, but it is too verbose and I would like to be able to use the upload/download methods with a
BytesIO
type parameters.If you want to do this in several places, I didn't expect you to write the suggested code everytime. :-) Of course you can wrap this in a function or overridden method. Yes, I think it would make sense to inherit from
FTPHost
and overrideupload
and possibly other upload/download method(s) so that they behave as I described in my previous paragraph. But there's a caveat:A possible downside of overriding the
upload*
and/ordownload*
methods is that you would "have to" support thecallback
argument because objects of subclasses should be able to stand in for objects of their baseclass (see https://www.tomdalling.com/blog/software-design/solid-class-design-the-liskov-substitution-principle/ ). That said, if you limit the access to the subclass enough or you're sure you don't need the callback functionality, it may be a fair tradeoff not to support thecallback
parameter or support it later if you need to. You have to be aware of the risk though that code that usesFTPHost
objects in general may break if it happens to use acallback
argument with the methods from your subclass.