diff --git a/mypy.ini b/mypy.ini index 9db3a7c7..a4043596 100644 --- a/mypy.ini +++ b/mypy.ini @@ -18,6 +18,10 @@ warn_return_any = True no_implicit_reexport = True strict_equality = True +[mypy-colorama] +; https://github.com/tartley/colorama/issues/206 +ignore_missing_imports = True + [mypy-importlib_metadata] ; https://gitlab.com/python-devs/importlib_metadata/-/issues/10 ignore_missing_imports = True diff --git a/setup.cfg b/setup.cfg index fadcc21b..b5e81b26 100644 --- a/setup.cfg +++ b/setup.cfg @@ -43,6 +43,7 @@ install_requires= importlib_metadata; python_version < "3.8" keyring >= 15.1 rfc3986 >= 1.4.0 + colorama >= 0.4.3 setup_requires = setuptools_scm >= 1.15 include_package_data = True diff --git a/tests/test_main.py b/tests/test_main.py index e8869f94..9fa53dcf 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -10,14 +10,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pretend +import sys + +import colorama from twine import __main__ as dunder_main -from twine import cli -from twine import exceptions def test_exception_handling(monkeypatch): - replaced_dispatch = pretend.raiser(exceptions.InvalidConfiguration("foo")) - monkeypatch.setattr(cli, "dispatch", replaced_dispatch) - assert dunder_main.main() == "InvalidConfiguration: foo" + monkeypatch.setattr(sys, "argv", ["twine", "upload", "missing.whl"]) + message = "InvalidDistribution: Cannot find file (or expand pattern): 'missing.whl'" + assert dunder_main.main() == colorama.Fore.RED + message + colorama.Style.RESET_ALL + + +def test_no_color_exception(monkeypatch): + monkeypatch.setattr(sys, "argv", ["twine", "--no-color", "upload", "missing.whl"]) + message = "InvalidDistribution: Cannot find file (or expand pattern): 'missing.whl'" + assert dunder_main.main() == message diff --git a/tox.ini b/tox.ini index 115b31e0..e28ff85b 100644 --- a/tox.ini +++ b/tox.ini @@ -11,6 +11,7 @@ deps = portend pytest-services munch + colorama passenv = PYTEST_ADDOPTS commands = diff --git a/twine/__main__.py b/twine/__main__.py index 1bd71b0c..3a19f9d1 100644 --- a/twine/__main__.py +++ b/twine/__main__.py @@ -15,6 +15,7 @@ import sys from typing import Any +import colorama import requests from twine import cli @@ -25,7 +26,16 @@ def main() -> Any: try: return cli.dispatch(sys.argv[1:]) except (exceptions.TwineException, requests.HTTPError) as exc: - return "{}: {}".format(exc.__class__.__name__, exc.args[0]) + return _format_error(f"{exc.__class__.__name__}: {exc.args[0]}") + + +def _format_error(message: str) -> str: + pre_style, post_style = "", "" + if not cli.args.no_color: + colorama.init() + pre_style, post_style = colorama.Fore.RED, colorama.Style.RESET_ALL + + return f"{pre_style}{message}{post_style}" if __name__ == "__main__": diff --git a/twine/cli.py b/twine/cli.py index 936d859f..7ff20855 100644 --- a/twine/cli.py +++ b/twine/cli.py @@ -27,6 +27,8 @@ import twine from twine import _installed +args = argparse.Namespace() + def _registered_commands( group: str = "twine.registered_commands", @@ -59,6 +61,13 @@ def dispatch(argv: List[str]) -> Any: action="version", version="%(prog)s version {} ({})".format(twine.__version__, dep_versions()), ) + parser.add_argument( + "--no-color", + default=False, + required=False, + action="store_true", + help="disable colored output", + ) parser.add_argument( "command", choices=registered_commands.keys(), ) @@ -66,7 +75,7 @@ def dispatch(argv: List[str]) -> Any: "args", help=argparse.SUPPRESS, nargs=argparse.REMAINDER, ) - args = parser.parse_args(argv) + parser.parse_args(argv, namespace=args) main = registered_commands[args.command].load()