[ENH]: Prepare Junifer for extension support #366

Merged
synchon merged 5 commits from feat/junifer-ext into main 2025-03-21 14:54:21 +00:00
8 changed files with 75 additions and 2 deletions

View file

@ -0,0 +1 @@
Add documentation on how to make a ``junifer`` plugin by `Synchon Mandal`_

View file

@ -0,0 +1 @@
Add support for plugins by `Synchon Mandal`_

View file

@ -30,3 +30,4 @@ DataGrabbers, Preprocessors, Markers, etc., following the *junifer* way.
parcellations
coordinates
masks
plugins

View file

@ -0,0 +1,50 @@
.. include:: ../links.inc
.. _plugins:
Creating a ``junifer`` plugin
=============================
What is a plugin
----------------
Plugins are additional components that extend the functionality of ``junifer``.
Technically, plugins can also be called as extensions, but we clearly differentiate
them in ``junifer`` as :ref:`extensions<extending_extension>` serve a different
purpose. ``junifer`` plugins are command line programs which can be used
independently but also integrates with the core ``junifer`` command line interface
(CLI) framework.
When a plugin is installed, we basically inject new behaviours into the ``junifer`` CLI
while promoting modularity and allowing for easier maintenance and updates to the core
application. A great plugin example is `junifer-data`_ which is well-integrated
in ``junifer`` to access assets like parcellations, coordinates and masks, but also
remains modular enough to update its CLI without disturbing that of ``junifer``.
How to make a plugin
--------------------
A key distinction between a ``junifer`` extension and a ``junifer`` plugin is that a
plugin will have its own CLI and be a Python package in its own right, for example,
`junifer-data`_.
The following steps should lead you to make a successful plugin:
#. Create CLI for your plugin. As ``junifer`` uses `click`_ for its CLI, a plugin
should be creating its own ``click.Group`` as shown here:
`<https://click.palletsprojects.com/en/stable/commands/#commands-and-groups>`_.
#. Integrate the `click`_-based plugin CLI with `setuptools`_ as shown here:
`<https://click.palletsprojects.com/en/stable/setuptools/#setuptools-integration>`_.
#. Make sure the plugin CLI independently works as intended.
#. Follow the steps here:
`<https://setuptools.pypa.io/en/latest/userguide/entry_point.html#entry-points-for-plugins>`_
and make a new entry point with the *name* as the sub-command you want under ``junifer`` command
and the *group* as ``"junifer.ext"``. The *name* should refer to the ``click.Group`` of your
plugin CLI.
#. After you install your plugin in the same environment as your ``junifer`` installation, you can
observe your plugin being added as a new sub-command under ``junifer`` and working exactly as it
did independently.

View file

@ -12,6 +12,7 @@
.. _`AML`: https://www.fz-juelich.de/inm/inm-7/EN/Forschung/Applied%20Machine%20Learning/_node.html
.. _`INM-7`: https://www.fz-juelich.de/inm/inm-7/EN/Home/home_node.html
.. _`julearn`: https://juaml.github.io/julearn
.. _`junifer-data`: https://github.com/juaml/junifer-data-client
.. _`pandas`: https://pandas.pydata.org
.. _`pandas.DataFrame` : https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html
@ -24,6 +25,7 @@
.. _`nipype`: https://nipype.readthedocs.io
.. _`datalad`: https://datalad.org
.. _`templateflow`: https://www.templateflow.org
.. _`click`: https://click.palletsprojects.com/en/stable/
.. _`venv`: https://docs.python.org/3/tutorial/venv.html
.. _`conda env`: https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html
@ -36,6 +38,7 @@
.. _`Wikipedia`: https://wikipedia.org
.. _`YAML`: https://yaml.org
.. _`setuptools`: https://setuptools.pypa.io/en/latest/
.. _`setuptools_scm`: https://github.com/pypa/setuptools_scm/
.. _`sphinx gallery`: https://sphinx-gallery.github.io/stable/index.html

View file

@ -3,7 +3,22 @@
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL
import sys
import lazy_loader as lazy
if sys.version_info < (3, 11): # pragma: no cover
from importlib_metadata import entry_points
else:
from importlib.metadata import entry_points
__getattr__, __dir__, __all__ = lazy.attach_stub(__name__, __file__)
# Register extensions
from .cli import cli
for ep in entry_points(group="junifer.ext"):
cli.add_command(ep.load(), name=ep.name)

View file

@ -111,7 +111,9 @@ def _validate_verbose(
)
@click.group()
@click.group
@click.version_option()
@click.help_option()
def cli() -> None: # pragma: no cover
"""JUelich NeuroImaging FEature extractoR."""

View file

@ -52,7 +52,7 @@ dependencies = [
"lazy_loader==0.4",
"importlib_metadata; python_version<'3.9'",
"looseversion==1.3.0; python_version>='3.12'",
"junifer_data==1.2.0",
"junifer_data==1.3.0",
]
dynamic = ["version"]