The structure of QIIME 2 plugin packages#

QIIME 2 doesn’t put restrictions on how you structure the Python package that contains your plugin, but there are some conventions that experienced developers follow. This Explanation article will discuss these conventions in the context of the plugin developed in Tutorial: A step-by-step guide to building your first QIIME 2 plugin. Specifically, we’ll look at the initial commit in that repository.

I’m going to use the tree command to get a convenient view of all of the files and directories my plugin package after my first commit. Then we’ll start from the top and talk about what each of these are.

$ tree -a q2-dwq2
q2-dwq2
├── .git
│   └── # many files
├── .github
│   └── workflows
│       └── ci.yml
├── .gitignore
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.md
├── ci
│   └── recipe
│       └── meta.yaml
├── q2_dwq2
│   ├── __init__.py
│   ├── _methods.py
│   ├── _version.py
│   ├── citations.bib
│   ├── plugin_setup.py
│   └── tests
│       ├── __init__.py
│       ├── data
│          └── table-1.biom
│       └── test_methods.py
├── setup.cfg
├── setup.py
└── versioneer.py

q2-dwq2#

q2-dwq2 is the top-level directory containing all of the files in the Python package.

q2-dwq2/.git#

I am maintaining my plugin code using git for version control, and the .git directory contains all of the information for managing the .git repository. There are many files in there, and going through them is out-of-scope for this book, but poke through that directory if you’re interested. Nothing magical is happening with git repositories: the files in this directory are used by git clients to do all of the cool stuff that git does for us. Don’t ever edit files in this directory directly.

q2-dwq2/.github#

This directory contains information used by GitHub, and is here because I maintain my git repository for this plugin on GitHub. GitHub will use some files in this directory in special ways if they exist (see here). In my case, this directory contains a single GitHub Action file, .github/workflows/ci.yml, that was added when the repository was built from the cookiecutter template. This GitHub Action is what builds the plugin and runs the tests when pulls requests or commits are submitted to GitHub. To learn more about GitHub Actions, see GitHub’s documentation. An additional resource that may help is GitHub Automation for Scientists.

q2-dwq2/.gitignore#

A file used by git that specifies filename patterns that should be ignored by git (and therefore not included in revision control). This helps keep your repository neat and clean by excluding things like temporary files created by text editors or operating systems. There are lots of examples that you can use or build from here.

q2-dwq2/LICENSE#

File containing the software’s license. Naming the file this way, and storing it in the top-level directory, enables it to be recognized easily by users or systems (such as GitHub).

q2-dwq2/MANIFEST.in#

Used by setuptools, along with setupy.py and setup.cfg, to explicitly define files that should or shouldn’t be included in the Python package.

q2-dwq2/Makefile#

make instructions for building the Python module, running its tests, and more. When you run a command like make test or make dev, you are applying instructions defined in this file. make is a powerful tool, to put it lightly, and it has been around since pre-historic times (i.e., 1976).

q2-dwq2/README.md#

The project’s readme file. This is often when someone interested in your Python package will first look for information. If you manage your project on GitHub, this will be displayed on the repositories front page.

q2-dwq2/ci#

This directory contains information used by the Continuous Integration system, which is used by the GitHub Action workflow referenced in .github/workflows/ci.yml. The one file contained here in this case, ci/recipe/meta.yaml, provides instructions for how this plugin can be built and tested.

q2-dwq2/q2_dwq2#

The top-level module directory. This is where all of files relevant to the use of this code with Python are stored. All files not included in this directory can be considered metadata about the Python module.

q2-dwq2/q2_dwq2/__init__.py#

A special file whose existance (even if the file is empty) specifies that this directory is a Python module. This will often contain import statements that the developer wants to propagate up to be module-level imports, enabling statements like from qiime2 import Artifact.

q2-dwq2/q2_dwq2/_methods.py#

This is not a required file, but in this plugin it’s used to store code for functions that will ultimately be registered as Methods. As a plugin grows, it may make sense to consider reorganization such as creating a _methods directory that contains files with code for each individual Method.

By convention in Python, files, functions, or objects (or anything else) whose name starts with an _ should be treated as private. In other words, outside of this specific code base, anything named with a leading underscore shouldn’t be referenced or used directly. This leaves the developer free to make interface changes (such as renaming the _methods.py) file without breaking other people’s code.

q2-dwq2/q2_dwq2/_version.py#

This is a file created by The Versioneer to assist with creating versions of software from information in the .git directory, if it exists. You shouldn’t ever edit this file directly.

q2-dwq2/q2_dwq2/citations.bib#

This file stores any citations that QIIME 2 will reference for this plugin in BibTeX format. The relative filepath is specified when the Plugin object is initialized, so can be called whatever you’d like.

q2-dwq2/q2_dwq2/plugin_setup.py#

By convention, this file is where the QIIME 2 Plugin object is instantiated and where actions and other information are registered to the plugin. Again, this file can be called anything and live in other places, but it’s pretty standard across plugins at this stage so it’s a good idea to just adopt this naming convention in your plugin. (For example, the first thing I typically do when someone sends me their plugin for feedback is read their plugin_setup.py file.)

q2-dwq2/q2_dwq2/tests#

This directory contains all unit tests for functionality in the plugin. Any associated test data files are generally nested under this directory.

q2-dwq2/q2_dwq2/tests/__init__.py#

The file initializing q2_dwq2/tests as a submodule in this Python package.

q2-dwq2/q2_dwq2/tests/data#

A directory containing any data files that are used in tests.

q2-dwq2/q2_dwq2/tests/test_methods.py#

The file containing unit tests of the functionality in the module’s _methods.py By convention, the naming of test files roughly parallels the naming of the files they are testing.

q2-dwq2/q2_dwq2/setup.cfg#

A file containing general configuration information related to the Python module. For example, there is information in here that helps The Versioneer find the inforamtion that it needs to create version numbers.

q2-dwq2/q2_dwq2/setup.py#

A special file for the Python pacakge that sets up the Python module. An important component in this file for QIIME 2 is that the qiime2.plugins entry point is defined:

setup(
    ...
    entry_points={
        "qiime2.plugins": ["q2-dwq2=q2_dwq2.plugin_setup:plugin"]
    },
    ...

This allows the QIIME 2 PluginManager to load the module and determine if one or mroe QIIME 2 plugins are defined in the module, and if so where they can be imported from. In this case, one plugin is registered (q2-dwq2) and it can be imported from q2_dwq2.plugin_setup through the variable name plugin. If you prefer to not follow the naming convention described above with respect to plugin_setup.py, this is where you can let the PluginManager know where it should be looking for your plugin(s).

q2-dwq2/q2_dwq2/versioneer.py#

Another file created by The Versioneer to assist with creating versions of software from information in the .git directory, if it exists.