diff --git a/Changelog.rst b/Changelog.rst index 8fa31309..7d12294f 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -1,14 +1,15 @@ Change Log ============ -1.12.2 -++++++ +1.12.2 (unreleased) ++++++++++++++++++++ Fixes ------ * `ParallelSSHClient.copy_file` with recurse enabled and absolute destination path would create empty directory in home directory of user - #197. * `ParallelSSHClient.copy_file` and `scp_recv` with recurse enabled would not create remote directories when copying empty local directories. +* `ParallelSSHClient.scp_send` would require SFTP when recurse is off and remote destination path contains directory - #157. 1.12.1 ++++++ diff --git a/pssh/clients/native/single.py b/pssh/clients/native/single.py index 227de0b3..fdfd86f2 100644 --- a/pssh/clients/native/single.py +++ b/pssh/clients/native/single.py @@ -484,8 +484,8 @@ def scp_recv(self, remote_file, local_file, recurse=False, sftp=None, :raises: :py:class:`IOError` on local file IO errors. :raises: :py:class:`OSError` on local OS errors like permission denied. """ - sftp = self._make_sftp() if (sftp is None and recurse) else sftp if recurse: + sftp = self._make_sftp() if sftp is None else sftp try: self._eagain(sftp.stat, remote_file) except (SFTPHandleError, SFTPProtocolError): @@ -575,13 +575,14 @@ def scp_send(self, local_file, remote_file, recurse=False, sftp=None): elif os.path.isdir(local_file) and not recurse: raise ValueError("Recurse must be True if local_file is a " "directory.") - destination = self._remote_paths_split(remote_file) - if destination is not None: - sftp = self._make_sftp() if sftp is None else sftp - try: - self._eagain(sftp.stat, destination) - except (SFTPHandleError, SFTPProtocolError): - self.mkdir(sftp, destination) + if recurse: + destination = self._remote_paths_split(remote_file) + if destination is not None: + sftp = self._make_sftp() if sftp is None else sftp + try: + self._eagain(sftp.stat, destination) + except (SFTPHandleError, SFTPProtocolError): + self.mkdir(sftp, destination) self._scp_send(local_file, remote_file) logger.info("SCP local file %s to remote destination %s:%s", local_file, self.host, remote_file) diff --git a/tests/native/test_parallel_client.py b/tests/native/test_parallel_client.py index 3a3ed1db..f324b9e5 100644 --- a/tests/native/test_parallel_client.py +++ b/tests/native/test_parallel_client.py @@ -1319,6 +1319,10 @@ def test_file_read_no_timeout(self): self.assertListEqual(_contents, _out) def test_scp_send_dir(self): + """ + Attempting to copy into a non-existent remote directory via scp_send() + without recurse=True should raise an SCPError. + """ test_file_data = 'test' local_filename = 'test_file' remote_test_dir, remote_filepath = 'remote_test_dir', 'test_file_copy' @@ -1330,7 +1334,30 @@ def test_scp_send_dir(self): try: cmds = self.client.scp_send(local_filename, remote_filename) joinall(cmds, raise_error=True) - time.sleep(.2) + except Exception as ex: + self.assertIsInstance(ex, SCPError) + finally: + try: + os.unlink(local_filename) + except OSError: + pass + try: + shutil.rmtree(remote_test_dir_abspath) + except OSError: + pass + + def test_scp_send_dir_recurse(self): + test_file_data = 'test' + local_filename = 'test_file' + remote_test_dir, remote_filepath = 'remote_test_dir', 'test_file_copy' + with open(local_filename, 'w') as file_h: + file_h.writelines([test_file_data + os.linesep]) + remote_filename = os.path.sep.join([remote_test_dir, remote_filepath]) + remote_file_abspath = os.path.expanduser('~/' + remote_filename) + remote_test_dir_abspath = os.path.expanduser('~/' + remote_test_dir) + try: + cmds = self.client.scp_send(local_filename, remote_filename, recurse=True) + joinall(cmds, raise_error=True) self.assertTrue(os.path.isdir(remote_test_dir_abspath)) self.assertTrue(os.path.isfile(remote_file_abspath)) remote_file_data = open(remote_file_abspath, 'r').read()