diff --git a/tests/test_utils.py b/tests/test_utils.py index 8d70a51d..3d58ddf6 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -222,8 +222,33 @@ def my_import(name, *args, **kwargs): monkeypatch.setattr(builtins, '__import__', my_import) -def test_get_password_keyring_missing_prompts(monkeypatch, keyring_missing): +@pytest.fixture +def entered_password(monkeypatch): monkeypatch.setattr(utils, 'password_prompt', lambda prompt: 'entered pw') - pw = utils.get_password('system', 'user', None, {}) - assert pw == 'entered pw' + +def test_get_password_keyring_missing_prompts( + entered_password, keyring_missing): + assert utils.get_password('system', 'user', None, {}) == 'entered pw' + + +@pytest.fixture +def keyring_no_backends(monkeypatch): + """ + Simulate that keyring has no available backends. When keyring + has no backends for the system, the backend will be a + fail.Keyring, which raises RuntimeError on get_password. + """ + class FailKeyring(object): + @staticmethod + def get_password(system, username): + raise RuntimeError("fail!") + monkeypatch.setitem(sys.modules, 'keyring', FailKeyring()) + + +def test_get_password_runtime_error_suppressed( + entered_password, keyring_no_backends, recwarn): + assert utils.get_password('system', 'user', None, {}) == 'entered pw' + assert len(recwarn) == 1 + warning = recwarn.pop(UserWarning) + assert 'fail!' in str(warning) diff --git a/twine/utils.py b/twine/utils.py index 7318c992..218a37ad 100644 --- a/twine/utils.py +++ b/twine/utils.py @@ -20,7 +20,7 @@ import getpass import sys import argparse - +import warnings try: import configparser @@ -196,7 +196,10 @@ def get_password_from_keyring(system, username): except ImportError: return - return keyring.get_password(system, username) + try: + return keyring.get_password(system, username) + except Exception as exc: + warnings.warn(str(exc)) def password_from_keyring_or_prompt(system, username):