diff options
author | Jeff Forcier <jeff@bitprophet.org> | 2019-07-02 21:19:50 -0400 |
---|---|---|
committer | Jeff Forcier <jeff@bitprophet.org> | 2019-08-26 20:48:25 -0400 |
commit | 77ea8aca1c78dcc1f00b675e3212d253018f3004 (patch) | |
tree | 7d306949b5c334387c713f98cb62875e5c3f1b1c | |
parent | 5a56eed757c598301f1cc1f5a31e9d06aa081ac1 (diff) |
Add new SSHConfig constructors
-rw-r--r-- | paramiko/config.py | 45 | ||||
-rw-r--r-- | paramiko/util.py | 3 | ||||
-rw-r--r-- | sites/www/changelog.rst | 4 | ||||
-rw-r--r-- | tests/test_config.py | 53 |
4 files changed, 88 insertions, 17 deletions
diff --git a/paramiko/config.py b/paramiko/config.py index af194452..f9ea02dc 100644 --- a/paramiko/config.py +++ b/paramiko/config.py @@ -49,9 +49,54 @@ class SSHConfig(object): def __init__(self): """ Create a new OpenSSH config object. + + Note: the newer alternate constructors `from_path`, `from_file` and + `from_text` are simpler to use, as they parse on instantiation. For + example, instead of:: + + config = SSHConfig() + config.parse(open("some-path.config") + + you could:: + + config = SSHConfig.from_file(open("some-path.config")) + # Or more directly: + config = SSHConfig.from_path("some-path.config") + # Or if you have arbitrary ssh_config text from some other source: + config = SSHConfig.from_text("Host foo\\n\\tUser bar") """ self._config = [] + @classmethod + def from_text(cls, text): + """ + Create a new, parsed `SSHConfig` from ``text`` string. + + .. versionadded:: 2.7 + """ + return cls.from_file(StringIO(text)) + + @classmethod + def from_path(cls, path): + """ + Create a new, parsed `SSHConfig` from the file found at ``path``. + + .. versionadded:: 2.7 + """ + with open(path) as flo: + return cls.from_file(flo) + + @classmethod + def from_file(cls, flo): + """ + Create a new, parsed `SSHConfig` from file-like object ``flo``. + + .. versionadded:: 2.7 + """ + obj = cls() + obj.parse(flo) + return obj + def parse(self, file_obj): """ Read an OpenSSH config from the given file object. diff --git a/paramiko/util.py b/paramiko/util.py index 29c52bfb..93970289 100644 --- a/paramiko/util.py +++ b/paramiko/util.py @@ -194,6 +194,9 @@ def load_host_keys(filename): def parse_ssh_config(file_obj): """ Provided only as a backward-compatible wrapper around `.SSHConfig`. + + .. deprecated:: 2.7 + Use `SSHConfig.from_file` instead. """ config = SSHConfig() config.parse(file_obj) diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst index e664f7d4..6718f888 100644 --- a/sites/www/changelog.rst +++ b/sites/www/changelog.rst @@ -2,6 +2,10 @@ Changelog ========= +- :feature:`-` Add new convenience classmethod constructors to + `~paramiko.config.SSHConfig`: `~paramiko.config.SSHConfig.from_text`, + `~paramiko.config.SSHConfig.from_file`, and + `~paramiko.config.SSHConfig.from_path`. No more annoying two-step process! - :release:`2.6.0 <2019-06-23>` - :feature:`1463` Add a new keyword argument to `SSHClient.connect <paramiko.client.SSHClient.connect>` and `~paramiko.transport.Transport`, diff --git a/tests/test_config.py b/tests/test_config.py index cecb6204..185c0173 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -9,7 +9,7 @@ from pytest import raises, mark from paramiko.py3compat import StringIO from paramiko import SSHConfig, SSHConfigDict -from paramiko.util import lookup_ssh_host_config, parse_ssh_config +from paramiko.util import lookup_ssh_host_config from .util import _support @@ -21,8 +21,27 @@ class TestSSHConfig(object): def teardown(self): self.config_flo.close() + def test_init(self): + # No args! + with raises(TypeError): + SSHConfig("uh oh!") + # No args. + assert not SSHConfig()._config + + def test_from_text(self): + config = SSHConfig.from_text(self.config_flo.read()) + assert config.lookup("foo.example.com")["user"] == "robey" + + def test_from_file(self): + config = SSHConfig.from_file(self.config_flo) + assert config.lookup("whatever")["user"] == "robey" + + def test_from_path(self): + config = SSHConfig.from_path(_support("robey.config")) + assert config.lookup("meh.example.com")["port"] == "3333" + def test_parse_config(self): - config = parse_ssh_config(self.config_flo) + config = SSHConfig.from_file(self.config_flo) expected = [ {"host": ["*"], "config": {}}, { @@ -42,7 +61,7 @@ class TestSSHConfig(object): assert config._config == expected def test_host_config(self): - config = parse_ssh_config(self.config_flo) + config = SSHConfig.from_file(self.config_flo) for host, values in { "irc.danger.com": { @@ -82,7 +101,7 @@ Host * Port 3333 """ f = StringIO(test_config_file) - config = parse_ssh_config(f) + config = SSHConfig.from_file(f) host = "www13.example.com" expected = {"hostname": host, "port": "22"} assert lookup_ssh_host_config(host, config) == expected @@ -99,7 +118,7 @@ Host equals-delimited ProxyCommand=foo bar=biz baz """ f = StringIO(conf) - config = parse_ssh_config(f) + config = SSHConfig.from_file(f) for host in ("space-delimited", "equals-delimited"): value = lookup_ssh_host_config(host, config)["proxycommand"] assert value == "foo bar=biz baz" @@ -108,7 +127,7 @@ Host equals-delimited """ ProxyCommand should perform interpolation on the value """ - config = parse_ssh_config( + config = SSHConfig.from_file( StringIO( """ Host specific @@ -135,7 +154,7 @@ Host * """ Tilde (~) should be expanded inside ProxyCommand """ - config = parse_ssh_config( + config = SSHConfig.from_file( StringIO( """ Host test @@ -164,7 +183,7 @@ Host * Port 3333 """ f = StringIO(test_config_file) - config = parse_ssh_config(f) + config = SSHConfig.from_file(f) host = "www13.example.com" expected = {"hostname": host, "port": "8080"} assert lookup_ssh_host_config(host, config) == expected @@ -196,7 +215,7 @@ ProxyCommand foo=bar:%h-%p }.items(): f = StringIO(test_config_file) - config = parse_ssh_config(f) + config = SSHConfig.from_file(f) assert lookup_ssh_host_config(host, config) == values def test_host_config_test_identityfile(self): @@ -226,7 +245,7 @@ IdentityFile id_dsa22 }.items(): f = StringIO(test_config_file) - config = parse_ssh_config(f) + config = SSHConfig.from_file(f) assert lookup_ssh_host_config(host, config) == values def test_config_addressfamily_and_lazy_fqdn(self): @@ -237,7 +256,7 @@ IdentityFile id_dsa22 AddressFamily inet IdentityFile something_%l_using_fqdn """ - config = parse_ssh_config(StringIO(test_config)) + config = SSHConfig.from_file(StringIO(test_config)) assert config.lookup( "meh" ) # will die during lookup() if bug regresses @@ -249,7 +268,7 @@ IdentityFile something_%l_using_fqdn assert config.lookup("abcqwerty")["hostname"] == "127.0.0.1" def test_get_hostnames(self): - config = parse_ssh_config(self.config_flo) + config = SSHConfig.from_file(self.config_flo) expected = {"*", "*.example.com", "spoo.example.com"} assert config.get_hostnames() == expected @@ -281,7 +300,7 @@ Host param4 "p a r" "p" "par" para "para": {"hostname": "para", "port": "4444"}, } f = StringIO(test_config_file) - config = parse_ssh_config(f) + config = SSHConfig.from_file(f) for host, values in res.items(): assert lookup_ssh_host_config(host, config) == values @@ -312,7 +331,7 @@ Host param3 parara }, } f = StringIO(test_config_file) - config = parse_ssh_config(f) + config = SSHConfig.from_file(f) for host, values in res.items(): assert lookup_ssh_host_config(host, config) == values @@ -356,7 +375,7 @@ Host proxycommand-with-equals-none }.items(): f = StringIO(test_config_file) - config = parse_ssh_config(f) + config = SSHConfig.from_file(f) assert lookup_ssh_host_config(host, config) == values def test_proxycommand_none_masking(self): @@ -428,7 +447,7 @@ class TestSSHConfigDict(object): Port 3333 """ f = StringIO(test_config_file) - config = parse_ssh_config(f) + config = SSHConfig.from_file(f) assert config.lookup("foo.example.com").as_int("port") == 2222 def test_SSHConfig_wildcard_host_dicts_are_SSHConfigDict_instances(self): @@ -440,5 +459,5 @@ class TestSSHConfigDict(object): Port 3333 """ f = StringIO(test_config_file) - config = parse_ssh_config(f) + config = SSHConfig.from_file(f) assert config.lookup("anything-else").as_int("port") == 3333 |