Package: sftpretty Version: 1.2.2
Related (not duplicate): #24 (fixed put_d semantics at depth 1, PR #25),
Repro
Local tree:
import sftpretty
with sftpretty.Connection(host, username=USER, password=PASS) as conn:
conn.put_r("data", "remote_root")
Expected: remote_root/data/a/b/file.txt
Actual: remote_root/a/b/b/file.txt, plus spurious empty dirs remote_root/data/ and remote_root/a/a/.
Root cause
localtree() (helpers.py) records the remote path for each subdirectory as the already-final path:
remote = Path(remotedir).joinpath(localpath.relative_to(localdir).as_posix()).as_posix()
put_d() (__init__.py) then appends the directory's own name again, since its contract (per #24) is that remotedir is the parent:
self.mkdir_p(Path(remotedir).joinpath(localdir.parts[-1]).as_posix())
...
Path(remotedir).joinpath(localpath.relative_to(localdir.parent).as_posix()).as_posix()
This only works correctly for put_r()'s first call (tree[lwd] = [(lwd, rwd)], where rwd really is the parent). Every deeper entry from localtree() passes an already-resolved directory, so put_d() double-appends it.
Suggested fix
In localtree(), store the parent remote dir per entry (remote = remotedir, not remotedir.joinpath(subdir_name)) to match put_d()'s existing contract.
Package: sftpretty Version: 1.2.2
Related (not duplicate): #24 (fixed
put_dsemantics at depth 1, PR #25),Repro
Local tree:
Expected:
remote_root/data/a/b/file.txtActual:
remote_root/a/b/b/file.txt, plus spurious empty dirsremote_root/data/andremote_root/a/a/.Root cause
localtree()(helpers.py) records the remote path for each subdirectory as the already-final path:put_d()(__init__.py) then appends the directory's own name again, since its contract (per #24) is thatremotediris the parent:This only works correctly for
put_r()'s first call (tree[lwd] = [(lwd, rwd)], whererwdreally is the parent). Every deeper entry fromlocaltree()passes an already-resolved directory, soput_d()double-appends it.Suggested fix
In
localtree(), store the parent remote dir per entry (remote = remotedir, notremotedir.joinpath(subdir_name)) to matchput_d()'s existing contract.