"""File system hook for the SFTP (SSH) file system."""
from builtins import super
try:
import pysftp
except ImportError:
pysftp = None
from . import FsHook
[docs]class SftpHook(FsHook):
"""Hook for interacting with files over SFTP."""
# TODO: Use walktree for a more efficient walk implementation?
def __init__(self, conn_id):
super().__init__()
self._conn_id = conn_id
self._conn = None
def get_conn(self):
if pysftp is None:
raise ImportError("pysftp must be installed to use the SftpHook")
if self._conn is None:
params = self.get_connection(self._conn_id)
private_key = params.extra_dejson.get('private_key', None)
cnopts = pysftp.CnOpts()
if params.extra_dejson.get('ignore_hostkey_verification', False):
cnopts.hostkeys = None
if not private_key:
self._conn = pysftp.Connection(
params.host,
username=params.login,
password=params.password,
cnopts=cnopts)
elif private_key and params.password:
self._conn = pysftp.Connection(
params.host,
username=params.login,
private_key=private_key,
private_key_pass=params.password,
cnopts=cnopts)
else:
self._conn = pysftp.Connection(
params.host,
username=params.login,
private_key=private_key,
cnopts=cnopts)
return self._conn
[docs] def disconnect(self):
if self._conn is not None:
self._conn.close()
self._conn = None
[docs] def open(self, file_path, mode='rb'):
return self.get_conn().open(file_path, mode=mode)
[docs] def exists(self, file_path):
return self.get_conn().exists(file_path)
[docs] def isdir(self, path):
return self.get_conn().isdir(path)
[docs] def mkdir(self, dir_path, mode=0o755, exist_ok=True):
if self.exists(dir_path):
if not exist_ok:
self._raise_dir_exists(dir_path)
else:
self.get_conn().mkdir(dir_path, mode=self._int_mode(mode))
[docs] def listdir(self, dir_path):
return self.get_conn().listdir(dir_path)
[docs] def rm(self, file_path):
self.get_conn().remove(file_path)
[docs] def rmtree(self, dir_path):
result = self.get_conn().execute('rm -r {!r}'.format(dir_path))
if result:
message = b'\n'.join(result)
raise OSError(message.decode())
# Overridden default implementations.
[docs] def makedirs(self, dir_path, mode=0o755, exist_ok=True):
if self.exists(dir_path):
if not exist_ok:
self._raise_dir_exists(dir_path)
else:
self.get_conn().makedirs(dir_path, mode=self._int_mode(mode))
@staticmethod
def _int_mode(mode):
"""Convert octal mode to its literal int representation."""
return int(oct(mode)[-3:])