{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# How to write a custom PyZEAL plugin" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Imports" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# some standard library imports\n", "from json import load\n", "from os.path import dirname, join\n", "from typing import Callable, Optional, Tuple, Type\n", "\n", "# the core imports for writing your custom logic and the plugin\n", "from pyzeal.algorithms.finder_algorithm import FinderAlgorithm\n", "from pyzeal.plugins.pyzeal_plugin import PyZEALPlugin\n", "from pyzeal.plugins.installation_helper import InstallationHelper\n", "from pyzeal.utils.root_context import RootContext\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Implement your custom logic" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "class TestAlgorithm(FinderAlgorithm):\n", " \"My awesome custom root finding algorithm!\"\n", "\n", " def __init__(self, arg1: str, arg2: int) -> None:\n", " self.arg1 = arg1\n", " self.arg2 = arg2\n", "\n", " def calcRoots(self, context: RootContext) -> None:\n", " \"Override this to implement an algorithm.\"\n", " raise NotImplementedError(\n", " f\"test algorithm [{self.arg1} | {self.arg2}] is not implemented!\"\n", " )\n", "\n", " def __str__(self) -> str:\n", " \"Just for printing, you don't actually need this.\"\n", " return f\"TestAlgorithm({self.arg1}, {self.arg2})...! :)\"" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## How to include custom configuration data" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# here is an example of how to include custom configuration data with your\n", "# plugin:\n", "configFile = \"./algorithm_data.json\"\n", "# once you installed the 'algorithm_data.json' data file you should use the\n", "# following line instead:\n", "# configFile = InstallationHelper.returnDataPath(\"algorithm_data.json\")\n", "\n", "with open(configFile, \"r\", encoding=\"utf-8\") as f:\n", " customData = load(f)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## The actual plugin class" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "the plugin itself: MyTestAlgorithmPlugin @ v22.1.0\n", "the provided service: TestAlgorithm(hi from algorithm_data.json!, 9)...! :)\n" ] } ], "source": [ "class AlgorithmPlugin(PyZEALPlugin[FinderAlgorithm]):\n", " \"My custom plugin (providing my algorithm to PyZEAL).\"\n", "\n", " _instance: Optional[PyZEALPlugin[FinderAlgorithm]] = None\n", "\n", " @staticmethod\n", " def initialize() -> Callable[..., FinderAlgorithm]:\n", " \"This is the hook of plugins into `PyZEAL`.\"\n", " arg1, arg2 = customData[\"arg1\"], customData[\"arg2\"]\n", " return lambda: TestAlgorithm(arg1=arg1, arg2=arg2)\n", "\n", " @staticmethod\n", " def getInstance() -> PyZEALPlugin[FinderAlgorithm]:\n", " \"Plugins should be realized as singletons.\"\n", " if AlgorithmPlugin._instance is None:\n", " AlgorithmPlugin._instance = AlgorithmPlugin()\n", " return AlgorithmPlugin._instance\n", "\n", " @property\n", " def pluginType(self) -> Type[FinderAlgorithm]:\n", " \"The type provided by the plugin.\"\n", " return FinderAlgorithm\n", "\n", " @property\n", " def pluginName(self) -> str:\n", " \"The name of the plugin.\"\n", " return \"MyTestAlgorithmPlugin\"\n", "\n", " @property\n", " def pluginVersion(self) -> Tuple[int, int, int]:\n", " \"\"\"\n", " The version of the plugin (the combination of version and name should\n", " be unique). The semantics are (`major`, `minor`, `patch`).\n", " \"\"\"\n", " return (22, 1, 0)\n", "\n", "\n", "if __name__ == \"__main__\":\n", " print(f\"the plugin itself: {AlgorithmPlugin.getInstance()}\")\n", " print(f\"the provided service: {AlgorithmPlugin.initialize()()}\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Installing the plugin" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "With the prerequisits above placed in a single `.py` source file named\n", "`algorithm_plugin.py` you can now install your plugin as follows:\n", "\n", "```\n", "$ pyzeal plugin --install algorithm_plugin.py\n", "$ pyzeal plugin --install algorithm_data.json\n", "```\n", "\n", "Your algorithm will now be used by any root finder instances! If you would like\n", "to change the configuration data provided via `algorithm_data.json`, simple\n", "adjust the file contents and issue the second command again.\n", "\n", "Note that your plugin overrides the default algorithms contained in **PyZEAL**.\n", "To restore these defaults simply `--uninstall` your plugin. By adapting the\n", "example above it is also quite straightforward to replace a given default\n", "algorithm with your custom plugin and leave the remaining ones untouched. To\n", "achieve this the return value of your plugin's `initialize` must accept an\n", "`algorithmType` parameter of type\n", "`pyzeal.pyzeal_types.algorithm_types.AlgorithmTypes`. You may then return any\n", "algorithm of your choosing upon a given value of `algorithmType`." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.2" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }