Skip to content

Commit 43140ba

Browse files
authored
Merge pull request #866 from dbcli/paramiko-compatibility
Changed paramiko usage to work with older versions
2 parents 8cd661b + e06071d commit 43140ba

File tree

4 files changed

+60
-47
lines changed

4 files changed

+60
-47
lines changed

mycli/main.py

Lines changed: 28 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
try:
6666
import paramiko
6767
except ImportError:
68-
paramiko = False
68+
from mycli.packages.paramiko_stub import paramiko
6969

7070
# Query tuples are used for maintaining history
7171
Query = namedtuple('Query', ['query', 'successful', 'mutating'])
@@ -1091,22 +1091,7 @@ def cli(database, user, host, port, socket, password, dbname,
10911091
click.secho(alias)
10921092
sys.exit(0)
10931093
if list_ssh_config:
1094-
if not paramiko:
1095-
click.secho(
1096-
"This features requires paramiko. Please install paramiko and try again.",
1097-
err=True, fg='red'
1098-
)
1099-
exit(1)
1100-
try:
1101-
ssh_config = paramiko.config.SSHConfig().from_path(ssh_config_path)
1102-
except paramiko.ssh_exception.ConfigParseError as err:
1103-
click.secho('Invalid SSH configuration file. '
1104-
'Please check the SSH configuration file.',
1105-
err=True, fg='red')
1106-
exit(1)
1107-
except FileNotFoundError as e:
1108-
click.secho(str(e), err=True, fg='red')
1109-
exit(1)
1094+
ssh_config = read_ssh_config(ssh_config_path)
11101095
for host in ssh_config.get_hostnames():
11111096
if verbose:
11121097
host_config = ssh_config.lookup(host)
@@ -1166,38 +1151,16 @@ def cli(database, user, host, port, socket, password, dbname,
11661151
port = uri.port
11671152

11681153
if ssh_config_host:
1169-
if not paramiko:
1170-
click.secho(
1171-
"This features requires paramiko. Please install paramiko and try again.",
1172-
err=True, fg='red'
1173-
)
1174-
exit(1)
1175-
try:
1176-
ssh_config = paramiko.config.SSHConfig().from_path(ssh_config_path)
1177-
except paramiko.ssh_exception.ConfigParseError as err:
1178-
click.secho('Invalid SSH configuration file. '
1179-
'Please check the SSH configuration file.',
1180-
err=True, fg='red')
1181-
exit(1)
1182-
except FileNotFoundError as e:
1183-
click.secho(str(e), err=True, fg='red')
1184-
exit(1)
1185-
ssh_config = ssh_config.lookup(ssh_config_host)
1154+
ssh_config = read_ssh_config(
1155+
ssh_config_path
1156+
).lookup(ssh_config_host)
11861157
ssh_host = ssh_host if ssh_host else ssh_config.get('hostname')
11871158
ssh_user = ssh_user if ssh_user else ssh_config.get('user')
11881159
if ssh_config.get('port') and ssh_port == 22:
11891160
# port has a default value, overwrite it if it's in the config
11901161
ssh_port = int(ssh_config.get('port'))
11911162
ssh_key_filename = ssh_key_filename if ssh_key_filename else ssh_config.get(
1192-
'identityfile', [''])[0]
1193-
1194-
if not paramiko and ssh_host:
1195-
click.secho(
1196-
"Cannot use SSH transport because paramiko isn't installed, "
1197-
"please install paramiko or don't use --ssh-host=",
1198-
err=True, fg="red"
1199-
)
1200-
exit(1)
1163+
'identityfile', [None])[0]
12011164

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

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

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

13341298

1299+
def read_ssh_config(ssh_config_path):
1300+
ssh_config = paramiko.config.SSHConfig()
1301+
try:
1302+
with open(ssh_config_path) as f:
1303+
ssh_config.parse(f)
1304+
# Paramiko prior to version 2.7 raises Exception on parse errors.
1305+
# In 2.7 it has become paramiko.ssh_exception.SSHException,
1306+
# but let's catch everything for compatibility
1307+
except Exception as err:
1308+
click.secho(
1309+
f'Could not parse SSH configuration file {ssh_config_path}:\n{err} ',
1310+
err=True, fg='red'
1311+
)
1312+
sys.exit(1)
1313+
except FileNotFoundError as e:
1314+
click.secho(str(e), err=True, fg='red')
1315+
sys.exit(1)
1316+
else:
1317+
return ssh_config
1318+
1319+
13351320
if __name__ == "__main__":
13361321
cli()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""A module to import instead of paramiko when it is not available (to avoid
2+
checking for paramiko all over the place).
3+
4+
When paramiko is first envoked, it simply shuts down mycli, telling
5+
user they either have to install paramiko or should not use SSH
6+
features.
7+
8+
"""
9+
10+
11+
class Paramiko:
12+
def __getattr__(self, name):
13+
import sys
14+
from textwrap import dedent
15+
print(dedent("""
16+
To enable certain SSH features you need to install paramiko:
17+
18+
pip install paramiko
19+
20+
It is required for the following configuration options:
21+
--list-ssh-config
22+
--ssh-config-host
23+
--ssh-host
24+
"""))
25+
sys.exit(1)
26+
27+
28+
paramiko = Paramiko()

mycli/sqlexecute.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
decoders)
99
try:
1010
import paramiko
11-
except:
12-
paramiko = False
11+
except ImportError:
12+
from mycli.packages.paramiko_stub import paramiko
1313

1414
_logger = logging.getLogger(__name__)
1515

@@ -118,7 +118,7 @@ def connect(self, database=None, user=None, password=None, host=None,
118118
defer_connect=defer_connect
119119
)
120120

121-
if ssh_host and paramiko:
121+
if ssh_host:
122122
client = paramiko.SSHClient()
123123
client.load_system_host_keys()
124124
client.set_missing_host_key_policy(paramiko.WarningPolicy())

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def initialize_options(self):
6969

7070
def run_tests(self):
7171
unit_test_errno = subprocess.call(
72-
'pytest ' + self.pytest_args,
72+
'pytest test/ ' + self.pytest_args,
7373
shell=True
7474
)
7575
cli_errno = subprocess.call(

0 commit comments

Comments
 (0)