##############################
Making packages compatible
##############################
*********************
Developing for pipx
*********************
If you are a developer and want users to be able to run
.. code-block:: console
$ pipx install MY_PACKAGE
make sure you include ``scripts`` in your main table [#main-table]_ in ``pyproject.toml`` or its legacy equivalents for
``setup.cfg`` and ``setup.py``. pipx also exposes ``gui-scripts`` entry points, which are useful for GUI applications on
Windows (they launch without opening a console window).
.. tab-set::
.. tab-item:: pyproject.toml
.. code-block:: toml
[project.scripts]
foo = "my_package.some_module:main_func"
bar = "other_module:some_func"
[project.gui-scripts]
baz = "my_package_gui:start_func"
.. tab-item:: setup.cfg
.. code-block:: ini
[options.entry_points]
console_scripts =
foo = my_package.some_module:main_func
bar = other_module:some_func
gui_scripts =
baz = my_package_gui:start_func
.. tab-item:: setup.py
.. code-block:: python
setup(
# other arguments here...
entry_points={
"console_scripts": [
"foo = my_package.some_module:main_func",
"bar = other_module:some_func",
],
"gui_scripts": [
"baz = my_package_gui:start_func",
],
},
)
Here ``foo`` and ``bar`` (and ``baz`` on Windows) become applications that pipx exposes after installing the package,
invoking their corresponding entry point functions.
.. mermaid::
flowchart LR
TOML["pyproject.toml
[project.scripts]"] -->|defines| EP["entry points
foo, bar"]
EP -->|pipx install| VENV["isolated venv"]
VENV -->|symlink or copy| BIN["~/.local/bin/
foo, bar"]
BIN -->|invokes| FUNC["my_package:
main_func()"]
classDef input fill:#3f72af,stroke:#28517f,color:#fff
classDef proc fill:#2a9d8f,stroke:#1c6b61,color:#fff
classDef venv fill:#7c4dff,stroke:#5a2fd0,color:#fff
classDef out fill:#388e3c,stroke:#256128,color:#fff
classDef fn fill:#c78c20,stroke:#8a6011,color:#fff
class TOML input
class EP proc
class VENV venv
class BIN out
class FUNC fn
The ``pipx.run`` entry point group
==================================
When a user runs ``pipx run PACKAGE``, pipx looks for a console script matching the package name. If the package name
and script name differ, the user has to write ``pipx run --spec PACKAGE SCRIPT``, which is less convenient.
Package authors can declare a ``pipx.run`` entry point group to tell pipx which function to invoke for ``pipx run``.
This entry point takes priority over console scripts when present.
.. tab-set::
.. tab-item:: pyproject.toml
.. code-block:: toml
[project.entry-points."pipx.run"]
my-package = "my_package.cli:main"
.. tab-item:: setup.cfg
.. code-block:: ini
[options.entry_points]
pipx.run =
my-package = my_package.cli:main
With this declaration, ``pipx run my-package`` invokes ``my_package.cli:main`` even if no console script named
``my-package`` exists. The `build `_ package uses this pattern so that ``pipx run build``
works even though build's console script is named ``pyproject-build``.
Detect a pipx installation
==========================
pipx records the environment name in ``pipx_metadata.json`` for packages installed with ``pipx install``. Package code
can read this name when it needs to give pipx-specific recovery instructions:
.. code-block:: python
import json
import sys
from pathlib import Path
def pipx_environment() -> str | None:
try:
metadata = json.loads((Path(sys.prefix) / "pipx_metadata.json").read_text(encoding="utf-8"))
except (json.JSONDecodeError, OSError):
return None
environment = metadata.get("environment") if isinstance(metadata, dict) else None
return environment if isinstance(environment, str) else None
Use the returned name as the target for commands such as ``pipx inject ENVIRONMENT 'mytool[feature]'``. pipx records the
extra on the main package, so reinstalling the environment retains it.
``pipx run`` may create internal metadata without setting ``environment``, so check the field value. Treat other keys as
internal pipx state. If the function returns ``None``, show the package's usual installer-neutral advice.
Manual pages
============
To provide documentation via ``man`` pages on UNIX-like systems, ship them as data files. pipx exposes them the same way
it exposes apps.
.. tab-set::
.. tab-item:: setuptools
.. code-block:: toml
# pyproject.toml
[tool.setuptools.data-files]
"share/man/man1" = [
"manpage.1",
]
.. code-block:: ini
# setup.cfg
[options.data_files]
share/man/man1 =
manpage.1
.. code-block:: python
# setup.py
setup(
# other arguments here...
data_files=[("share/man/man1", ["manpage.1"])],
)
.. warning::
The ``data-files`` keyword is "discouraged" in the `setuptools documentation
`_,
but there is no alternative when ``man`` pages are a requirement.
.. tab-item:: pdm-backend
.. code-block:: toml
# pyproject.toml
[tool.pdm.build]
source-includes = ["share"]
[tool.pdm.build.wheel-data]
data = [
{ path = "share/man/man1/*", relative-to = "." },
]
The manual page ``manpage.1`` can then be read with ``man manpage`` after installing the package. For a real-world
example, see `pycowsay's setup.py `_.
Shell completions
=================
A package can ship the completion script for its own command the same way it ships a man page. pipx picks up the three
directories a wheel installs completion scripts into and links each one under ``PIPX_COMPLETION_DIR`` (default
``~/.local/share``), so the completions arrive with the package and leave with it.
.. list-table::
:header-rows: 1
:widths: 40 40 20
- - Shipped by the package
- Exposed to
- Loaded by
- - ``share/bash-completion/completions/``
- ``~/.local/share/bash-completion/completions/``
- bash
- - ``share/fish/vendor_completions.d/``
- ``~/.local/share/fish/vendor_completions.d/``
- fish
- - ``share/zsh/site-functions/``
- ``~/.local/share/zsh/site-functions/``
- zsh (with the directory on ``fpath``)
Declare them through the data files of your build backend, exactly as with man pages:
.. code-block:: python
setup(
name="your-tool",
data_files=[
("share/bash-completion/completions", ["completions/your-tool"]),
("share/zsh/site-functions", ["completions/_your-tool"]),
("share/fish/vendor_completions.d", ["completions/your-tool.fish"]),
],
)
See :doc:`../how-to/shell-completions` for how each shell loads these directories.
You can read more about entry points in the `setuptools quickstart
`_.
.. [#main-table]
This is often the ``[project]`` table, but it might be named differently. Read more in the `PyPUG
`_.