Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non standalone exit #533

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from .types import convert_type, IntRange, BOOL
from .utils import make_str, make_default_short_help, echo, get_os_args
from .exceptions import ClickException, UsageError, BadParameter, Abort, \
MissingParameter
MissingParameter, Exit
from .termui import prompt, confirm
from .formatting import HelpFormatter, join_options
from .parser import OptionParser, split_opt
Expand Down Expand Up @@ -480,7 +480,7 @@ def abort(self):

def exit(self, code=0):
"""Exits the application with a given exit code."""
sys.exit(code)
raise Exit(code, self)

def get_usage(self):
"""Helper method to get formatted usage string for the current
Expand Down Expand Up @@ -700,6 +700,12 @@ def main(self, args=None, prog_name=None, complete_var=None,
except (EOFError, KeyboardInterrupt):
echo(file=sys.stderr)
raise Abort()
except Exit as e:

This comment was marked as off-topic.

if not standalone_mode:

This comment was marked as off-topic.

if e.exit_code:
raise

This comment was marked as off-topic.

return None
sys.exit(e.exit_code)
except ClickException as e:
if not standalone_mode:
raise
Expand Down
43 changes: 36 additions & 7 deletions click/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class ClickException(Exception):

#: The exit code for this exception
exit_code = 1
show_prefix ='Error: '

def __init__(self, message):
ctor_msg = message
Expand All @@ -28,19 +29,17 @@ def __str__(self):
def show(self, file=None):
if file is None:
file = get_text_stderr()
echo('Error: %s' % self.format_message(), file=file)
echo('%s%s' % (self.show_prefix, self.format_message()), file=file)


class UsageError(ClickException):
"""An internal exception that signals a usage error. This typically
aborts any further handling.
class ContextAwareException(ClickException):
"""An exception that knows how to use a :class:`~click.Context` object
to properly display itself.

:param message: the error message to display.
:param ctx: optionally the context that caused this error. Click will
fill in the context automatically in some situations.
"""
exit_code = 2

def __init__(self, message, ctx=None):
ClickException.__init__(self, message)
self.ctx = ctx
Expand All @@ -52,7 +51,37 @@ def show(self, file=None):
if self.ctx is not None:
color = self.ctx.color
echo(self.ctx.get_usage() + '\n', file=file, color=color)
echo('Error: %s' % self.format_message(), file=file, color=color)
echo(
'%s%s' % (self.show_prefix, self.format_message()),
file=file,
color=color,
)


class Exit(ContextAwareException):
"""An exception that indicates that the application should exit with some
status code.

:param code: the status code to exit with.
:param ctx: optionally the context that caused this error. Click will
fill in the context automatically in some situations.
"""
show_prefix = 'Exit: '

def __init__(self, code, ctx=None):
ContextAwareException.__init__(self, str(code), ctx=None)

This comment was marked as off-topic.

self.exit_code = code


class UsageError(ContextAwareException):
"""An internal exception that signals a usage error. This typically
aborts any further handling.

:param message: the error message to display.
:param ctx: optionally the context that caused this error. Click will
fill in the context automatically in some situations.
"""
exit_code = 2


class BadParameter(UsageError):
Expand Down
26 changes: 26 additions & 0 deletions tests/test_context.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
import pytest

import click


Expand Down Expand Up @@ -203,3 +205,27 @@ def foo():
assert not result.exception
assert result.output == 'aha!\n'
assert called == [True]


def test_exit_not_standalone_failure():
@click.command()
@click.pass_context
def cli(ctx):
ctx.exit(1)

with pytest.raises(click.exceptions.Exit) as e:
cli.main([], 'test_exit_not_standalone', standalone_mode=False)
assert e.value.exit_code == 1


def test_exit_not_standalone_success():
@click.command()
@click.pass_context
def cli(ctx):
ctx.exit(0)

assert cli.main(
[],
'test_exit_not_standalone',
standalone_mode=False,
) is None
30 changes: 30 additions & 0 deletions tests/test_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,34 @@ def cli_string():
click.echo('hello world')
sys.exit('error')

@click.command()
@click.pass_context
def cli_string_ctx_exit(ctx):
click.echo('hello world')
ctx.exit('error')

@click.command()
def cli_int():
click.echo('hello world')
sys.exit(1)

@click.command()
@click.pass_context
def cli_int_ctx_exit(ctx):
click.echo('hello world')
ctx.exit(1)

@click.command()
def cli_float():
click.echo('hello world')
sys.exit(1.0)

@click.command()
@click.pass_context
def cli_float_ctx_exit(ctx):
click.echo('hello world')
ctx.exit(1.0)

@click.command()
def cli_no_error():
click.echo('hello world')
Expand All @@ -172,14 +190,26 @@ def cli_no_error():
assert result.exit_code == 1
assert result.output == 'hello world\nerror\n'

result = runner.invoke(cli_string_ctx_exit)
assert result.exit_code == 1
assert result.output == 'hello world\nerror\n'

result = runner.invoke(cli_int)
assert result.exit_code == 1
assert result.output == 'hello world\n'

result = runner.invoke(cli_int_ctx_exit)
assert result.exit_code == 1
assert result.output == 'hello world\n'

result = runner.invoke(cli_float)
assert result.exit_code == 1
assert result.output == 'hello world\n1.0\n'

result = runner.invoke(cli_float_ctx_exit)
assert result.exit_code == 1
assert result.output == 'hello world\n1.0\n'

result = runner.invoke(cli_no_error)
assert result.exit_code == 0
assert result.output == 'hello world\n'