diff --git a/keyring/cli.py b/keyring/cli.py index 8b3d712c..2c0ba4d3 100644 --- a/keyring/cli.py +++ b/keyring/cli.py @@ -132,14 +132,11 @@ def do_get(self): getattr(self, f'_emit_{self.output_format}')(credential) def _emit_json(self, credential: credentials.Credential): - print( - json.dumps(dict(username=credential.username, password=credential.password)) - ) + print(json.dumps(credential._vars())) def _emit_plain(self, credential: credentials.Credential): - if credential.username: - print(credential.username) - print(credential.password) + for val in credential._vars().values(): + print(val) def _get_creds(self) -> credentials.Credential | None: return get_credential(self.service, self.username) diff --git a/keyring/credentials.py b/keyring/credentials.py index 4b28c6c7..6a2cecd8 100644 --- a/keyring/credentials.py +++ b/keyring/credentials.py @@ -13,6 +13,9 @@ def username(self) -> str: ... @abc.abstractproperty def password(self) -> str: ... + def _vars(self) -> dict[str, str]: + return dict(username=self.username, password=self.password) + class SimpleCredential(Credential): """Simple credentials implementation""" @@ -38,6 +41,9 @@ def __init__(self, password: str): def username(self) -> str: raise ValueError("Anonymous credential has no username") + def _vars(self) -> dict[str, str]: + return dict(password=self.password) + class EnvironCredential(Credential): """ diff --git a/newsfragments/694.bugfix.rst b/newsfragments/694.bugfix.rst new file mode 100644 index 00000000..be4c2559 --- /dev/null +++ b/newsfragments/694.bugfix.rst @@ -0,0 +1 @@ +Fixed ValueError for AnonymousCredentials in CLI. \ No newline at end of file diff --git a/tests/test_cli.py b/tests/test_cli.py index 435c55a3..87c3a2fc 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -6,6 +6,7 @@ import pytest from keyring import cli +from keyring import credentials flatten = itertools.chain.from_iterable @@ -36,6 +37,12 @@ def mocked_set(): yield set_password +@pytest.fixture +def mocked_get_credential(): + with mock.patch('keyring.cli.get_credential') as get_credential: + yield get_credential + + def test_set_interactive(monkeypatch, mocked_set): tool = cli.CommandLineTool() tool.service = 'svc' @@ -64,3 +71,27 @@ def test_set_pipe_newline(monkeypatch, mocked_set): monkeypatch.setattr(sys.stdin, 'read', lambda: 'foo123\n') tool.do_set() mocked_set.assert_called_once_with('svc', 'usr', 'foo123') + + +@pytest.mark.parametrize('format', ['json', 'plain']) +def test_get_anonymous(monkeypatch, mocked_get_credential, format, capsys): + mocked_get_credential.return_value = credentials.AnonymousCredential('s3cret') + tool = cli.CommandLineTool() + tool.service = 'svc' + tool.username = None + tool.get_mode = 'creds' + tool.output_format = format + tool.do_get() + assert 's3cret' in capsys.readouterr().out + + +@pytest.mark.parametrize('format', ['json', 'plain']) +def test_get(monkeypatch, mocked_get_credential, format, capsys): + mocked_get_credential.return_value = credentials.SimpleCredential('alice', 's3cret') + tool = cli.CommandLineTool() + tool.service = 'svc' + tool.username = 'alice' + tool.get_mode = 'creds' + tool.output_format = format + tool.do_get() + assert 's3cret' in capsys.readouterr().out