Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin support #5144

Merged
merged 11 commits into from
Feb 15, 2023
1 change: 1 addition & 0 deletions docs/src/common_links.inc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
.. _@ajdawson: https://github.com/ajdawson
.. _@bjlittle: https://github.com/bjlittle
.. _@bouweandela: https://github.com/bouweandela
.. _@bsherratt: https://github.com/bsherratt
.. _@corinnebosley: https://github.com/corinnebosley
.. _@cpelley: https://github.com/cpelley
.. _@djkirkham: https://github.com/djkirkham
Expand Down
10 changes: 10 additions & 0 deletions docs/src/community/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,13 @@ smoother interoperability:
:hidden:

iris_xarray

Plugins
-------
trexfeathers marked this conversation as resolved.
Show resolved Hide resolved

Iris can be extended with **plugins**! See below for further information:

.. toctree::
:maxdepth: 2

plugins
68 changes: 68 additions & 0 deletions docs/src/community/plugins.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
.. _namespace package: https://packaging.python.org/en/latest/guides/packaging-namespace-packages/

.. _community_plugins:

Plugins
=======

Iris supports **plugins** under the ``iris.plugins`` `namespace package`_.
This allows packages that extend Iris' functionality to be developed and
maintained independently, while still being installed into ``iris.plugins``
instead of a separate package. For example, a plugin may provide loaders or
savers for additional file formats, or alternative visualisation methods.


Using plugins
-------------

Once a plugin is installed, it can be used either via the
:func:`iris.use_plugin` function, or by importing it directly:

.. code-block:: python

import iris

iris.use_plugin("my_plugin")
# OR
import iris.plugins.my_plugin


Creating plugins
----------------

The choice of a `namespace package`_ makes writing a plugin relatively
straightforward: it simply needs to appear as a folder within ``iris/plugins``,
then can be distributed in the same way as any other package. An example
repository layout:

.. code-block:: text

+ lib
+ iris
+ plugins
+ my_plugin
- __init__.py
- (more code...)
- README.md
- pyproject.toml
- setup.cfg
- (other project files...)

In particular, note that there must **not** be any ``__init__.py`` files at
higher levels than the plugin itself.

The package name - how it is referred to by PyPI/conda, specified by
``metadata.name`` in ``setup.cfg`` - is recommended to include both "iris" and
the plugin name. Continuing this example, its ``setup.cfg`` should include, at
minimum:

.. code-block:: ini

[metadata]
name = iris-my-plugin

[options]
packages = find_namespace:

[options.packages.find]
where = lib
4 changes: 3 additions & 1 deletion docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ This document explains the changes made to Iris for this release
✨ Features
===========

#. N/A
#. `@bsherratt`_ added support for plugins - see the corresponding
:ref:`documentation page<community_plugins>` for further information.
(:pull:`5144`)


🐛 Bugs Fixed
Expand Down
21 changes: 21 additions & 0 deletions lib/iris/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ def callback(cube, field, filename):

import contextlib
import glob
import importlib
import itertools
import os.path
import pathlib
Expand Down Expand Up @@ -129,6 +130,7 @@ def callback(cube, field, filename):
"sample_data_path",
"save",
"site_configuration",
"use_plugin",
]


Expand Down Expand Up @@ -470,3 +472,22 @@ def sample_data_path(*path_to_join):
"appropriate for general file access.".format(target)
)
return target


def use_plugin(plugin_name):
vsherratt marked this conversation as resolved.
Show resolved Hide resolved
"""
Convenience function to import a plugin

For example::

use_plugin("my_plugin")

is equivalent to::

import iris.plugins.my_plugin

This is useful for plugins that are not used directly, but instead do all
vsherratt marked this conversation as resolved.
Show resolved Hide resolved
their setup on import. In this case, style checkers would not know the
significance of the import statement and warn that it is an unused import.
"""
importlib.import_module(f"iris.plugins.{plugin_name}")
5 changes: 3 additions & 2 deletions lib/iris/io/format_picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ def get_spec(self, basename, buffer_obj):
value = value[:50] + "..."
printable_values[key] = value
msg = (
"No format specification could be found for the given buffer."
" File element cache:\n {}".format(printable_values)
"No format specification could be found for the given buffer. "
"Perhaps a plugin is missing or has not been loaded. "
"File element cache:\n {}".format(printable_values)
)
raise ValueError(msg)

Expand Down
10 changes: 10 additions & 0 deletions lib/iris/plugins/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Iris plugins
vsherratt marked this conversation as resolved.
Show resolved Hide resolved

`iris.plugins` is a [namespace package] allowing arbitrary plugins to be
installed alongside Iris.

See [the Iris documentation][plugins] for more information.


[namespace package]: https://packaging.python.org/en/latest/guides/packaging-namespace-packages/
[plugins]: https://scitools-iris.readthedocs.io/en/latest/community/plugins.html