diff --git a/tests/test_settings.py b/tests/test_settings.py index ea5c5d31..972b8c76 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import argparse +import logging import os.path import textwrap @@ -55,6 +56,18 @@ def test_settings_transforms_repository_config(tmpdir): assert s.disable_progress_bar is False +@pytest.mark.parametrize( + "verbose, log_level", [(True, logging.INFO), (False, logging.WARNING)] +) +def test_setup_logging(verbose, log_level): + """Set log level based on verbose field.""" + settings.Settings(verbose=verbose) + + logger = logging.getLogger("twine") + + assert logger.level == log_level + + def test_identity_requires_sign(): """Raise an exception when user provides identity but doesn't require sigining.""" with pytest.raises(exceptions.InvalidSigningConfiguration): diff --git a/tests/test_utils.py b/tests/test_utils.py index 13fd5ff7..e6d890eb 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -273,7 +273,12 @@ def test_check_status_code_for_deprecated_pypi_url(repo_url): @pytest.mark.parametrize( "repo_url", ["https://pypi.python.org", "https://testpypi.python.org"], ) -def test_check_status_code_for_missing_status_code(capsys, repo_url): +@pytest.mark.parametrize( + "verbose", [True, False], +) +def test_check_status_code_for_missing_status_code( + capsys, repo_url, verbose, make_settings +): """Print HTTP errors based on verbosity level.""" response = pretend.stub( status_code=403, @@ -282,18 +287,17 @@ def test_check_status_code_for_missing_status_code(capsys, repo_url): text="Forbidden", ) - with pytest.raises(requests.HTTPError): - utils.check_status_code(response, True) - - # Different messages are printed based on the verbose level - captured = capsys.readouterr() - assert captured.out == "Content received from server:\nForbidden\n" + make_settings(verbose=verbose) with pytest.raises(requests.HTTPError): - utils.check_status_code(response, False) + utils.check_status_code(response, verbose) captured = capsys.readouterr() - assert captured.out == "NOTE: Try --verbose to see response content.\n" + + if verbose: + assert captured.out == "Content received from server:\nForbidden\n" + else: + assert captured.out == "NOTE: Try --verbose to see response content.\n" @pytest.mark.parametrize( diff --git a/twine/__init__.py b/twine/__init__.py index 8a934e0d..6d8c7ea8 100644 --- a/twine/__init__.py +++ b/twine/__init__.py @@ -27,7 +27,7 @@ import sys if sys.version_info[:2] >= (3, 8): - import importlib.metadata as importlib_metadata + from importlib import metadata as importlib_metadata else: import importlib_metadata diff --git a/twine/commands/upload.py b/twine/commands/upload.py index 64f928e1..776d6b63 100644 --- a/twine/commands/upload.py +++ b/twine/commands/upload.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import argparse +import logging import os.path from typing import Dict from typing import List @@ -25,6 +26,8 @@ from twine import settings from twine import utils +logger = logging.getLogger(__name__) + def skip_upload( response: requests.Response, skip_existing: bool, package: package_file.PackageFile @@ -63,11 +66,10 @@ def _make_package( elif upload_settings.sign: package.sign(upload_settings.sign_with, upload_settings.identity) - if upload_settings.verbose: - file_size = utils.get_file_size(package.filename) - print(f" {package.filename} ({file_size})") - if package.gpg_signature: - print(f" Signed with {package.signed_filename}") + file_size = utils.get_file_size(package.filename) + logger.info(f" {package.filename} ({file_size})") + if package.gpg_signature: + logger.info(f" Signed with {package.signed_filename}") return package diff --git a/twine/settings.py b/twine/settings.py index 34c23435..73d4608c 100644 --- a/twine/settings.py +++ b/twine/settings.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. import argparse +import logging +import sys from typing import Any from typing import Optional from typing import cast @@ -148,6 +150,18 @@ def password(self) -> Optional[str]: # Workaround for https://github.com/python/mypy/issues/5858 return cast(Optional[str], self.auth.password) + @property + def verbose(self) -> bool: + return self._verbose + + @verbose.setter + def verbose(self, verbose: bool) -> None: + """Initialize a logger based on the --verbose option.""" + self._verbose = verbose + root_logger = logging.getLogger("twine") + root_logger.addHandler(logging.StreamHandler(sys.stdout)) + root_logger.setLevel(logging.INFO if verbose else logging.WARNING) + @staticmethod def register_argparse_arguments(parser: argparse.ArgumentParser) -> None: """Register the arguments for argparse.""" diff --git a/twine/utils.py b/twine/utils.py index ea602368..113b427c 100644 --- a/twine/utils.py +++ b/twine/utils.py @@ -15,6 +15,7 @@ import collections import configparser import functools +import logging import os import os.path from typing import Any @@ -45,6 +46,8 @@ # get_userpass_value. RepositoryConfig = Dict[str, Optional[str]] +logger = logging.getLogger(__name__) + def get_config(path: str = "~/.pypirc") -> Dict[str, RepositoryConfig]: # even if the config file does not exist, set up the parser @@ -200,10 +203,9 @@ def check_status_code(response: requests.Response, verbose: bool) -> None: response.raise_for_status() except requests.HTTPError as err: if response.text: - if verbose: - print("Content received from server:\n{}".format(response.text)) - else: - print("NOTE: Try --verbose to see response content.") + logger.info("Content received from server:\n{}".format(response.text)) + if not verbose: + logger.warning("NOTE: Try --verbose to see response content.") raise err