Skip to content

Commit

Permalink
Plugin support (#5144)
Browse files Browse the repository at this point in the history
* Set up `iris.plugins` namespace package

* Provide convenience function to use a plugin

* Basic documentation for plugins

* Add a hint about plugins when load fails

* Expose `use_plugin` in `__all__`

* Add example usage

* Split plugin documentation to its own page

* Document how to create a plugin

* Link to documentation

* Correct "plain" code block -> "text"

* Whatsnew
  • Loading branch information
vsherratt committed Feb 15, 2023
1 parent 58cdd78 commit 504c188
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 3 deletions.
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
-------

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):
"""
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
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

`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

0 comments on commit 504c188

Please sign in to comment.