summaryrefslogtreecommitdiff
path: root/paramiko/sftp_client.py
diff options
context:
space:
mode:
authorJeff Forcier <jeff@bitprophet.org>2018-05-29 15:26:48 -0700
committerJeff Forcier <jeff@bitprophet.org>2018-05-29 15:26:48 -0700
commit3c90e2cd52f7b956671db944061dc5b6cc7872d1 (patch)
tree1f03c2065463d2c7c71181c3a766593b4ed04f9d /paramiko/sftp_client.py
parent2b40bf14493245ef4112128a66b3f347c6f7b746 (diff)
parentf2ab9241daa49be4a7293b903eac34188d3a74d8 (diff)
downloadparamiko-3c90e2cd52f7b956671db944061dc5b6cc7872d1.tar.gz
Merge branch '2.4'
Diffstat (limited to 'paramiko/sftp_client.py')
-rw-r--r--paramiko/sftp_client.py175
1 files changed, 104 insertions, 71 deletions
diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py
index 31dc234c..de3f9f58 100644
--- a/paramiko/sftp_client.py
+++ b/paramiko/sftp_client.py
@@ -30,12 +30,37 @@ from paramiko.message import Message
from paramiko.common import INFO, DEBUG, o777
from paramiko.py3compat import b, u, long
from paramiko.sftp import (
- BaseSFTP, CMD_OPENDIR, CMD_HANDLE, SFTPError, CMD_READDIR, CMD_NAME,
- CMD_CLOSE, SFTP_FLAG_READ, SFTP_FLAG_WRITE, SFTP_FLAG_CREATE,
- SFTP_FLAG_TRUNC, SFTP_FLAG_APPEND, SFTP_FLAG_EXCL, CMD_OPEN, CMD_REMOVE,
- CMD_RENAME, CMD_MKDIR, CMD_RMDIR, CMD_STAT, CMD_ATTRS, CMD_LSTAT,
- CMD_SYMLINK, CMD_SETSTAT, CMD_READLINK, CMD_REALPATH, CMD_STATUS,
- CMD_EXTENDED, SFTP_OK, SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED,
+ BaseSFTP,
+ CMD_OPENDIR,
+ CMD_HANDLE,
+ SFTPError,
+ CMD_READDIR,
+ CMD_NAME,
+ CMD_CLOSE,
+ SFTP_FLAG_READ,
+ SFTP_FLAG_WRITE,
+ SFTP_FLAG_CREATE,
+ SFTP_FLAG_TRUNC,
+ SFTP_FLAG_APPEND,
+ SFTP_FLAG_EXCL,
+ CMD_OPEN,
+ CMD_REMOVE,
+ CMD_RENAME,
+ CMD_MKDIR,
+ CMD_RMDIR,
+ CMD_STAT,
+ CMD_ATTRS,
+ CMD_LSTAT,
+ CMD_SYMLINK,
+ CMD_SETSTAT,
+ CMD_READLINK,
+ CMD_REALPATH,
+ CMD_STATUS,
+ CMD_EXTENDED,
+ SFTP_OK,
+ SFTP_EOF,
+ SFTP_NO_SUCH_FILE,
+ SFTP_PERMISSION_DENIED,
)
from paramiko.sftp_attr import SFTPAttributes
@@ -51,15 +76,15 @@ def _to_unicode(s):
probably doesn't know the filename's encoding.
"""
try:
- return s.encode('ascii')
+ return s.encode("ascii")
except (UnicodeError, AttributeError):
try:
- return s.decode('utf-8')
+ return s.decode("utf-8")
except UnicodeError:
return s
-b_slash = b'/'
+b_slash = b"/"
class SFTPClient(BaseSFTP, ClosingContextManager):
@@ -71,6 +96,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
Instances of this class may be used as context managers.
"""
+
def __init__(self, sock):
"""
Create an SFTP client from an existing `.Channel`. The channel
@@ -97,15 +123,18 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
# override default logger
transport = self.sock.get_transport()
self.logger = util.get_logger(
- transport.get_log_channel() + '.sftp')
+ transport.get_log_channel() + ".sftp"
+ )
self.ultra_debug = transport.get_hexdump()
try:
server_version = self._send_version()
except EOFError:
- raise SSHException('EOF during negotiation')
+ raise SSHException("EOF during negotiation")
self._log(
INFO,
- 'Opened sftp connection (server version {})'.format(server_version)
+ "Opened sftp connection (server version {})".format(
+ server_version
+ ),
)
@classmethod
@@ -132,11 +161,12 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionchanged:: 1.15
Added the ``window_size`` and ``max_packet_size`` arguments.
"""
- chan = t.open_session(window_size=window_size,
- max_packet_size=max_packet_size)
+ chan = t.open_session(
+ window_size=window_size, max_packet_size=max_packet_size
+ )
if chan is None:
return None
- chan.invoke_subsystem('sftp')
+ chan.invoke_subsystem("sftp")
return cls(chan)
def _log(self, level, msg, *args):
@@ -148,10 +178,12 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
# logging.Logger.log() explicitly requires it. Grump.
# escape '%' in msg (they could come from file or directory names)
# before logging
- msg = msg.replace('%', '%%')
+ msg = msg.replace("%", "%%")
super(SFTPClient, self)._log(
level,
- "[chan %s] " + msg, *([self.sock.get_name()] + list(args)))
+ "[chan %s] " + msg,
+ *([self.sock.get_name()] + list(args))
+ )
def close(self):
"""
@@ -159,7 +191,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionadded:: 1.4
"""
- self._log(INFO, 'sftp session closed.')
+ self._log(INFO, "sftp session closed.")
self.sock.close()
def get_channel(self):
@@ -171,7 +203,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
"""
return self.sock
- def listdir(self, path='.'):
+ def listdir(self, path="."):
"""
Return a list containing the names of the entries in the given
``path``.
@@ -185,7 +217,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
"""
return [f.filename for f in self.listdir_attr(path)]
- def listdir_attr(self, path='.'):
+ def listdir_attr(self, path="."):
"""
Return a list containing `.SFTPAttributes` objects corresponding to
files in the given ``path``. The list is in arbitrary order. It does
@@ -203,10 +235,10 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionadded:: 1.2
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'listdir({!r})'.format(path))
+ self._log(DEBUG, "listdir({!r})".format(path))
t, msg = self._request(CMD_OPENDIR, path)
if t != CMD_HANDLE:
- raise SFTPError('Expected handle')
+ raise SFTPError("Expected handle")
handle = msg.get_binary()
filelist = []
while True:
@@ -216,18 +248,18 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
# done with handle
break
if t != CMD_NAME:
- raise SFTPError('Expected name response')
+ raise SFTPError("Expected name response")
count = msg.get_int()
for i in range(count):
filename = msg.get_text()
longname = msg.get_text()
attr = SFTPAttributes._from_msg(msg, filename, longname)
- if (filename != '.') and (filename != '..'):
+ if (filename != ".") and (filename != ".."):
filelist.append(attr)
self._request(CMD_CLOSE, handle)
return filelist
- def listdir_iter(self, path='.', read_aheads=50):
+ def listdir_iter(self, path=".", read_aheads=50):
"""
Generator version of `.listdir_attr`.
@@ -242,11 +274,11 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionadded:: 1.15
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'listdir({!r})'.format(path))
+ self._log(DEBUG, "listdir({!r})".format(path))
t, msg = self._request(CMD_OPENDIR, path)
if t != CMD_HANDLE:
- raise SFTPError('Expected handle')
+ raise SFTPError("Expected handle")
handle = msg.get_string()
@@ -261,7 +293,6 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
num = self._async_request(type(None), CMD_READDIR, handle)
nums.append(num)
-
# For each of our sent requests
# Read and parse the corresponding packets
# If we're at the end of our queued requests, then fire off
@@ -280,8 +311,9 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
filename = msg.get_text()
longname = msg.get_text()
attr = SFTPAttributes._from_msg(
- msg, filename, longname)
- if (filename != '.') and (filename != '..'):
+ msg, filename, longname
+ )
+ if (filename != ".") and (filename != ".."):
yield attr
# If we've hit the end of our queued requests, reset nums.
@@ -291,8 +323,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
self._request(CMD_CLOSE, handle)
return
-
- def open(self, filename, mode='r', bufsize=-1):
+ def open(self, filename, mode="r", bufsize=-1):
"""
Open a file on the remote server. The arguments are the same as for
Python's built-in `python:file` (aka `python:open`). A file-like
@@ -325,26 +356,28 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:raises: ``IOError`` -- if the file could not be opened.
"""
filename = self._adjust_cwd(filename)
- self._log(DEBUG, 'open({!r}, {!r})'.format(filename, mode))
+ self._log(DEBUG, "open({!r}, {!r})".format(filename, mode))
imode = 0
- if ('r' in mode) or ('+' in mode):
+ if ("r" in mode) or ("+" in mode):
imode |= SFTP_FLAG_READ
- if ('w' in mode) or ('+' in mode) or ('a' in mode):
+ if ("w" in mode) or ("+" in mode) or ("a" in mode):
imode |= SFTP_FLAG_WRITE
- if 'w' in mode:
+ if "w" in mode:
imode |= SFTP_FLAG_CREATE | SFTP_FLAG_TRUNC
- if 'a' in mode:
+ if "a" in mode:
imode |= SFTP_FLAG_CREATE | SFTP_FLAG_APPEND
- if 'x' in mode:
+ if "x" in mode:
imode |= SFTP_FLAG_CREATE | SFTP_FLAG_EXCL
attrblock = SFTPAttributes()
t, msg = self._request(CMD_OPEN, filename, imode, attrblock)
if t != CMD_HANDLE:
- raise SFTPError('Expected handle')
+ raise SFTPError("Expected handle")
handle = msg.get_binary()
self._log(
DEBUG,
- 'open({!r}, {!r}) -> {}'.format(filename, mode, u(hexlify(handle)))
+ "open({!r}, {!r}) -> {}".format(
+ filename, mode, u(hexlify(handle))
+ ),
)
return SFTPFile(self, handle, mode, bufsize)
@@ -361,7 +394,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:raises: ``IOError`` -- if the path refers to a folder (directory)
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'remove({!r})'.format(path))
+ self._log(DEBUG, "remove({!r})".format(path))
self._request(CMD_REMOVE, path)
unlink = remove
@@ -386,7 +419,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
"""
oldpath = self._adjust_cwd(oldpath)
newpath = self._adjust_cwd(newpath)
- self._log(DEBUG, 'rename({!r}, {!r})'.format(oldpath, newpath))
+ self._log(DEBUG, "rename({!r}, {!r})".format(oldpath, newpath))
self._request(CMD_RENAME, oldpath, newpath)
def posix_rename(self, oldpath, newpath):
@@ -406,7 +439,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
"""
oldpath = self._adjust_cwd(oldpath)
newpath = self._adjust_cwd(newpath)
- self._log(DEBUG, 'posix_rename({!r}, {!r})'.format(oldpath, newpath))
+ self._log(DEBUG, "posix_rename({!r}, {!r})".format(oldpath, newpath))
self._request(
CMD_EXTENDED, "posix-rename@openssh.com", oldpath, newpath
)
@@ -421,7 +454,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param int mode: permissions (posix-style) for the newly-created folder
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'mkdir({!r}, {!r})'.format(path, mode))
+ self._log(DEBUG, "mkdir({!r}, {!r})".format(path, mode))
attr = SFTPAttributes()
attr.st_mode = mode
self._request(CMD_MKDIR, path, attr)
@@ -433,7 +466,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param str path: name of the folder to remove
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'rmdir({!r})'.format(path))
+ self._log(DEBUG, "rmdir({!r})".format(path))
self._request(CMD_RMDIR, path)
def stat(self, path):
@@ -456,10 +489,10 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
file
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'stat({!r})'.format(path))
+ self._log(DEBUG, "stat({!r})".format(path))
t, msg = self._request(CMD_STAT, path)
if t != CMD_ATTRS:
- raise SFTPError('Expected attributes')
+ raise SFTPError("Expected attributes")
return SFTPAttributes._from_msg(msg)
def lstat(self, path):
@@ -474,10 +507,10 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
file
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'lstat({!r})'.format(path))
+ self._log(DEBUG, "lstat({!r})".format(path))
t, msg = self._request(CMD_LSTAT, path)
if t != CMD_ATTRS:
- raise SFTPError('Expected attributes')
+ raise SFTPError("Expected attributes")
return SFTPAttributes._from_msg(msg)
def symlink(self, source, dest):
@@ -488,7 +521,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param str dest: path of the newly created symlink
"""
dest = self._adjust_cwd(dest)
- self._log(DEBUG, 'symlink({!r}, {!r})'.format(source, dest))
+ self._log(DEBUG, "symlink({!r}, {!r})".format(source, dest))
source = b(source)
self._request(CMD_SYMLINK, source, dest)
@@ -502,7 +535,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param int mode: new permissions
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'chmod({!r}, {!r})'.format(path, mode))
+ self._log(DEBUG, "chmod({!r}, {!r})".format(path, mode))
attr = SFTPAttributes()
attr.st_mode = mode
self._request(CMD_SETSTAT, path, attr)
@@ -519,7 +552,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param int gid: new group id
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'chown({!r}, {!r}, {!r})'.format(path, uid, gid))
+ self._log(DEBUG, "chown({!r}, {!r}, {!r})".format(path, uid, gid))
attr = SFTPAttributes()
attr.st_uid, attr.st_gid = uid, gid
self._request(CMD_SETSTAT, path, attr)
@@ -541,7 +574,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
path = self._adjust_cwd(path)
if times is None:
times = (time.time(), time.time())
- self._log(DEBUG, 'utime({!r}, {!r})'.format(path, times))
+ self._log(DEBUG, "utime({!r}, {!r})".format(path, times))
attr = SFTPAttributes()
attr.st_atime, attr.st_mtime = times
self._request(CMD_SETSTAT, path, attr)
@@ -556,7 +589,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param int size: the new size of the file
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'truncate({!r}, {!r})'.format(path, size))
+ self._log(DEBUG, "truncate({!r}, {!r})".format(path, size))
attr = SFTPAttributes()
attr.st_size = size
self._request(CMD_SETSTAT, path, attr)
@@ -571,15 +604,15 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:return: target path, as a `str`
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'readlink({!r})'.format(path))
+ self._log(DEBUG, "readlink({!r})".format(path))
t, msg = self._request(CMD_READLINK, path)
if t != CMD_NAME:
- raise SFTPError('Expected name response')
+ raise SFTPError("Expected name response")
count = msg.get_int()
if count == 0:
return None
if count != 1:
- raise SFTPError('Readlink returned {} results'.format(count))
+ raise SFTPError("Readlink returned {} results".format(count))
return _to_unicode(msg.get_string())
def normalize(self, path):
@@ -595,13 +628,13 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:raises: ``IOError`` -- if the path can't be resolved on the server
"""
path = self._adjust_cwd(path)
- self._log(DEBUG, 'normalize({!r})'.format(path))
+ self._log(DEBUG, "normalize({!r})".format(path))
t, msg = self._request(CMD_REALPATH, path)
if t != CMD_NAME:
- raise SFTPError('Expected name response')
+ raise SFTPError("Expected name response")
count = msg.get_int()
if count != 1:
- raise SFTPError('Realpath returned {} results'.format(count))
+ raise SFTPError("Realpath returned {} results".format(count))
return msg.get_text()
def chdir(self, path=None):
@@ -625,9 +658,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
return
if not stat.S_ISDIR(self.stat(path).st_mode):
code = errno.ENOTDIR
- raise SFTPError(
- code, "{}: {}".format(os.strerror(code), path)
- )
+ raise SFTPError(code, "{}: {}".format(os.strerror(code), path))
self._cwd = b(self.normalize(path))
def getcwd(self):
@@ -680,7 +711,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionadded:: 1.10
"""
- with self.file(remotepath, 'wb') as fr:
+ with self.file(remotepath, "wb") as fr:
fr.set_pipelined(True)
size = self._transfer_with_callback(
reader=fl, writer=fr, file_size=file_size, callback=callback
@@ -689,7 +720,8 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
s = self.stat(remotepath)
if s.st_size != size:
raise IOError(
- 'size mismatch in put! {} != {}'.format(s.st_size, size))
+ "size mismatch in put! {} != {}".format(s.st_size, size)
+ )
else:
s = SFTPAttributes()
return s
@@ -723,7 +755,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
``confirm`` param added.
"""
file_size = os.stat(localpath).st_size
- with open(localpath, 'rb') as fl:
+ with open(localpath, "rb") as fl:
return self.putfo(fl, remotepath, file_size, callback, confirm)
def getfo(self, remotepath, fl, callback=None):
@@ -744,7 +776,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionadded:: 1.10
"""
file_size = self.stat(remotepath).st_size
- with self.open(remotepath, 'rb') as fr:
+ with self.open(remotepath, "rb") as fr:
fr.prefetch(file_size)
return self._transfer_with_callback(
reader=fr, writer=fl, file_size=file_size, callback=callback
@@ -766,12 +798,13 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
.. versionchanged:: 1.7.4
Added the ``callback`` param
"""
- with open(localpath, 'wb') as fl:
+ with open(localpath, "wb") as fl:
size = self.getfo(remotepath, fl, callback)
s = os.stat(localpath)
if s.st_size != size:
raise IOError(
- 'size mismatch in get! {} != {}'.format(s.st_size, size))
+ "size mismatch in get! {} != {}".format(s.st_size, size)
+ )
# ...internals...
@@ -809,7 +842,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
try:
t, data = self._read_packet()
except EOFError as e:
- raise SSHException('Server connection dropped: {}'.format(e))
+ raise SSHException("Server connection dropped: {}".format(e))
msg = Message(data)
num = msg.get_int()
self._lock.acquire()
@@ -817,7 +850,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
if num not in self._expecting:
# might be response for a file that was closed before
# responses came back
- self._log(DEBUG, 'Unexpected response #{}'.format(num))
+ self._log(DEBUG, "Unexpected response #{}".format(num))
if waitfor is None:
# just doing a single check
break