Skip to content
Merged
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
81 changes: 41 additions & 40 deletions docs/entry-points.rst → docs/entry-points.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
Packaging Entry Points
======================
# Packaging Entry Points

```{eval-rst}
.. currentmodule:: click
```

It's recommended to write command line utilities as installable packages with
entry points instead of telling users to run ``python hello.py``.
Expand All @@ -14,74 +17,72 @@ function. These are known as "entry points". The installer knows how to create
an executable regardless of the operating system, so it will work on Linux,
Windows, MacOS, etc.


Project Files
-------------
## Project Files

To install your app with an entry point, all you need is the script and a
``pyproject.toml`` file. Here's an example project directory:

.. code-block:: text

hello-project/
src/
hello/
__init__.py
hello.py
pyproject.toml
```text
hello-project/
src/
hello/
__init__.py
hello.py
pyproject.toml
```

Contents of ``hello.py``:

```{eval-rst}
.. click:example::

import click

@click.command()
def cli():
"""Prints a greeting."""
click.echo("Hello, World!")
```

Contents of ``pyproject.toml``:

.. code-block:: toml
```toml
[project]
name = "hello"
version = "1.0.0"
description = "Hello CLI"
requires-python = ">=3.11"
dependencies = [
"click>=8.1",
]

[project]
name = "hello"
version = "1.0.0"
description = "Hello CLI"
requires-python = ">=3.11"
dependencies = [
"click>=8.1",
]
[project.scripts]
hello = "hello.hello:cli"

[project.scripts]
hello = "hello.hello:cli"

[build-system]
requires = ["flit_core<4"]
build-backend = "flit_core.buildapi"
[build-system]
requires = ["flit_core<4"]
build-backend = "flit_core.buildapi"
```

The magic is in the ``project.scripts`` section. Each line identifies one executable
script. The first part before the equals sign (``=``) is the name of the script that
should be generated, the second part is the import path followed by a colon
(``:``) with the function to call (the Click command).


Installation
------------
## Installation

When your package is installed, the installer will create an executable script
based on the configuration. During development, you can install in editable
mode using the ``-e`` option. Remember to use a virtual environment!

.. code-block:: console

$ python -m venv .venv
$ . .venv/bin/activate
$ pip install -e .
```console
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install -e .
```

Afterwards, your command should be available:

.. click:run::

invoke(cli, prog_name="hello")
```console
$ hello
Hello, World!
```
136 changes: 136 additions & 0 deletions docs/exceptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
(exception-handling-exit-codes)=

# Exception Handling and Exit Codes

```{eval-rst}
.. currentmodule:: click
```

Click internally uses exceptions to signal various error conditions that
the user of the application might have caused. Primarily this is things
like incorrect usage.

```{contents}
:depth: 1
:local:
```

## Where are Errors Handled?

Click's main error handling is happening in {meth}`Command.main`. In
there it handles all subclasses of {exc}`ClickException` as well as the
standard {exc}`EOFError` and {exc}`KeyboardInterrupt` exceptions. The
latter are internally translated into an {exc}`Abort`.

The logic applied is the following:

1. If an {exc}`EOFError` or {exc}`KeyboardInterrupt` happens, reraise it
as {exc}`Abort`.
2. If a {exc}`ClickException` is raised, invoke the
{meth}`ClickException.show` method on it to display it and then exit
the program with {attr}`ClickException.exit_code`.
3. If an {exc}`Abort` exception is raised print the string ``Aborted!``
to standard error and exit the program with exit code ``1``.
4. If it goes through well, exit the program with exit code ``0``.

## What if I Don't Want That?

Generally you always have the option to invoke the {meth}`Command.invoke`
method yourself. For instance if you have a {class}`Command` you can
invoke it manually like this:

```python
ctx = command.make_context("command-name", ["args", "go", "here"])
with ctx:
result = command.invoke(ctx)
```

In this case exceptions will not be handled at all and bubbled up as you
would expect.

Starting with Click 3.0 you can also use the {meth}`Command.main` method
but disable the standalone mode which will do two things: disable
exception handling and disable the implicit {func}`sys.exit` at the end.

So you can do something like this:

```python
command.main(
["command-name", "args", "go", "here"],
standalone_mode=False,
)
```

## Which Exceptions Exist?

Click has two exception bases: {exc}`ClickException` which is raised for
all exceptions that Click wants to signal to the user and {exc}`Abort`
which is used to instruct Click to abort the execution.

A {exc}`ClickException` has a {meth}`ClickException.show` method which
can render an error message to stderr or the given file object. If you
want to use the exception yourself for doing something check the API docs
about what else they provide.

The following common subclasses exist:

- {exc}`UsageError` to inform the user that something went wrong.
- {exc}`BadParameter` to inform the user that something went wrong with
a specific parameter. These are often handled internally in Click and
augmented with extra information if possible. For instance if those
are raised from a callback Click will automatically augment it with
the parameter name if possible.
- {exc}`FileError` this is an error that is raised by the
{class}`FileType` if Click encounters issues opening the file.

(help-page-exit-codes)=

## Help Pages and Exit Codes

Triggering the a help page intentionally (by passing in ``--help``)
returns exit code 0. If a help page is displayed due to incorrect user
input, the program returns exit code 2. See {ref}`exit-codes` for more
general information.

For clarity, here is an example.

```{eval-rst}
.. click:example::

@click.group('printer_group')
def printer_group():
pass

@printer_group.command('printer')
@click.option('--this')
def printer(this):
if this:
click.echo(this)

.. click:run::
invoke(printer_group, args=['--help'])

The above invocation returns exit code 0.

.. click:run::
invoke(printer_group, args=[])
```

The above invocation returns exit code 2 since the user invoked the command incorrectly. However, since this is such a common error when first using a command, Click invokes the help page for the user. To see that `printer-group` is an invalid invocation, turn `no_args_is_help` off.

```{eval-rst}
.. click:example::

@click.group('printer_group', no_args_is_help=False)
def printer_group():
pass

@printer_group.command('printer')
@click.option('--this')
def printer(this):
if this:
click.echo(this)

.. click:run::
invoke(printer_group, args=[])
```
126 changes: 0 additions & 126 deletions docs/exceptions.rst

This file was deleted.