Skip to content

Commit

Permalink
Merge pull request #866 from dbcli/paramiko-compatibility
Browse files Browse the repository at this point in the history
Changed paramiko usage to work with older versions
  • Loading branch information
amjith authored May 23, 2020
2 parents 8cd661b + e06071d commit 43140ba
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 47 deletions.
71 changes: 28 additions & 43 deletions mycli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
try:
import paramiko
except ImportError:
paramiko = False
from mycli.packages.paramiko_stub import paramiko

# Query tuples are used for maintaining history
Query = namedtuple('Query', ['query', 'successful', 'mutating'])
Expand Down Expand Up @@ -1091,22 +1091,7 @@ def cli(database, user, host, port, socket, password, dbname,
click.secho(alias)
sys.exit(0)
if list_ssh_config:
if not paramiko:
click.secho(
"This features requires paramiko. Please install paramiko and try again.",
err=True, fg='red'
)
exit(1)
try:
ssh_config = paramiko.config.SSHConfig().from_path(ssh_config_path)
except paramiko.ssh_exception.ConfigParseError as err:
click.secho('Invalid SSH configuration file. '
'Please check the SSH configuration file.',
err=True, fg='red')
exit(1)
except FileNotFoundError as e:
click.secho(str(e), err=True, fg='red')
exit(1)
ssh_config = read_ssh_config(ssh_config_path)
for host in ssh_config.get_hostnames():
if verbose:
host_config = ssh_config.lookup(host)
Expand Down Expand Up @@ -1166,38 +1151,16 @@ def cli(database, user, host, port, socket, password, dbname,
port = uri.port

if ssh_config_host:
if not paramiko:
click.secho(
"This features requires paramiko. Please install paramiko and try again.",
err=True, fg='red'
)
exit(1)
try:
ssh_config = paramiko.config.SSHConfig().from_path(ssh_config_path)
except paramiko.ssh_exception.ConfigParseError as err:
click.secho('Invalid SSH configuration file. '
'Please check the SSH configuration file.',
err=True, fg='red')
exit(1)
except FileNotFoundError as e:
click.secho(str(e), err=True, fg='red')
exit(1)
ssh_config = ssh_config.lookup(ssh_config_host)
ssh_config = read_ssh_config(
ssh_config_path
).lookup(ssh_config_host)
ssh_host = ssh_host if ssh_host else ssh_config.get('hostname')
ssh_user = ssh_user if ssh_user else ssh_config.get('user')
if ssh_config.get('port') and ssh_port == 22:
# port has a default value, overwrite it if it's in the config
ssh_port = int(ssh_config.get('port'))
ssh_key_filename = ssh_key_filename if ssh_key_filename else ssh_config.get(
'identityfile', [''])[0]

if not paramiko and ssh_host:
click.secho(
"Cannot use SSH transport because paramiko isn't installed, "
"please install paramiko or don't use --ssh-host=",
err=True, fg="red"
)
exit(1)
'identityfile', [None])[0]

ssh_key_filename = ssh_key_filename and os.path.expanduser(ssh_key_filename)

Expand Down Expand Up @@ -1308,6 +1271,7 @@ def is_mutating(status):
'replace', 'truncate', 'load', 'rename'])
return status.split(None, 1)[0].lower() in mutating


def is_select(status):
"""Returns true if the first word in status is 'select'."""
if not status:
Expand All @@ -1332,5 +1296,26 @@ def edit_and_execute(event):
buff.open_in_editor(validate_and_handle=False)


def read_ssh_config(ssh_config_path):
ssh_config = paramiko.config.SSHConfig()
try:
with open(ssh_config_path) as f:
ssh_config.parse(f)
# Paramiko prior to version 2.7 raises Exception on parse errors.
# In 2.7 it has become paramiko.ssh_exception.SSHException,
# but let's catch everything for compatibility
except Exception as err:
click.secho(
f'Could not parse SSH configuration file {ssh_config_path}:\n{err} ',
err=True, fg='red'
)
sys.exit(1)
except FileNotFoundError as e:
click.secho(str(e), err=True, fg='red')
sys.exit(1)
else:
return ssh_config


if __name__ == "__main__":
cli()
28 changes: 28 additions & 0 deletions mycli/packages/paramiko_stub/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""A module to import instead of paramiko when it is not available (to avoid
checking for paramiko all over the place).
When paramiko is first envoked, it simply shuts down mycli, telling
user they either have to install paramiko or should not use SSH
features.
"""


class Paramiko:
def __getattr__(self, name):
import sys
from textwrap import dedent
print(dedent("""
To enable certain SSH features you need to install paramiko:
pip install paramiko
It is required for the following configuration options:
--list-ssh-config
--ssh-config-host
--ssh-host
"""))
sys.exit(1)


paramiko = Paramiko()
6 changes: 3 additions & 3 deletions mycli/sqlexecute.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
decoders)
try:
import paramiko
except:
paramiko = False
except ImportError:
from mycli.packages.paramiko_stub import paramiko

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -118,7 +118,7 @@ def connect(self, database=None, user=None, password=None, host=None,
defer_connect=defer_connect
)

if ssh_host and paramiko:
if ssh_host:
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def initialize_options(self):

def run_tests(self):
unit_test_errno = subprocess.call(
'pytest ' + self.pytest_args,
'pytest test/ ' + self.pytest_args,
shell=True
)
cli_errno = subprocess.call(
Expand Down

0 comments on commit 43140ba

Please sign in to comment.