~sschwarzer/ftputil#128: 
Transfer to/from in memory variables

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.

Status
REPORTED
Submitter
ftputiluser (unverified)
Assigned to
No-one
Submitted
4 years ago
Updated
4 years ago
Labels
enhancement library

schwa (unverified) 4 years ago · edit

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 (corresponding os.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.

schwa (unverified) 4 years ago · edit

For the record, it seems there's a lot of overlap with ticket #118. But let's continue the discussion here now.

ftputiluser (unverified) 4 years ago · edit

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.

schwa (unverified) 4 years ago · edit

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 than LocalFile 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 the upload* methods and target in the download* methods. This would of course include BytesIO objects. However, before making any promises I'll need to check how this would fit into the *File API in the file_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 override upload 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/or download* methods is that you would "have to" support the callback 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 the callback parameter or support it later if you need to. You have to be aware of the risk though that code that uses FTPHost objects in general may break if it happens to use a callback argument with the methods from your subclass.

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