diff --git a/news/2553.behavior.rst b/news/2553.behavior.rst new file mode 100644 index 0000000000..d66edfa2fa --- /dev/null +++ b/news/2553.behavior.rst @@ -0,0 +1 @@ +Make conservative checks of known exceptions when subprocess returns output, so user won't see the whole traceback - just the error. \ No newline at end of file diff --git a/pipenv/core.py b/pipenv/core.py index 768e7ee73a..76ce7e8b47 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -925,9 +925,10 @@ def do_create_virtualenv(python=None, site_packages=False, pypi_mirror=None): ) click.echo(crayons.blue("{0}".format(c.out)), err=True) if c.returncode != 0: - sp.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format(u"Failed creating virtual environment")) + sp.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format("Failed creating virtual environment")) + error = c.err if environments.is_verbose() else exceptions.prettify_exc(c.err) raise exceptions.VirtualenvCreationException( - extra=[crayons.blue("{0}".format(c.err)),] + extra=[crayons.red("{0}".format(error)),] ) else: diff --git a/pipenv/exceptions.py b/pipenv/exceptions.py index 000c57153d..495a46bfbb 100644 --- a/pipenv/exceptions.py +++ b/pipenv/exceptions.py @@ -18,7 +18,11 @@ ) from .vendor.click.types import Path from .vendor.click.utils import echo as click_echo +import vistir +KNOWN_EXCEPTIONS = { + "PermissionError": "Permission denied:", +} def handle_exception(exc_type, exception, traceback, hook=sys.excepthook): if environments.is_verbose() or not issubclass(exc_type, ClickException): @@ -402,3 +406,18 @@ def __init__(self, req=None): ) extra = [crayons.normal(decode_for_output(str(req)))] super(RequirementError, self).__init__(message, extra=extra) + super(ResolutionFailure, self).__init__(fix_utf8(message), extra=extra) + + +def prettify_exc(error): + """Catch known errors and prettify them instead of showing the + entire traceback, for better UX""" + matched_exceptions = [k for k in KNOWN_EXCEPTIONS.keys() if k in error] + if not matched_exceptions: + return "{}".format(vistir.misc.decode_for_output(error)) + errors = [] + for match in matched_exceptions: + _, error, info = error.rpartition(KNOWN_EXCEPTIONS[match]) + errors.append("{} {}".format(error, info)) + + return "\n".join(errors)