# Your First Plugin

In this topic, we'll step through the fundamental concepts for building a
**bare minimum** "hello napari" plugin from scratch. At the end of this
tutorial, we'll point you to a "cookiecutter" template repository that
helps automate the creation of a new plugin, and adds a number of
conveniences for testing and deploying your plugin.

We will assume you've set up a Python virtual environment.  If you are
new to virtual environments, we recommend [installing miniconda][miniconda]
and [creating a new environment with python][python_env]

## Create a new directory

Let's create a new folder called `napari-hello` for your plugin,
and navigate into it.

```sh
mkdir napari-hello
cd napari-hello
```

## Add standard python package metadata files

In your root `napari-hello` folder, create a `pyproject.toml` and `setup.cfg` file,
then create a `napari_hello` directory with a single `__init__.py` file inside of it.

````{tabbed} macOS / Linux
```sh
mkdir napari_hello
touch napari_hello/__init__.py pyproject.toml setup.cfg
```
````

````{tabbed} Windows
```bat
mkdir napari_hello
copy /b napari_hello\__init__.py +,,
copy /b pyproject.toml +,,
copy /b setup.cfg +,,
```
````

Your project should now look like this:

```
~/napari-hello/
├── napari_hello/
│   └── __init__.py
├── pyproject.toml
├── setup.cfg
```

````{admonition} **napari-hello** vs **napari_hello**
:class: tip
You might notice that we used a **dash** in our top level folder, and an
**underscore** in the inner folder. This is the python convention:
*distribution packages* use dashes, while python *files* and *modules* use
underscores. Our *package* is named `napari-hello`, and it includes a
single top-level module, called `napari_hello`.

You would *install* this package with
```sh
pip install napari-hello
```

... and *import* it with ...
```python
import napari_hello
```
````

Next, copy and paste the following text into `pyproject.toml` and `setup.cfg`

### `pyproject.toml`

```{note}
`pyproject.toml` is a relative recent standard file (introduced in
[PEP518](https://www.python.org/dev/peps/pep-0518/), May 2016) that tells
the python package installer [pip](https://pip.pypa.io/en/stable/) how to
build your package. For more background, see
[Clarifying PEP 518](https://snarky.ca/clarifying-pep-518/) and
[What the heck is pyproject.toml?](https://snarky.ca/what-the-heck-is-pyproject-toml/).
```

Here, we declare we want to use [setuptools](https://setuptools.pypa.io/en/latest/)
for packaging our plugin:

```toml
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
```

### `setup.cfg`

This file is where [setuptools](https://setuptools.pypa.io/en/latest/)
looks for our package metadata (like package name and version). Here, we
provide the bare minimum package metadata. See
[setuptools quickstart](https://setuptools.pypa.io/en/latest/userguide/quickstart.html)
for more.

```ini
[metadata]
name = napari-hello
version = 0.0.1

[options]
packages = find:
```

## Add napari plugin files

So far, we've done nothing napari-specific.  Let's create the actual plugin.
We're going to add a [Widget contribution](./contributions.html#contributions-widgets)
with a single button that shows a "Hello, world!" message when clicked.

### The plugin functionality: `napari_hello/__init__.py`

Copy and paste the following text into the file at `napari_hello/__init__.py`.
It just uses the napari notifications API to show a message:

```python
from napari.utils.notifications import show_info

def show_hello_message():
    show_info('Hello, world!')
```

*(It doesn't look like a widget yet! We're going to use napari's widget
"autogeneration" capabilities to turn this function into a widget)*


### The manifest: `napari.yaml`

Create an empty [plugin manifest](./manifest) file at `napari_hello/napari.yaml`
We will use this file to tell napari that:

1. Our plugin provides a **command** (we give the command an ID of `napari-hello.say_hi`)
2. Where to find the exact function that executes the command (the **`python_name`**)
3. That we'd like the widget to be **autogenerated** from the function
   (so we don't need to deal with any GUI code)

Add the following text to `napari.yaml`:

```yaml
name: napari-hello
contributions:
  commands:
    - id: napari-hello.say_hi
      title: Say hello, world!
      python_name: napari_hello:show_hello_message
  widgets:
    - command: napari-hello.say_hi
      display_name: Hello World
      autogenerate: true
```

```{tip}
We could have put that `show_hello_message` function anywhere, it didn't
need to go in the top `__init__.py` file. Just make sure that the `python_name`
of the corresponding `command` in the manifest points to the correct
`<module>:<function_name>`.
```

### Update `setup.cfg`

Lastly, let's make a few small changes to `setup.cfg`.

1. Because we are directly using the `napari.utils.notifications` API, we
   need to add `napari` to our package **`install_requires`**. (You should
   add *all* of your package dependencies here.  Assume nothing.)

2. We need to instruct setuptools to *include* that `napari.yaml` file
   when it bundles our package for distribution, by adding it to the
   **`[options.package_data]`**

3. In order for napari to find our plugin when it's installed in
   the environment, we need to add a `napari.manifest` entry to our
   the environment, we need to add a `napari.manifest` entry to our
   **`[options.entry_points]`** that directs napari to the `napari.yaml`
   file we added to the `napari_hello` module.

    ```{tip}
    Entry points are a standard python mechanism for an installed distribution to
    advertise components it provides to be discovered and used by other code.

    See the [Entry points specification][entry_points] for details.
    ```

With the above changes, your final `setup.cfg` file should look like this:

```ini
[metadata]
name = napari-hello
version = 0.0.1

[options]
packages = find:
install_requires =
    napari

[options.package_data]
napari-hello = napari.yaml

[options.entry_points]
napari.manifest =
    napari-hello = napari_hello:napari.yaml
```

## Install your plugin and try it out!

With that we're ready to go.  Let's install our package in the environment
and then run napari.  Note: we're using `pip install -e .` here to install our
package (in the current working directory) in
["editable" mode][editable_mode].
This means that changes we make to our package during development will be
detected when we re-run napari, without having to run `pip install` again.


```sh
pip install -e .
napari
```

Once napari starts, select `napari-hello: Hello World` from the
`Plugins` menu, then click the `Run` button to see the message.

## Get going quickly with cookiecutter

Now that you've learned all of the critical steps for creating a plugin,
you can use our [cookiecutter template repository][cookiecutter]
to get up and running quickly with each new plugin.
This will ask you a few questions about your new plugin, and
autogenerate a package structure much like the one above. It additionally
includes conveniences like testing, continuous integration, version
management, and deployment hooks.

```sh
pip install cookiecutter
cookiecutter https://github.com/napari/cookiecutter-napari-plugin
```

## Next Steps

Plugins can do a lot more than just say hi!  You can see the complete list
of available contributions and their fields in the
[Contributions Reference](./contributions), and learn more about each
specific contribution type in the [Guides](./guides).

[miniconda]: https://docs.conda.io/projects/conda/en/latest/user-guide/install/download.html
[python_env]: https://docs.conda.io/projects/conda/en/latest/user-guide/getting-started.html#managing-python
[editable_mode]: https://pip.pypa.io/en/stable/cli/pip_install/#editable-installs
[cookiecutter]: https://github.com/napari/cookiecutter-napari-plugin
[entry_points]: https://packaging.python.org/en/latest/specifications/entry-points/
