diff --git a/pipenv/core.py b/pipenv/core.py index 7a6e7bb9..fcf846c0 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -57,6 +57,7 @@ from .environments import ( SESSION_IS_INTERACTIVE, PIPENV_CACHE_DIR, ) +from ._compat import fix_utf8 from . import exceptions # Packages that should be ignored later. @@ -621,7 +622,7 @@ def ensure_project( err=True, ) else: - click.echo(crayons.red("Deploy aborted."), err=True) + raise exceptions.DeployException sys.exit(1) # Ensure the Pipfile exists. ensure_pipfile( @@ -1499,7 +1500,7 @@ def system_which(command, mult=False): ) assert c.return_code == 0 except AssertionError: - return None if not mult else[] + return None if not mult else [] except TypeError: from .vendor.pythonfinder import Finder finder = Finder() diff --git a/pipenv/exceptions.py b/pipenv/exceptions.py index feb15df3..3f8aafdc 100644 --- a/pipenv/exceptions.py +++ b/pipenv/exceptions.py @@ -1,7 +1,11 @@ # -*- coding=utf-8 -*- + +import six + from ._compat import fix_utf8 from .patched import crayons from .vendor.click import echo as click_echo +from .vendor.click._compat import get_text_stderr from .vendor.click.exceptions import ( Abort, BadOptionUsage, @@ -15,63 +19,162 @@ from .vendor.click.exceptions import ( class PipenvException(ClickException): - def __init__(self, message, *args, **kwargs): - super(PipenvException, self).__init__(message, *args, **kwargs) + message = "{0}: {{1}}".format(crayons.red("Error", bold=True)) + + def __init__(self, *args, message=None, **kwargs): + if not message: + message = "Pipenv encountered a problem and had to exit." + extra = kwargs.pop("extra", []) + message = self.message.format(message) + ClickException.__init__(self, message) + self.extra = extra + + def show(self, file=None): + if file is None: + file = get_text_stderr() + if self.extra: + if isinstance(self.extra, six.string_types): + self.extra = [self.extra,] + for extra in self.extra: + click_echo(extra, file=file) + super(PipenvException, self).show(file=file) -class PipfileNotFound(ClickException): - message = "{0}: Pipfile is missing! Cannot proceed.".format( - crayons.red("Error", bold=True) - ) +class PipenvUsageError(UsageError): + message = "{0}: {{1}}".format(crayons.red("Error", bold=True)) + + def __init__(self, *args, message=None, ctx=None, **kwargs): + if not message: + message = "Pipenv encountered a problem and had to exit." + self.message = self.message.format(message) + extra = kwargs.pop("extra", []) + UsageError.__init__(self, message, ctx) + self.extra = extra + + def show(self, file=None): + if file is None: + file = get_text_stderr() + if self.extra: + if isinstance(self.extra, six.string_types): + self.extra = [self.extra,] + for extra in self.extra: + click_echo(extra, file=file) + super(PipenvUsageError, self).show(file=file) -class LockfileNotFound(ClickException): - message = "{0}: Pipfile.lock is missing! You need to run {1} first.".format( - crayons.red("Error", bold=True), crayons.red("$ pipenv lock", bold=True) - ) +class PipenvFileError(FileError): + def __init__(self, filename, message=None, **kwargs): + extra = kwargs.pop("extra", []) + FileError.__init__(self, filename, hint=message, **kwargs) + self.extra = extra + + def show(self, file=None): + if file is None: + file = get_text_stderr() + if self.extra: + if isinstance(self.extra, six.string_types): + self.extra = [self.extra,] + for extra in self.extra: + click_echo(extra, file=file) + super(PipenvException, self).show(file=file) -class DeployException(ClickException): - message = crayons.normal("Aborting deploy", bold=True) +class PipfileNotFound(PipenvFileError): + def __init__(self, extra=None, **kwargs): + extra = kwargs.pop("extra", []) + message = ("Cannot proceed. Please ensure that a Pipfile exists and is located " + "in your project root directory.") + filename = "Pipfile" + PipenvFileError.__init__(filename, message=message, extra=extra, **kwargs) + + +class LockfileNotFound(PipenvFileError): + def __init__(self, extra=None, **kwargs): + extra = kwargs.pop("extra", []) + message = "You need to run {0} before you can continue.".format( + crayons.red("$ pipenv lock", bold=True) + ) + filename = "Pipfile.lock" + PipenvFileError.__init__(self, filename, message=message, extra=extra, **kwargs) + + +class DeployException(PipenvException): + def __init__(self, message=None, **kwargs): + if not message: + message = crayons.normal("Aborting deploy", bold=True) + extra = kwargs.pop("extra", None) + PipenvException.__init__(self, message=message, extra=extra, **kwargs) class PipenvOptionsError(BadOptionUsage): - def format_message(self): - return "{0}: {1}".format(crayons.red("Warning", bold=True), self.message) + def __init__(self, message=None, *args, **kwargs): + extra = kwargs.pop("extra", []) + BadOptionUsage.__init__(self, message, *args, **kwargs) + self.extra = extra + + def show(self, file=None): + if file is None: + file = get_text_stderr() + if self.extra: + if isinstance(self.extra, six.string_types): + self.extra = [self.extra,] + for extra in self.extra: + click_echo(extra, file=file) + click_echo("{0}: {1}".format(crayons.red("Warning", bold=True), self.message)) -class PipfileException(FileError): - def __init__(self, hint=None): +class PipfileException(PipenvFileError): + def __init__(self, hint=None, **kwargs): from .core import project hint = "{0} {1}".format(crayons.red("ERROR (PACKAGE NOT INSTALLED):"), hint) filename = project.pipfile_location - super(PipfileException, self).__init__(filename, hint) + extra = kwargs.pop("extra", []) + PipfileException.__init__(self, filename, hint, extra=extra, **kwargs) -class SetupException(ClickException): - pass +class SetupException(PipenvException): + def __init__(self, message=None, *args, **kwargs): + PipenvException.__init__(message, *args, **kwargs) -class VirtualenvException(ClickException): - def __init__(self, message=None): +class VirtualenvException(PipenvException): + + def __init__(self, message=None, **kwargs): if not message: message = ( "There was an unexpected error while activating your virtualenv. " "Continuing anyway..." ) - message = fix_utf8( - "{0}: {1}".format(crayons.red("Warning", bold=True), message) - ) - super(VirtualenvException, self).__init__(message) + PipenvException.__init__(self, message, **kwargs) + + def show(self, file=None): + if file is None: + file = get_text_stderr() + if self.extra: + if isinstance(self.extra, six.string_types): + self.extra = [self.extra,] + for extra in self.extra: + click_echo(extra, file=file) + click_echo(fix_utf8( + "{0}: {1}".format(crayons.red("Warning", bold=True), self.message) + )) class VirtualenvActivationException(VirtualenvException): - message = ( - "activate_this.py not found. Your environment is most certainly " - "not activated. Continuing anyway…" - ) + def __init__(self, message=None, **kwargs): + if not message: + message = ( + "activate_this.py not found. Your environment is most certainly " + "not activated. Continuing anyway…" + ) + self.message = message + VirtualenvException.__init__(self, message, **kwargs) class VirtualenvCreationException(VirtualenvException): - message = "Failed to create virtual environment." + def __init__(self, message=None, **kwargs): + if not message: + message = "Failed to create virtual environment." + self.message = message + VirtualenvException.__init__(self, message, **kwargs)