Skip to content
Back to top

# Your first plugin

You can define plugins directly in your project by creating a `toxfile.py` file next to your tox configuration file. This avoids the need to package and install a separate plugin. All the same hooks are available.

## Your first plugin: adding a CLI flag

Create a `toxfile.py` that adds a `--magic` flag to the tox CLI:

    from tox.config.cli.parser import ToxParser
    from tox.plugin import impl

    @impl
    def tox_add_option(parser: ToxParser) -> None:
        parser.add_argument("--magic", action="store_true", help="enable magic mode")

Place this file next to your `tox.toml` or `tox.ini`. When tox starts, it discovers and loads `toxfile.py` automatically.

## Appending version info

A `toxfile.py` that appends text to the `tox --version` output:

    from tox.plugin import impl

    @impl
    def tox_append_version_info() -> str:
        return "magic"

## Providing environments

Plugins can dynamically inject tox environments that don’t need to be declared in `tox.toml` or `tox.ini`. This uses two hooks together: `tox_extend_envs` to declare the environment name, and `tox_add_core_config` with `MemoryLoader` to configure it.

For example, a `toxfile.py` that adds a `lint` environment running pre-commit:

    from collections.abc import Iterable

    from tox.config.loader.memory import MemoryLoader
    from tox.config.sets import ConfigSet
    from tox.plugin import impl
    from tox.session.state import State

    @impl
    def tox_extend_envs() -> Iterable[str]:
        return ("lint",)

    @impl
    def tox_add_core_config(core_conf: ConfigSet, state: State) -> None:
        state.conf.memory_seed_loaders["lint"].append(
            MemoryLoader(
                description="run pre-commit linters",
                commands=[["pre-commit", "run", "--all-files"]],
                deps=["pre-commit"],
            ),
        )

With this `toxfile.py` in your project root, `tox list` shows the `lint` environment and `tox run -e lint` executes it – no configuration file entry needed.

Any configuration key accepted by the environment can be passed as a keyword argument to `MemoryLoader`. Values from `MemoryLoader` act as defaults and can still be overridden by the configuration file or CLI `--override`.