diff options
-rw-r--r-- | paramiko/config.py | 36 | ||||
-rw-r--r-- | tests/test_config.py | 74 |
2 files changed, 109 insertions, 1 deletions
diff --git a/paramiko/config.py b/paramiko/config.py index 21c9dab8..71973533 100644 --- a/paramiko/config.py +++ b/paramiko/config.py @@ -119,7 +119,7 @@ class SSHConfig(object): if self._allowed(config["host"], hostname) ] - ret = {} + ret = SSHConfigDict() for match in matches: for key, value in match["config"].items(): if key not in ret: @@ -291,3 +291,37 @@ class LazyFqdn(object): # Cache self.fqdn = fqdn return self.fqdn + + +class SSHConfigDict(dict): + """ + A dictionary wrapper for ssh host configurations. + + This class introduces some usage niceties for consumers of SSHConfig, + specifically around the issue of variable type conversions. This offers + as_bool(key) and as_int(key) for the current raw string values in + SSHConfig + """ + + def __init__(self, *args, **kwargs): + # Hey, guess what? Python 2's userdict is an old-style class! + super(SSHConfigDict, self).__init__(*args, **kwargs) + + def as_bool(self, key): + """ + Express the key as a boolean value. + + Variations on 'yes' or boolean values are accepted. + """ + val = self[key] + if isinstance(val, bool): + return val + return val.lower() == 'yes' + + def as_int(self, key): + """ + Express the key as a true integer, if possible. + + Raises an Error otherwise (following conventional int conversion rules) + """ + return int(self[key]) diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 00000000..20f051fc --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,74 @@ +# This file is part of Paramiko and subject to the license in /LICENSE in this +# repository + +import pytest + +from paramiko import config +from paramiko.util import parse_ssh_config +from paramiko.py3compat import StringIO + +def test_SSHConfigDict_construct_empty(): + assert not config.SSHConfigDict() + + +def test_SSHConfigDict_construct_from_list(): + assert config.SSHConfigDict([(1, 2)])[1] == 2 + + +def test_SSHConfigDict_construct_from_dict(): + assert config.SSHConfigDict({1: 2})[1] == 2 + + +@pytest.mark.parametrize("true_ish", ("yes", "YES", "Yes", True)) +def test_SSHConfigDict_as_bool_true_ish(true_ish): + assert config.SSHConfigDict({"key": true_ish}).as_bool("key") is True + + +@pytest.mark.parametrize("false_ish", ("no", "NO", "No", False)) +def test_SSHConfigDict_as_bool(false_ish): + assert config.SSHConfigDict({"key": false_ish}).as_bool("key") is False + + +@pytest.mark.parametrize("int_val", ("42", 42)) +def test_SSHConfigDict_as_int(int_val): + assert config.SSHConfigDict({"key": int_val}).as_int("key") == 42 + + +@pytest.mark.parametrize("non_int", ("not an int", None, object())) +def test_SSHConfigDict_as_int_failures(non_int): + conf = config.SSHConfigDict({"key": non_int}) + + try: + int(non_int) + except Exception as e: + exception_type = type(e) + + with pytest.raises(exception_type): + conf.as_int("key") + + +def test_SSHConfig_host_dicts_are_SSHConfigDict_instances(): + test_config_file = """ +Host *.example.com + Port 2222 + +Host * + Port 3333 + """ + f = StringIO(test_config_file) + config = parse_ssh_config(f) + assert config.lookup("foo.example.com").as_int("port") == 2222 + + +def test_SSHConfig_wildcard_host_dicts_are_SSHConfigDict_instances(): + test_config_file = """\ +Host *.example.com + Port 2222 + +Host * + Port 3333 + """ + f = StringIO(test_config_file) + config = parse_ssh_config(f) + assert config.lookup("anything-else").as_int("port") == 3333 + |