11import getpass
22import logging
3+ import re
34
45import pytest
56
@@ -13,23 +14,23 @@ def config() -> utils.RepositoryConfig:
1314 return dict (repository = "system" )
1415
1516
16- def test_get_password_keyring_overrides_prompt (monkeypatch , config ):
17+ def test_get_username_keyring_defers_to_prompt (monkeypatch , entered_username , config ):
1718 class MockKeyring :
1819 @staticmethod
19- def get_password (system , user ):
20- return f" { user } @ { system } sekure pa55word"
20+ def get_credential (system , user ):
21+ return None
2122
2223 monkeypatch .setattr (auth , "keyring" , MockKeyring )
2324
24- pw = auth .Resolver (config , auth .CredentialInput ("user" )).password
25- assert pw == "user@system sekure pa55word "
25+ username = auth .Resolver (config , auth .CredentialInput ()).username
26+ assert username == "entered user "
2627
2728
2829def test_get_password_keyring_defers_to_prompt (monkeypatch , entered_password , config ):
2930 class MockKeyring :
3031 @staticmethod
3132 def get_password (system , user ):
32- return
33+ return None
3334
3435 monkeypatch .setattr (auth , "keyring" , MockKeyring )
3536
@@ -51,7 +52,7 @@ def test_empty_password_bypasses_prompt(monkeypatch, entered_password, config):
5152
5253def test_no_username_non_interactive_aborts (config ):
5354 with pytest .raises (exceptions .NonInteractive ):
54- auth .Private (config , auth .CredentialInput ("user" )).password
55+ auth .Private (config , auth .CredentialInput ()).username
5556
5657
5758def test_no_password_non_interactive_aborts (config ):
@@ -124,55 +125,99 @@ def test_get_password_keyring_missing_non_interactive_aborts(
124125 auth .Private (config , auth .CredentialInput ("user" )).password
125126
126127
127- @pytest .fixture
128- def keyring_no_backends (monkeypatch ):
129- """Simulate missing keyring backend raising RuntimeError on get_password."""
130-
128+ def test_get_username_keyring_runtime_error_logged (
129+ entered_username , monkeypatch , config , caplog
130+ ):
131131 class FailKeyring :
132+ """Simulate missing keyring backend raising RuntimeError on get_credential."""
133+
132134 @staticmethod
133- def get_password (system , username ):
135+ def get_credential (system , username ):
134136 raise RuntimeError ("fail!" )
135137
136- monkeypatch .setattr (auth , "keyring" , FailKeyring () )
138+ monkeypatch .setattr (auth , "keyring" , FailKeyring )
137139
140+ assert auth .Resolver (config , auth .CredentialInput ()).username == "entered user"
138141
139- @pytest .fixture
140- def keyring_no_backends_get_credential (monkeypatch ):
141- """Simulate missing keyring backend raising RuntimeError on get_credential."""
142+ assert re .search (
143+ r"Error getting username from keyring.+Traceback.+RuntimeError: fail!" ,
144+ caplog .text ,
145+ re .DOTALL ,
146+ )
142147
148+
149+ def test_get_password_keyring_runtime_error_logged (
150+ entered_username , entered_password , monkeypatch , config , caplog
151+ ):
143152 class FailKeyring :
153+ """Simulate missing keyring backend raising RuntimeError on get_password."""
154+
144155 @staticmethod
145- def get_credential (system , username ):
156+ def get_password (system , username ):
146157 raise RuntimeError ("fail!" )
147158
148- monkeypatch .setattr (auth , "keyring" , FailKeyring () )
159+ monkeypatch .setattr (auth , "keyring" , FailKeyring )
149160
161+ assert auth .Resolver (config , auth .CredentialInput ()).password == "entered pw"
150162
151- def test_get_username_runtime_error_suppressed (
152- entered_username , keyring_no_backends_get_credential , caplog , config
153- ):
154- assert auth . Resolver ( config , auth . CredentialInput ()). username == "entered user"
155- assert caplog . messages == [ "fail!" ]
163+ assert re . search (
164+ r"Error getting password from keyring.+Traceback.+RuntimeError: fail!" ,
165+ caplog . text ,
166+ re . DOTALL ,
167+ )
156168
157169
158- def test_get_password_runtime_error_suppressed (
159- entered_password , keyring_no_backends , caplog , config
160- ) :
161- assert auth . Resolver ( config , auth . CredentialInput ( "user" )). password == "entered pw"
162- assert caplog . messages == [ "fail!" ]
163-
170+ def _raise_home_key_error ():
171+ """Simulate environment from https://github.com/pypa/twine/issues/889."""
172+ try :
173+ raise KeyError ( "HOME" )
174+ except KeyError :
175+ raise KeyError ( "uid not found: 999" )
164176
165- def test_get_username_return_none (entered_username , monkeypatch , config ):
166- """Prompt for username when it's not in keyring."""
167177
178+ def test_get_username_keyring_key_error_logged (
179+ entered_username , monkeypatch , config , caplog
180+ ):
168181 class FailKeyring :
169182 @staticmethod
170183 def get_credential (system , username ):
171- return None
184+ _raise_home_key_error ()
185+
186+ monkeypatch .setattr (auth , "keyring" , FailKeyring )
172187
173- monkeypatch .setattr (auth , "keyring" , FailKeyring ())
174188 assert auth .Resolver (config , auth .CredentialInput ()).username == "entered user"
175189
190+ assert re .search (
191+ r"Error getting username from keyring"
192+ r".+Traceback"
193+ r".+KeyError: 'HOME'"
194+ r".+KeyError: 'uid not found: 999'" ,
195+ caplog .text ,
196+ re .DOTALL ,
197+ )
198+
199+
200+ def test_get_password_keyring_key_error_logged (
201+ entered_username , entered_password , monkeypatch , config , caplog
202+ ):
203+ class FailKeyring :
204+ @staticmethod
205+ def get_password (system , username ):
206+ _raise_home_key_error ()
207+
208+ monkeypatch .setattr (auth , "keyring" , FailKeyring )
209+
210+ assert auth .Resolver (config , auth .CredentialInput ()).password == "entered pw"
211+
212+ assert re .search (
213+ r"Error getting password from keyring"
214+ r".+Traceback"
215+ r".+KeyError: 'HOME'"
216+ r".+KeyError: 'uid not found: 999'" ,
217+ caplog .text ,
218+ re .DOTALL ,
219+ )
220+
176221
177222def test_logs_cli_values (caplog ):
178223 caplog .set_level (logging .INFO , "twine" )
0 commit comments