diff --git a/AUTHORS b/AUTHORS index 3fa60e85..4d5da32b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -28,3 +28,4 @@ Brian Rutledge Peter Stensmyr (http://www.peterstensmyr.com) Felipe Mulinari Rocha Campos Devesh Kumar Singh +Yesha Maggi diff --git a/tests/test_settings.py b/tests/test_settings.py index a17ff388..07fce2de 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -91,14 +91,6 @@ def test_identity_requires_sign(): settings.Settings(sign=False, identity="fakeid") -def test_password_not_required_if_client_cert(entered_password): - """Don't set password when only client_cert is provided.""" - test_client_cert = "/random/path" - settings_obj = settings.Settings(username="fakeuser", client_cert=test_client_cert) - assert not settings_obj.password - assert settings_obj.client_cert == test_client_cert - - @pytest.mark.parametrize("client_cert", [None, ""]) def test_password_is_required_if_no_client_cert(client_cert, entered_password): """Set password when client_cert is not provided.""" @@ -106,16 +98,32 @@ def test_password_is_required_if_no_client_cert(client_cert, entered_password): assert settings_obj.password == "entered pw" -def test_client_cert_is_set_and_password_not_if_both_given(entered_password): +def test_client_cert_and_password_both_set_if_given(): """Set password and client_cert when both are provided.""" client_cert = "/random/path" settings_obj = settings.Settings( username="fakeuser", password="anything", client_cert=client_cert ) - assert not settings_obj.password + assert settings_obj.password == "anything" assert settings_obj.client_cert == client_cert +def test_password_required_if_no_client_cert_and_non_interactive(): + """Raise exception if no password or client_cert when non interactive.""" + settings_obj = settings.Settings(username="fakeuser", non_interactive=True) + with pytest.raises(exceptions.NonInteractive): + settings_obj.password + + +def test_no_password_prompt_if_client_cert_and_non_interactive(entered_password): + """Don't prompt for password when client_cert is provided and non interactive.""" + client_cert = "/random/path" + settings_obj = settings.Settings( + username="fakeuser", client_cert=client_cert, non_interactive=True + ) + assert not settings_obj.password + + class TestArgumentParsing: @staticmethod def parse_args(args): diff --git a/twine/settings.py b/twine/settings.py index 73d4608c..2dac15ef 100644 --- a/twine/settings.py +++ b/twine/settings.py @@ -13,9 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. import argparse +import contextlib import logging import sys from typing import Any +from typing import ContextManager from typing import Optional from typing import cast @@ -144,11 +146,14 @@ def username(self) -> Optional[str]: @property def password(self) -> Optional[str]: - if self.client_cert: - return None - - # Workaround for https://github.com/python/mypy/issues/5858 - return cast(Optional[str], self.auth.password) + with self._allow_noninteractive(): + # Workaround for https://github.com/python/mypy/issues/5858 + return cast(Optional[str], self.auth.password) + + def _allow_noninteractive(self) -> ContextManager[None]: + """Bypass NonInteractive error when client cert is present.""" + suppressed = (exceptions.NonInteractive,) if self.client_cert else () + return contextlib.suppress(*suppressed) @property def verbose(self) -> bool: