@@ -421,19 +421,44 @@ def _get_sftp(self, timeout):
421421
422422 @classmethod
423423 def _push_file (cls , sftp , src , dst ):
424- sftp .put (src , dst )
424+ try :
425+ sftp .put (src , dst )
426+ # Maybe the dst was a folder
427+ except OSError :
428+ # This might fail if the folder already exists
429+ with contextlib .suppress (IOError ):
430+ sftp .mkdir (dst )
431+
432+ new_dst = os .path .join (
433+ dst ,
434+ os .path .basename (src ),
435+ )
436+
437+ return cls ._push_file (sftp , src , new_dst )
438+
425439
426440 @classmethod
427441 def _push_folder (cls , sftp , src , dst ):
442+ # Behave like the "mv" command or adb push: a new folder is created
443+ # inside the destination folder, rather than merging the trees.
444+ dst = os .path .join (
445+ dst ,
446+ os .path .basename (src ),
447+ )
448+ return cls ._push_folder_internal (sftp , src , dst )
449+
450+ @classmethod
451+ def _push_folder_internal (cls , sftp , src , dst ):
428452 # This might fail if the folder already exists
429453 with contextlib .suppress (IOError ):
430454 sftp .mkdir (dst )
455+
431456 for entry in os .scandir (src ):
432457 name = entry .name
433458 src_path = os .path .join (src , name )
434459 dst_path = os .path .join (dst , name )
435460 if entry .is_dir ():
436- push = cls ._push_folder
461+ push = cls ._push_folder_internal
437462 else :
438463 push = cls ._push_file
439464
@@ -446,8 +471,16 @@ def _push_path(cls, sftp, src, dst):
446471
447472 @classmethod
448473 def _pull_file (cls , sftp , src , dst ):
474+ # Pulling a file into a folder will use the source basename
475+ if os .path .isdir (dst ):
476+ dst = os .path .join (
477+ dst ,
478+ os .path .basename (src ),
479+ )
480+
449481 with contextlib .suppress (FileNotFoundError ):
450482 os .remove (dst )
483+
451484 sftp .get (src , dst )
452485
453486 @classmethod
0 commit comments