Python pysftp put_r does not work on Windows

I'd like to upload multiple files from a Windows directory to an SFTP server using pysftp 0.2.8. I've read up the doc and it suggests to use put_d or put_r but both give me the following error:

OSError: Invalid path:

sftp_local_path = r'C:\Users\Swiss\some\path'

sftp_remote_path = '/FTP/LPS Data/ATC/RAND/20191019_RAND/XML'

with pysftp.Connection("xxx.xxx.xxx.xxx", username=myUsername, password=myPassword) as sftp:
    with sftp.cd(sftp_remote_path):
        sftp.put_r(sftp_local_path, sftp_remote_path)
        for i in sftp.listdir():
            lstatout=str(sftp.lstat(i)).split()[0]
            if 'd' in lstatout: print (i, 'is a directory')

sftp.close()

I'd like to be able to copy all files or selected files from my local directory to the SFTP server.


Solution 1:

I cannot reproduce your exact problem, but indeed the recursive functions of pysftp are known to be implemented in a way that makes them fail on Windows (or any system that does not use *nix-like path syntax).

Pysftp uses os.sep and os.path functions for remote SFTP paths, what is wrong, as SFTP paths always use a forward slash.


But you can easily implement a portable replacement:

import os
def put_r_portable(sftp, localdir, remotedir, preserve_mtime=False):
    for entry in os.listdir(localdir):
        remotepath = remotedir + "/" + entry
        localpath = os.path.join(localdir, entry)
        if not os.path.isfile(localpath):
            try:
                sftp.mkdir(remotepath)
            except OSError:     
                pass
            put_r_portable(sftp, localpath, remotepath, preserve_mtime)
        else:
            sftp.put(localpath, remotepath, preserve_mtime=preserve_mtime)    

Use it like:

put_r_portable(sftp, sftp_local_path, sftp_remote_path, preserve_mtime=False) 

Note that the above code can be easily modified to work with Paramiko directly, in case you do not want to use pysftp. The Paramiko SFTPClient class also has the put method. The only difference is that the Paramiko's put does not have the preserve_mtime parameter/functionality (but it can be implemented easily, if you need it).


For a similar question about get_r, see:
Python pysftp get_r from Linux works fine on Linux but not on Windows