diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..8cd4453 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,57 @@ +name: Build & Deploy Sphinx Docs + +on: + push: + branches: [20-documentation] + +permissions: + contents: read + pages: write + id-token: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: πŸ“₯ Checkout code + uses: actions/checkout@v3 + + - name: 🐍 Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: πŸ“¦ Install dependencies + run: | + python -m pip install --upgrade pip + pip install sphinx furo sphinx-autodoc-typehints + + # - name: πŸ› οΈ Build documentation # we are just using the uploaded ones + # run: | + # cd docs + # make html + + - name: πŸ“‚ Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/build/html/ + + deploy: + needs: build + + # Grant GITHUB_TOKEN the permissions required to make a Pages deployment + permissions: + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source + + # Deploy to the github-pages environment + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + # Specify runner + deployment step + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 # or specific "vX.X.X" version tag for this action \ No newline at end of file diff --git a/.gitignore b/.gitignore index a94e0e0..f048877 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,7 @@ test.ipynb image_model test.h5 .vscode -build +/build env.json creds.json test.py diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..770ef91 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,30 @@ +# Minimal makefile for Sphinx documentation + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Adjust this to point to your Python package directory (relative to this Makefile) +APIDOCSRC = ../poly_lithic +APIDOCOUT = $(SOURCEDIR)/api + +.PHONY: help Makefile apidoc + +# Default target: "make" is like "make help" +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +# Autogenerate .rst files from Python source using sphinx-apidoc +apidoc: + sphinx-apidoc -o $(APIDOCOUT) $(APIDOCSRC) --force --no-toc --separate + +# Build HTML docs, running apidoc first +html: apidoc + @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +# Catch-all target: route all unknown targets to Sphinx +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/build/doctrees/api/poly_lithic.doctree b/docs/build/doctrees/api/poly_lithic.doctree new file mode 100644 index 0000000..bbcbde0 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.pre_install.doctree b/docs/build/doctrees/api/poly_lithic.pre_install.doctree new file mode 100644 index 0000000..1804c17 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.pre_install.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.scripts.doctree b/docs/build/doctrees/api/poly_lithic.scripts.doctree new file mode 100644 index 0000000..56cc59e Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.scripts.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.scripts.main.doctree b/docs/build/doctrees/api/poly_lithic.scripts.main.doctree new file mode 100644 index 0000000..bcc7ba2 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.scripts.main.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.cli.doctree b/docs/build/doctrees/api/poly_lithic.src.cli.doctree new file mode 100644 index 0000000..1adacb9 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.cli.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.config.config_object.doctree b/docs/build/doctrees/api/poly_lithic.src.config.config_object.doctree new file mode 100644 index 0000000..abfde23 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.config.config_object.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.config.doctree b/docs/build/doctrees/api/poly_lithic.src.config.doctree new file mode 100644 index 0000000..2241697 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.config.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.config.parser.doctree b/docs/build/doctrees/api/poly_lithic.src.config.parser.doctree new file mode 100644 index 0000000..65bca8f Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.config.parser.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.doctree b/docs/build/doctrees/api/poly_lithic.src.doctree new file mode 100644 index 0000000..f93e541 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.interfaces.BaseInterface.doctree b/docs/build/doctrees/api/poly_lithic.src.interfaces.BaseInterface.doctree new file mode 100644 index 0000000..d1ea926 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.interfaces.BaseInterface.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.interfaces.doctree b/docs/build/doctrees/api/poly_lithic.src.interfaces.doctree new file mode 100644 index 0000000..7241c30 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.interfaces.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.interfaces.file_interface.doctree b/docs/build/doctrees/api/poly_lithic.src.interfaces.file_interface.doctree new file mode 100644 index 0000000..9282fc1 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.interfaces.file_interface.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.interfaces.k2eg_interface.doctree b/docs/build/doctrees/api/poly_lithic.src.interfaces.k2eg_interface.doctree new file mode 100644 index 0000000..a64f9db Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.interfaces.k2eg_interface.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.interfaces.p4p_interface.doctree b/docs/build/doctrees/api/poly_lithic.src.interfaces.p4p_interface.doctree new file mode 100644 index 0000000..ff8b680 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.interfaces.p4p_interface.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.logging_utils.doctree b/docs/build/doctrees/api/poly_lithic.src.logging_utils.doctree new file mode 100644 index 0000000..630b4db Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.logging_utils.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.logging_utils.make_logger.doctree b/docs/build/doctrees/api/poly_lithic.src.logging_utils.make_logger.doctree new file mode 100644 index 0000000..25705f2 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.logging_utils.make_logger.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.model_utils.LocalModelGetter.doctree b/docs/build/doctrees/api/poly_lithic.src.model_utils.LocalModelGetter.doctree new file mode 100644 index 0000000..76db98d Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.model_utils.LocalModelGetter.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.model_utils.MlflowModelGetter.doctree b/docs/build/doctrees/api/poly_lithic.src.model_utils.MlflowModelGetter.doctree new file mode 100644 index 0000000..c9c3e9f Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.model_utils.MlflowModelGetter.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.model_utils.ModelGetterBase.doctree b/docs/build/doctrees/api/poly_lithic.src.model_utils.ModelGetterBase.doctree new file mode 100644 index 0000000..3d29b19 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.model_utils.ModelGetterBase.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.model_utils.doctree b/docs/build/doctrees/api/poly_lithic.src.model_utils.doctree new file mode 100644 index 0000000..86fab2e Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.model_utils.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.transformers.BaseTransformer.doctree b/docs/build/doctrees/api/poly_lithic.src.transformers.BaseTransformer.doctree new file mode 100644 index 0000000..f6a57f6 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.transformers.BaseTransformer.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.transformers.BaseTransformers.doctree b/docs/build/doctrees/api/poly_lithic.src.transformers.BaseTransformers.doctree new file mode 100644 index 0000000..6009115 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.transformers.BaseTransformers.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.transformers.CompoundTransformer.doctree b/docs/build/doctrees/api/poly_lithic.src.transformers.CompoundTransformer.doctree new file mode 100644 index 0000000..9288792 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.transformers.CompoundTransformer.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.transformers.doctree b/docs/build/doctrees/api/poly_lithic.src.transformers.doctree new file mode 100644 index 0000000..7496a45 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.transformers.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.utils.builder.doctree b/docs/build/doctrees/api/poly_lithic.src.utils.builder.doctree new file mode 100644 index 0000000..1e0f95b Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.utils.builder.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.utils.doctree b/docs/build/doctrees/api/poly_lithic.src.utils.doctree new file mode 100644 index 0000000..13ad29c Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.utils.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.utils.lazyInterfaceLoader.doctree b/docs/build/doctrees/api/poly_lithic.src.utils.lazyInterfaceLoader.doctree new file mode 100644 index 0000000..d4fb8d4 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.utils.lazyInterfaceLoader.doctree differ diff --git a/docs/build/doctrees/api/poly_lithic.src.utils.messaging.doctree b/docs/build/doctrees/api/poly_lithic.src.utils.messaging.doctree new file mode 100644 index 0000000..5a83033 Binary files /dev/null and b/docs/build/doctrees/api/poly_lithic.src.utils.messaging.doctree differ diff --git a/docs/build/doctrees/config.doctree b/docs/build/doctrees/config.doctree new file mode 100644 index 0000000..4bd8a69 Binary files /dev/null and b/docs/build/doctrees/config.doctree differ diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle new file mode 100644 index 0000000..a9f63e5 Binary files /dev/null and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree new file mode 100644 index 0000000..b1f8c1a Binary files /dev/null and b/docs/build/doctrees/index.doctree differ diff --git a/docs/build/doctrees/interfaces.doctree b/docs/build/doctrees/interfaces.doctree new file mode 100644 index 0000000..0bf2dbb Binary files /dev/null and b/docs/build/doctrees/interfaces.doctree differ diff --git a/docs/build/doctrees/transformers.doctree b/docs/build/doctrees/transformers.doctree new file mode 100644 index 0000000..d9f4856 Binary files /dev/null and b/docs/build/doctrees/transformers.doctree differ diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo new file mode 100644 index 0000000..d06868f --- /dev/null +++ b/docs/build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. +config: f3fa5c90ff625fc0342352252d74e923 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/.buildinfo.bak b/docs/build/html/.buildinfo.bak new file mode 100644 index 0000000..37b21c5 --- /dev/null +++ b/docs/build/html/.buildinfo.bak @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 97716ac4656efbec94163cb968cb166f +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/_modules/index.html b/docs/build/html/_modules/index.html new file mode 100644 index 0000000..0fe5fea --- /dev/null +++ b/docs/build/html/_modules/index.html @@ -0,0 +1,191 @@ + + + + + + + + + Overview: module code | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/scripts/main.html b/docs/build/html/_modules/poly_lithic/scripts/main.html new file mode 100644 index 0000000..3ef8eb3 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/scripts/main.html @@ -0,0 +1,201 @@ + + + + + + + + + poly_lithic.scripts.main | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.scripts.main

+
+[docs] +def main(): + import asyncio + import logging + import os + from poly_lithic.src.cli import model_main, setup + from poly_lithic.src.logging_utils import make_logger, reset_logging + + logger = make_logger('model_manager') + logger.info('Starting model manager') + + (args, config, broker) = setup() + print('resetting logging...') + reset_logging() + + if os.environ.get('DEBUG') == 'True': + logger = make_logger('model_manager', level=logging.DEBUG) + else: + logger = make_logger('model_manager') + + asyncio.run(model_main(args, config, broker))
+ + + +if __name__ == '__main__': + main() +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/cli.html b/docs/build/html/_modules/poly_lithic/src/cli.html new file mode 100644 index 0000000..d956f45 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/cli.html @@ -0,0 +1,420 @@ + + + + + + + + + poly_lithic.src.cli | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.cli

+import argparse
+import asyncio
+import json
+import logging
+import os
+import sys
+import time
+import traceback
+
+from poly_lithic.src.config import ConfigParser
+from poly_lithic.src.logging_utils import get_logger, make_logger
+
+from poly_lithic.src.utils.builder import Builder
+
+from poly_lithic._version import __version__
+
+logger = get_logger()
+
+init_time = time.time()
+
+# if file build-info exists, set the environment variables
+if os.path.exists('build-info.json'):
+    with open('build-info.json') as stream:
+        data = json.load(stream)
+    for key, value in data.items():
+        os.environ[key] = value
+
+print('=' * 120)
+# print(f"Commit head: {os.environ['vcs-ref']}")
+# print(f"Build time: {os.environ['build-date']}")
+print(f'Version: {__version__}')
+print('=' * 120 + '\n')
+
+
+
+[docs] +def initailize_config(config_path): + """Initialize the configuration.""" + try: + config_parser = ConfigParser(config_path) + return config_parser.parse() + except Exception as e: + logger.error(f'Error initializing configuration: {e}') + raise e
+ +
+ +
+[docs] +def env_config(env_config): + """Set the environment variables.""" + logger.debug(f'Setting environment variables from: {env_config}') + try: + # load json + with open(env_config) as stream: + data = json.load(stream) + for key, value in data.items(): + os.environ[key] = value + except Exception as e: + logger.error(f'Error setting environment variables: {e}') + raise e
+ +
+ +
+[docs] +def setup(): + """Setup the model manager.""" + parser = argparse.ArgumentParser(description='Model Manager CLI') + + parser.add_argument( + '-d', + '--debug', + help='Debug mode', + required=False, + default=False, + action='store_true', + ) + + parser.add_argument( + '-c', '--config', help='Path to the configuration file', required=False + ) + parser.add_argument( + '-g', + '--model_getter', + help='Method to obtain the model', + choices=['mlflow', 'local'], + default='mlflow', + ) + # parser.add_argument( + # "-n", + # "--model_name", + # required=False, + # help="Name of the model to be loaded from mlflow", + # ) + parser.add_argument( + '-v', + '--version', + help='Print Version and exit', + required=False, + default=False, + action='store_true', + ) + parser.add_argument( + '-r', + '--reqirements', + help='reqirements install only, if True requirements.txt is obtained from the model and installed, program then exits', + required=False, + default=False, + action='store_true', + ) + # env + parser.add_argument( + '-e', + '--env', + help='Path to the environment configuration file, json format', + required=False, + ) + + parser.add_argument( + '-o', + '--one_shot', + help='One shot mode, run once and exit, helpful for debugging', + required=False, + default=False, + action='store_true', + ) + + # publish + parser.add_argument( + '-p', + '--publish', + help='Publish data to system, if True data is published, otherwise the step is skipped', + required=False, + default=False, + action='store_true', + ) + + # version print and exit + # parser.add_argument( + # '-v', + # '--version', + # help='Print version and exit', + # required=False, + # default=False, + # action='store_true', + # ) + + args = parser.parse_args() + + if args.version: + os.environ['version'] = __version__ + print(f'Ploy-Lithic version: {os.environ["version"]}') + sys.exit(0) + + # change logger level + if args.debug: + print('Debug mode') + logger = make_logger(level=logging.DEBUG) + os.environ['DEBUG'] = 'True' + else: + print('Info mode') + logger = make_logger(level=logging.INFO) + os.environ['DEBUG'] = 'False' + + logger.info('Model Manager CLI') + + logger.debug(f'Arguments: {args}') + + if args.publish: + logger.warning('Publishing data to system') + os.environ['PUBLISH'] = 'True' + else: + logger.warning('Not publishing data to system, to publish use -p or --publish') + os.environ['PUBLISH'] = 'False' + + # env useful when running in windows + if args.env: + env_config(args.env) + + if not args.config: + logger.info( + 'No configuration file provided, getting config from model artifacts' + ) + # check if os.environ["MODEL_CONFIG_FILE"] exists + if os.environ['MODEL_CONFIG_FILE']: + builder = Builder(args.config) + else: + raise Exception( + 'No configuration file provided, this should be done via command -c line or environment variable MODEL_CONFIG_FILE' + ) + else: + logger.info(f'Configuration file provided: {args.config}') + builder = Builder(args.config) + + broker = builder.build() + + return ( + args, + builder.config, + broker, + )
+ +
+ +
+[docs] +async def model_main(args, config, broker): + """Main.""" + # monitor and send to transformer handle + # reintialise logger to get the correct logger and clear any previous handlers + # print("model_main") + + logger = get_logger() + logger.info('Starting model manager') + + time.time() + os.environ['PUBLISH'] = str(args.publish) + try: + if config.deployment.type == 'continuous': + time_start = time.time() + while True: + if time.time() - time_start > config.deployment.rate: + time_start = time.time() + broker.get_all() + else: + if len(broker.queue) > 0: + broker.parse_queue() + + if len(broker.queue) > 0: + broker.parse_queue() + if args.one_shot: + logger.info('One shot mode, exiting') + break + + await asyncio.sleep(0.01) # sleep for 10ms + + else: + raise Exception('Deployment type not supported') + except Exception as e: + logger.error(f'Error monitoring: {traceback.format_exc()}') + raise e + finally: + logger.info('Exiting') + sys.exit(0)
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/config/config_object.html b/docs/build/html/_modules/poly_lithic/src/config/config_object.html new file mode 100644 index 0000000..2477f65 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/config/config_object.html @@ -0,0 +1,285 @@ + + + + + + + + + poly_lithic.src.config.config_object | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.config.config_object

+from typing import Any, Optional, Union
+from pydantic import computed_field
+import pydantic
+import networkx as nx
+from ..transformers import registered_transformers
+from matplotlib import pyplot as plt
+from uuid import uuid4
+import os
+import logging
+
+allowed_transformers = list(registered_transformers.keys())
+
+
+
+[docs] +class ModuleConfig(pydantic.BaseModel): + type: str + name: str + pub: Optional[Union[str, list]] = None + sub: Optional[Union[str, list]] = None + module_args: Optional[Union[dict[str, Union[str, dict, bool]], str]] = None + config: Any = None # kind of a free for all for now but we can narrow down the specifics later + +
+[docs] + @pydantic.field_validator('module_args', mode='before') + def validate_module_args(cls, v): + if isinstance(v, str): + return {} + return v
+
+ +
+ +
+[docs] +class DeploymentConfig(pydantic.BaseModel): + type: str + rate: Optional[Union[float, int]] = None
+ +
+ +
+[docs] +class ConfigObject(pydantic.BaseModel): + deployment: DeploymentConfig + modules: dict[str, ModuleConfig] + +
+[docs] + class Config: + arbitrary_types_allowed = True # to allow nx.DiGraph
+ +
+ @computed_field(return_type=nx.DiGraph) + @property + def graph(self): + G = nx.DiGraph() + nodes = [] + edges = [] + # to collect edges we need to go through each item , look at what its publishing and find matching subsribers, this will form an edge + for key, value in self.modules.items(): + if value.pub is not None or value.pub.lower() != 'none': + if isinstance(value.pub, str): + value.pub = [value.pub] + for key2, value2 in self.modules.items(): + if value2.sub is not None: + # normalise them all to lists + if isinstance(value2.sub, str): + value2.sub = [value2.sub] + + for pub in value.pub: + for sub in value2.sub: + if pub == sub and value.name != value2.name: + edges.append((value.name, value2.name)) + + nodes.append(value.name) + + logging.debug(f'Nodes: {nodes}') + logging.debug(f'Edges: {edges}') + nodes = list(set(nodes)) + nodes = [x for x in nodes if x is not None] + edges = [tuple(x) for x in edges] + + G.add_nodes_from(nodes) + G.add_edges_from(edges) + return G + +
+[docs] + @pydantic.field_validator('graph') + def check_routing(cls, v): + isolated_nodes = list(nx.isolates(v)) + if isolated_nodes: + raise ValueError( + f'Isolated nodes found in routing graph: {isolated_nodes}, this means that a modules is niether a publisher or subscriber to any other module' + ) + return v
+ +
+
+[docs] + def draw_routing_graph(self): + G = self.graph + plt.figure(figsize=(10, 10)) + nx.draw(G, with_labels=True) + if not os.path.exists('./graphs'): + os.makedirs('./graphs') + plt.savefig('./graphs/{}_routing_graph.png'.format(uuid4()))
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/config/parser.html b/docs/build/html/_modules/poly_lithic/src/config/parser.html new file mode 100644 index 0000000..e770270 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/config/parser.html @@ -0,0 +1,201 @@ + + + + + + + + + poly_lithic.src.config.parser | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.config.parser

+import yaml
+from .config_object import ConfigObject
+
+
+
+[docs] +class ConfigParser: + def __init__(self, config_path): + self.config_path = config_path + +
+[docs] + def parse(self): + with open(self.config_path) as stream: + try: + data = yaml.safe_load(stream) + # logging.debug(data) + config_object = ConfigObject(**data) + _ = ( + config_object.graph + ) # Access the graph property to trigger validation + return config_object + except yaml.YAMLError as exc: + print(exc) + return None
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/interfaces.html b/docs/build/html/_modules/poly_lithic/src/interfaces.html new file mode 100644 index 0000000..82aab1f --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/interfaces.html @@ -0,0 +1,206 @@ + + + + + + + + + poly_lithic.src.interfaces | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.interfaces

+from poly_lithic.src.utils.lazyInterfaceLoader import AbstractInterfaceLoader
+
+
+
+[docs] +class InterfaceLoader(AbstractInterfaceLoader): + def __init__(self): + super().__init__() + +
+[docs] + def keys(self): + return ['k2eg', 'p4p', 'p4p_server', 'h5df']
+ +
+ def _load_interface(self, key): + if key == 'k2eg': + return self.import_module('.interfaces.k2eg_interface', 'K2EGInterface') + elif key == 'p4p': + return self.import_module('.interfaces.p4p_interface', 'SimplePVAInterface') + elif key == 'p4p_server': + return self.import_module( + '.interfaces.p4p_interface', 'SimlePVAInterfaceServer' + ) + elif key == 'h5df': + return self.import_module('.interfaces.file_interface', 'h5dfInterface') + else: + raise KeyError(f"Interface '{key}' not registered.")
+ +
+ +registered_interfaces = InterfaceLoader() +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/interfaces/BaseInterface.html b/docs/build/html/_modules/poly_lithic/src/interfaces/BaseInterface.html new file mode 100644 index 0000000..8f13f01 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/interfaces/BaseInterface.html @@ -0,0 +1,247 @@ + + + + + + + + + poly_lithic.src.interfaces.BaseInterface | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.interfaces.BaseInterface

+# Abstract class for all interfaces
+
+from abc import ABC, abstractmethod
+
+
+
+[docs] +class BaseDataInterface(ABC): + @abstractmethod + def __init__(self, config, **kwargs): + pass + +
+[docs] + @abstractmethod + def load(self, **kwargs): + pass
+ +
+
+[docs] + @abstractmethod + def save(self, data, **kwargs): + pass
+
+ +
+ +
+[docs] +class BaseInterface(ABC): + @abstractmethod + def __init__(self, config): + pass + +
+[docs] + @abstractmethod + def monitor(self, name, handler, **kwargs): + pass
+ +
+
+[docs] + @abstractmethod + def get(self, name, **kwargs): + pass
+ +
+
+[docs] + @abstractmethod + def put(self, name, value, **kwargs): + pass
+ +
+
+[docs] + @abstractmethod + def put_many(self, data, **kwargs): + pass
+ +
+
+[docs] + @abstractmethod + def get_many(self, data, **kwargs): + pass
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/interfaces/file_interface.html b/docs/build/html/_modules/poly_lithic/src/interfaces/file_interface.html new file mode 100644 index 0000000..ce8f369 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/interfaces/file_interface.html @@ -0,0 +1,221 @@ + + + + + + + + + poly_lithic.src.interfaces.file_interface | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.interfaces.file_interface

+import h5py
+
+from .BaseInterface import BaseDataInterface
+
+# This is a simple interface that reads and writes to a h5df file
+# the structure should be dataset per variable so that we can read one at a time
+
+
+
+[docs] +class h5dfInterface(BaseDataInterface): + def __init__(self, config): + self.path = config['path'] + # check if file exists + try: + with open(self.path): + pass + except FileNotFoundError: + raise FileNotFoundError(f'File {self.path} not found') + + # we want to be able to read one or write one at a time, yield one at a time, in a generator fashion, this is for backtesting +
+[docs] + def load(self, **kwargs): + with h5py.File(self.path, 'r') as f: + for key in f.keys(): + yield key, f[key][()]
+ +
+
+[docs] + def save(self, data, **kwargs): + with h5py.File(self.path, 'w') as f: + for key, value in data: + f.create_dataset(key, data=value)
+ +
+
+[docs] + def monitor(self, handler, **kwargs): + # this is a faux monitor, it will just read the file and call the handler with each key value pair + pass
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/interfaces/k2eg_interface.html b/docs/build/html/_modules/poly_lithic/src/interfaces/k2eg_interface.html new file mode 100644 index 0000000..73f11ef --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/interfaces/k2eg_interface.html @@ -0,0 +1,286 @@ + + + + + + + + + poly_lithic.src.interfaces.k2eg_interface | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.interfaces.k2eg_interface

+import uuid
+from concurrent.futures import ThreadPoolExecutor
+
+import k2eg
+from poly_lithic.src.logging_utils import get_logger
+
+from .BaseInterface import BaseInterface
+
+executor = ThreadPoolExecutor(20)
+
+logger = get_logger()
+
+# _dir = os.path.dirname(os.path.abspath(__file__))
+# os.environ["K2EG_PYTHON_CONFIGURATION_PATH_FOLDER"] = _dir
+# print(f"K2EG_PYTHON_CONFIGURATION_PATH_FOLDER: {_dir}")
+
+
+
+[docs] +class K2EGInterface(BaseInterface): + def __init__(self, config): + try: + self.client = k2eg.dml( + 'env', + 'app-test-3', + group_name=f'model-deployment-{str(uuid.uuid4())[0:15]}', + ) + except Exception as e: + print(f'Error initializing K2EGInterface: {e}') + self.close() + raise e + + pv_dict = config['variables'] + pv_url_list = [] + for pv in pv_dict: + pv_url_list.append(pv_dict[pv]['proto'] + '://' + pv_dict[pv]['name']) + self.pv_url_list = pv_url_list + self.symbol_list = list(pv_dict.keys()) + self.variable_list = list(pv_dict.keys()) + self.url_lookup = { + pv_dict[pv]['proto'] + '://' + pv_dict[pv]['name']: pv for pv in pv_dict + } + self.reverse_url_lookup = { + pv: pv_dict[pv]['proto'] + '://' + pv_dict[pv]['name'] for pv in pv_dict + } + + logger.debug(f'K2EGInterface initialized with pv_url_list: {self.pv_url_list}') + logger.debug(f'K2EGInterface initialized with symbol_list: {self.symbol_list}') + logger.debug(f'K2EGInterface initialized with url_lookup: {self.url_lookup}') + logger.debug( + f'K2EGInterface initialized with reverse_url_lookup: {self.reverse_url_lookup}' + ) + +
+[docs] + def monitor(self, handler, **kwargs): + logger.debug(f'Monitoring {self.pv_url_list}') + + try: + self.client.monitor_many(self.pv_url_list, handler, timeout=1000) + except Exception as e: + print(f'Error monitoring: {e}') + raise e
+ +
+
+[docs] + def get(self, name, **kwargs): + url = self.reverse_url_lookup[name] + value = self.client.get(url) + return name, value
+ +
+
+[docs] + def put(self, name, value, **kwargs): + # print(f"putting {name} with value {value}") + try: + self.client.put(self.reverse_url_lookup[name], value) + except Exception as e: + print(f'Error putting: {e}') + raise e
+ +
+
+[docs] + def put_many(self, data, **kwargs): + results = [] + for name, value in data.items(): + res = executor.submit(self.put, name, value) + results.append(res) + for res in results: + res.result()
+ +
+
+[docs] + def get_many(self, data, **kwargs): + pass
+ +
+
+[docs] + def close(self): + self.client.close() + print('K2EGInterface closed') + return True
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/interfaces/p4p_interface.html b/docs/build/html/_modules/poly_lithic/src/interfaces/p4p_interface.html new file mode 100644 index 0000000..8e8d08c --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/interfaces/p4p_interface.html @@ -0,0 +1,508 @@ + + + + + + + + + poly_lithic.src.interfaces.p4p_interface | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.interfaces.p4p_interface

+import os
+import time
+
+# anyScalar
+import numpy as np
+from p4p import Value
+from p4p.client.thread import Context
+from p4p.nt import NTNDArray, NTScalar
+from p4p.server import Server, StaticProvider
+from p4p.server.raw import ServOpWrap
+from p4p.server.thread import SharedPV
+from poly_lithic.src.logging_utils import get_logger
+
+from .BaseInterface import BaseInterface
+
+# multi pool
+
+
+logger = get_logger()
+
+# os.environ["EPICS_PVA_NAME_SERVERS"] = "localhost:5075"
+
+
+
+[docs] +class SimplePVAInterface(BaseInterface): + def __init__(self, config): + self.ctxt = Context('pva', nt=False) + if 'EPICS_PVA_NAME_SERVERS' in os.environ: + logger.warning( + f'EPICS_PVA_NAME_SERVERS: {os.environ["EPICS_PVA_NAME_SERVERS"]}' + ) + elif 'EPICS_PVA_NAME_SERVERS' in config: + os.environ['EPICS_PVA_NAME_SERVERS'] = config['EPICS_PVA_NAME_SERVERS'] + logger.warning( + f'EPICS_PVA_NAME_SERVERS: {os.environ["EPICS_PVA_NAME_SERVERS"]}' + ) + else: + logger.warning( + 'EPICS_PVA_NAME_SERVERS not set in config or environment, using localhost:5075' + ) + os.environ['EPICS_PVA_NAME_SERVERS'] = 'localhost:5075' + + pv_dict = config['variables'] + pv_list = [] + # print(f"pv_dict: {pv_dict}") + for pv in pv_dict: + try: + assert pv_dict[pv]['proto'] == 'pva' + except Exception: + logger.error(f'Protocol for {pv} is not pva') + raise AssertionError + pv_list.append(pv_dict[pv]['name']) + self.pv_list = pv_list + self.variable_list = list(pv_dict.keys()) + logger.debug(f'SimplePVAInterface initialized with pv_url_list: {self.pv_list}') + + def __handler_wrapper(self, handler, name): + # unwrap p4p.Value into name, value + def wrapped_handler(value): + # logger.debug(f"SimplePVAInterface handler for {name, value['value']}") + + handler(name, {'value': value['value']}) + + return wrapped_handler + +
+[docs] + def monitor(self, handler, **kwargs): + for pv in self.pv_list: + try: + new_handler = self.__handler_wrapper(handler, pv) + self.ctxt.monitor(pv, new_handler) + except Exception as e: + logger.error( + f'Error monitoring in function monitor for SimplePVAInterface: {e}' + ) + logger.error(f'pv: {pv}') + raise e
+ +
+
+[docs] + def get(self, name, **kwargs): + value = self.ctxt.get(name) + if isinstance(value['value'], np.ndarray): + # if value has dimension + if 'dimension' in value: + y_size = value['dimension'][0]['size'] + x_size = value['dimension'][1]['size'] + value = value['value'].reshape((y_size, x_size)) + else: + value = value['value'] + else: + value = value['value'] + + value = {'value': value} + return name, value
+ +
+
+[docs] + def put(self, name, value, **kwargs): + if isinstance(value, np.ndarray): + value = NTNDArray().wrap(value) + else: + value = value + return self.ctxt.put(name, value)
+ +
+
+[docs] + def put_many(self, data, **kwargs): + for key, value in data.items(): + self.put(key, value)
+ +
+
+[docs] + def get_many(self, data, **kwargs): + results = self.ctxt.get(data, throw=False) + output = {} + # print(f"results: {results}") + for value, key in zip(results, data): + if isinstance(value['value'], np.ndarray): + # if value has dimension + if 'dimension' in value: + y_size = value['dimension'][0]['size'] + x_size = value['dimension'][1]['size'] + value = value['value'].reshape((y_size, x_size)) + else: + value = value['value'] + else: + value = value['value'] + + output[key] = {'value': value} + + return output
+ +
+
+[docs] + def close(self): + logger.debug('Closing SimplePVAInterface') + self.ctxt.close()
+
+ +
+ +
+[docs] +class SimlePVAInterfaceServer(SimplePVAInterface): + """ + Simple PVA integfcae with a server rather than just a client, this will host the PVs provided in the config + """ + + def __init__(self, config): + super().__init__(config) + self.shared_pvs = {} + pv_type_init = None + pv_type_nt = None + self.kv_store = {} + + if 'port' in config: + port = config['port'] + else: + port = ( + 5075 # this will fail if we have two servers running on the same port + ) + + # if "init" in config: + # if not config["init"]: + # self.init_pvs = False + # else: + # self.init_pvs = True + # else: + # self.init_pvs = True + + # print(f"self.init_pvs: {self.init_pvs}") + + for pv in self.pv_list: + if 'type' in config['variables'][pv]: + pv_type = config['variables'][pv]['type'] + if pv_type == 'image': + # note the y and x are flipped when reshaping (rows, columns) -> (y, x) + y_size = config['variables'][pv]['image_size']['y'] + x_size = config['variables'][pv]['image_size']['x'] + intial_value = np.zeros((y_size, x_size)) + pv_type_nt = NTNDArray() + pv_type_init = intial_value + self.value_build_fn = None + if 'default' in config['variables'][pv]: + raise NotImplementedError( + 'Default values for images not implemented' + ) + + # waveform or array + elif pv_type == 'waveform' or pv_type == 'array': + # print(f'pv: {pv}') + if 'length' in config['variables'][pv]: + length = config['variables'][pv]['length'] + else: + length = 10 + if 'default' in config['variables'][pv]: + intial_value = np.array(config['variables'][pv]['default']) + else: + intial_value = np.zeros(length, dtype=np.float64) + + pv_type_nt = NTScalar('ad') + pv_type_nt_bd = NTScalar.buildType('ad') + self.value_build_fn = Value(pv_type_nt_bd, {'value': intial_value}) + pv_type_init = intial_value + + elif pv_type == 'scalar': + pv_type_nt = NTScalar('d') + if 'default' in config['variables'][pv]: + pv_type_init = float(config['variables'][pv]['default']) + else: + pv_type_init = 0.0 + + else: + raise TypeError(f'Unknown PV type for {pv}: {pv_type}') + else: + pv_type_nt = NTScalar('d') + if 'default' in config['variables'][pv]: + pv_type_init = float(config['variables'][pv]['default']) + else: + pv_type_init = 0.0 + self.value_build_fn = None + + pv_item = {pv: SharedPV(initial=pv_type_init, nt=pv_type_nt)} + # print(f"pv_item: {pv_item}") + # print(f"pv_type_init: {pv_type_init}") + # print(f"pv_type_nt: {pv_type_nt}") + + @pv_item[pv].put + def put(pv: SharedPV, op: ServOpWrap): + pv.post(op.value(), timestamp=time.time()) + op.done() + + self.shared_pvs[pv] = pv_item[pv] + + self.provider = StaticProvider('pva') + for name, pv in self.shared_pvs.items(): + self.provider.add(name, pv) + + self.server = Server( + providers=[self.provider], conf={'EPICS_PVA_SERVER_PORT': str(port)} + ) + + # for pv in self.pv_list: + # self.server.start() + logger.info( + f'SimplePVAInterfaceServer initialized with config: {self.server.conf()}' + ) + +
+[docs] + def close(self): + logger.debug('Closing SimplePVAInterfaceServer') + self.server.stop() + super().close()
+ +
+
+[docs] + def put(self, name, value, **kwargs): + # if not open then open + if not self.shared_pvs[name].isOpen(): + self.shared_pvs[name].open(value) + else: + self.shared_pvs[name].post(value, timestamp=time.time())
+ +
+
+[docs] + def get(self, name, **kwargs): + value_raw = self.shared_pvs[name].current().raw + if isinstance(value_raw.value, np.ndarray): + # if value has dimension + if 'dimension' in value_raw: + y_size = value_raw.dimension[0]['size'] + x_size = value_raw.dimension[1]['size'] + value = value_raw.value.reshape((y_size, x_size)) + else: + value = value_raw.value + + elif ( + type(value_raw.value) == float + or type(value_raw.value) == int + or type(value_raw.value) == bool + ): + value = value_raw.value + + else: + raise ValueError(f'Unknown type for value_raw: {type(value_raw.value)}') + # print(f"value: {value}") + return name, {'value': value}
+ +
+
+[docs] + def put_many(self, data, **kwargs): + # for key, value in data.items(): + # self.put(key, value) + for key, value in data.items(): + # result = self.ctxt.put(key, value) + self.shared_pvs[key].post(value, timestamp=time.time())
+ +
# result = self.ctxt.put(channel_names,values, throw=False) + # with ThreadPool(processes=24) as pool: + # for key, value in data.items(): + # channel_names.append(key) + # values.append(value) + # pool.starmap(self.put, zip(channel_names, values)) + +
+[docs] + def get_many(self, data, **kwargs): + output_dict = {} + for key in data: + result = self.get(key) + output_dict[result[0]] = result[1] + # with ThreadPool(processes=24) as pool: + # results = pool.starmap(self.get, [(key,) for key in data]) + # for result in results: + # output_dict[result[0]] = result[1] + + return output_dict
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/logging_utils/make_logger.html b/docs/build/html/_modules/poly_lithic/src/logging_utils/make_logger.html new file mode 100644 index 0000000..b8cb8ed --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/logging_utils/make_logger.html @@ -0,0 +1,233 @@ + + + + + + + + + poly_lithic.src.logging_utils.make_logger | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.logging_utils.make_logger

+import logging
+
+import colorlog
+
+
+
+[docs] +def make_logger(name='model_manager', level=logging.INFO): + logger = colorlog.getLogger(name) + handler = colorlog.StreamHandler() + formatter = colorlog.ColoredFormatter( + '%(log_color)s[%(filename)s:%(lineno)s:%(funcName)s] %(levelname)s: %(message)s', + datefmt=None, + reset=True, + log_colors={ + 'DEBUG': 'cyan', + 'INFO': 'green', + 'WARNING': 'yellow', + 'ERROR': 'red', + 'CRITICAL': 'red,bg_white', + }, + secondary_log_colors={}, + style='%', + ) + handler.setFormatter(formatter) + + # check if logger has handlers + if logger.hasHandlers(): + logger.handlers.clear() + logger.addHandler(handler) + + logger.setLevel(level) + logger.propagate = ( + False # Prevent the log messages from being duplicated in the python consoles + ) + + return logger
+ +
+ +
+[docs] +def get_logger(): + logger = logging.getLogger('model_manager') + return logger
+ +
+ +
+[docs] +def reset_logging(): + # Remove all handlers from the root logger + root = logging.getLogger() + for handler in root.handlers[:]: + root.removeHandler(handler) + + # Reset the logging configuration + logging.basicConfig(level=logging.ERROR)
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/model_utils/LocalModelGetter.html b/docs/build/html/_modules/poly_lithic/src/model_utils/LocalModelGetter.html new file mode 100644 index 0000000..a976f0a --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/model_utils/LocalModelGetter.html @@ -0,0 +1,219 @@ + + + + + + + + + poly_lithic.src.model_utils.LocalModelGetter | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.model_utils.LocalModelGetter

+# not implemented yet warning
+import importlib.util
+
+from poly_lithic.src.logging_utils import get_logger
+from poly_lithic.src.model_utils import ModelGetterBase
+
+logger = get_logger()
+
+
+
+[docs] +class LocalModelGetter(ModelGetterBase): + def __init__(self, config): + self.model_module_path = config['model_path'] + self.model_class_name = config['model_factory_class'] + self.model_type = 'local' + self.requirements = config.get('requirements', None) + logger.debug( + f"LocalModelGetter initialized with model_module_path: {self.model_module_path}, " + f"model_class_name: {self.model_class_name}, requirements: {self.requirements}" + ) + +
+[docs] + def get_model(self): + # Import the model class from the specified module + spec = importlib.util.spec_from_file_location( + 'model_module', self.model_module_path + ) + model_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(model_module) + model_factory_class = getattr(model_module, self.model_class_name) + + # Create an instance of the model factory class + model_factory = model_factory_class() + model = model_factory.get_model() + return model
+ +
+
+[docs] + def get_requirements(self): + return self.requirements
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/model_utils/MlflowModelGetter.html b/docs/build/html/_modules/poly_lithic/src/model_utils/MlflowModelGetter.html new file mode 100644 index 0000000..48f2fb7 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/model_utils/MlflowModelGetter.html @@ -0,0 +1,296 @@ + + + + + + + + + poly_lithic.src.model_utils.MlflowModelGetter | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.model_utils.MlflowModelGetter

+import mlflow
+from mlflow import MlflowClient
+from mlflow.models.model import get_model_info
+from poly_lithic.src.logging_utils import get_logger
+from poly_lithic.src.model_utils import ModelGetterBase
+
+logger = get_logger()
+
+try:
+    from lume_model.models import TorchModel, TorchModule
+    LUME_MODEL_AVAILABLE = True
+except ImportError:
+    logger.warning("lume_model is not installed. TorchModel and TorchModule functionality will not be available.")
+    LUME_MODEL_AVAILABLE = False
+
+
+
+[docs] +class MLflowModelGetter(ModelGetterBase): + def __init__(self, config): + model_name = config['model_name'] + model_version = config['model_version'] + logger.debug(f'MLflowModelGetter: {model_name}, {model_version}') + self.model_name = model_name + self.model_version = model_version + self.client = MlflowClient() + self.model_type = None + self.tags = None + +
+[docs] + def get_config(self): + self.get_tags() + version = self.client.get_model_version(self.model_name, self.model_version) + + if 'artifact_location' in self.tags.keys(): + artifact_location = self.tags['artifact_location'] + logger.debug(f'Artifact location: {artifact_location}') + else: + artifact_location = version.name + logger.debug(f'Artifact location: {artifact_location}') + self.client.download_artifacts( + version.run_id, f'{artifact_location}/pv_mapping.yaml', '.' + ) + # return yaml.load( + # open(f"{artifact_location}/pv_mapping.yaml", "r"), Loader=yaml.FullLoader + # ) + return f'{artifact_location}/pv_mapping.yaml'
+ +
+
+[docs] + def get_tags(self): + registry_model = self.client.get_registered_model(self.model_name) + self.tags = registry_model.tags
+ +
+
+[docs] + def get_requirements(self): + # Get dependencies + if int(self.model_version) >= 0: + version = self.client.get_model_version(self.model_name, self.model_version) + elif self.model_version == 'champion': # this is stupid I need to change it + version_no = self.client.get_model_version_by_alias( + self.model_name, self.model_version + ) + version = self.client.get_model_version(self.model_name, version_no.version) + + deps = mlflow.artifacts.download_artifacts(f'{version.source}/requirements.txt') + return deps
+ +
+
+[docs] + def get_model(self): + self.get_tags() + version = self.client.get_model_version(self.model_name, self.model_version) + + # flavor + flavor = get_model_info(model_uri=version.source).flavors + loader_module = flavor['python_function']['loader_module'] + logger.debug(f'Loader module: {loader_module}') + + if loader_module == 'mlflow.pyfunc.model': + logger.debug('Loading pyfunc model') + model_pyfunc = mlflow.pyfunc.load_model(model_uri=version.source) + + # check if model has.get_lume_model() method + if not hasattr(model_pyfunc.unwrap_python_model(), 'get_lume_model'): + # check if it has get__model() method + if not hasattr(model_pyfunc.unwrap_python_model(), 'get_model'): + raise Exception( + 'Model does not have get_lume_model() or get_model() method' + ) + else: + logger.debug('Model has get_model() method') + logger.warning( + 'get_model() suggests a non-LUME model, please check if model has an evaluate method' + ) + model = model_pyfunc.unwrap_python_model().get_model() + else: + logger.debug('Model has get_lume_model() method') + model = model_pyfunc.unwrap_python_model().get_lume_model() + + logger.debug(f'Model: {model}, Model type: {type(model)}') + self.model_type = 'pyfunc' + return model + + elif loader_module == 'mlflow.pytorch': + print('Loading torch model') + model_torch_module = mlflow.pytorch.load_model(model_uri=version.source) + assert isinstance(model_torch_module, TorchModule) + model = model_torch_module.model + assert isinstance(model, TorchModel) + logger.debug(f'Model: {model}, Model type: {type(model)}') + self.model_type = 'torch' + return model + else: + raise Exception(f'Flavor {flavor} not supported')
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/model_utils/ModelGetterBase.html b/docs/build/html/_modules/poly_lithic/src/model_utils/ModelGetterBase.html new file mode 100644 index 0000000..aad1f80 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/model_utils/ModelGetterBase.html @@ -0,0 +1,203 @@ + + + + + + + + + poly_lithic.src.model_utils.ModelGetterBase | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.model_utils.ModelGetterBase

+# abstract class ModelGetterBase
+from abc import ABC, abstractmethod
+
+
+
+[docs] +class ModelGetterBase(ABC): +
+[docs] + @abstractmethod + def get_model(self): + pass
+ +
+ # @abstractmethod + # def get_config(self): + # pass + +
+[docs] + @abstractmethod + def get_requirements(self): + pass
+
+ +
+ # @abstractmethod + # def get_tags(self): + # pass +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/transformers.html b/docs/build/html/_modules/poly_lithic/src/transformers.html new file mode 100644 index 0000000..a4d454a --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/transformers.html @@ -0,0 +1,217 @@ + + + + + + + + + poly_lithic.src.transformers | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.transformers

+from poly_lithic.src.utils.lazyInterfaceLoader import AbstractInterfaceLoader
+
+
+
+[docs] +class TransformerLoader(AbstractInterfaceLoader): + def __init__(self): + super().__init__() + +
+[docs] + def keys(self): + return [ + 'SimpleTransformer', + 'CAImageTransfomer', + 'CompoundTransformer', + 'PassThroughTransformer', + ]
+ +
+ def _load_interface(self, key): + if key == 'SimpleTransformer': + return self.import_module( + '.transformers.BaseTransformers', 'SimpleTransformer' + ) + elif key == 'CAImageTransfomer': + return self.import_module( + '.transformers.BaseTransformers', 'CAImageTransfomer' + ) + elif key == 'CompoundTransformer': + return self.import_module( + '.transformers.CompoundTransformer', 'CompoundTransformer' + ) + elif key == 'PassThroughTransformer': + return self.import_module( + '.transformers.BaseTransformers', 'PassThroughTransformer' + ) + else: + raise KeyError(f"Interface '{key}' not registered.")
+ +
+ +registered_transformers = TransformerLoader() +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/transformers/BaseTransformer.html b/docs/build/html/_modules/poly_lithic/src/transformers/BaseTransformer.html new file mode 100644 index 0000000..cc171bd --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/transformers/BaseTransformer.html @@ -0,0 +1,211 @@ + + + + + + + + + poly_lithic.src.transformers.BaseTransformer | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.transformers.BaseTransformer

+from abc import abstractmethod
+
+
+
+[docs] +class BaseTransformer: + @abstractmethod + def __init__(self, config: dict): + """ + config: dict passed from the pv_mappings.yaml files. + """ + pass + +
+[docs] + @abstractmethod + def transform(self): + """ + Call transform function to transform the input data, see SimpleTransformer in model_manager/src/transformers/BaseTransformers.py for an example. + """ + pass
+ +
+
+[docs] + @abstractmethod + def handler(self, pv_name: str, value: dict | float | int): + """ + Handler function to handle the input data, in most cases it initiates the transform function when all the input data is available. + Handler is the only function exposed to the main loop of the program aside from initial configuration. + """ + pass
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/transformers/BaseTransformers.html b/docs/build/html/_modules/poly_lithic/src/transformers/BaseTransformers.html new file mode 100644 index 0000000..e85bfeb --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/transformers/BaseTransformers.html @@ -0,0 +1,480 @@ + + + + + + + + + poly_lithic.src.transformers.BaseTransformers | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.transformers.BaseTransformers

+import time
+import numpy as np
+import sympy as sp
+from poly_lithic.src.logging_utils.make_logger import get_logger
+from poly_lithic.src.transformers.BaseTransformer import BaseTransformer
+
+logger = get_logger()
+
+
+
+[docs] +class SimpleTransformer(BaseTransformer): + def __init__(self, config): + """ + config: dict + dictionary containing the following keys: + - variables: dict + dictionary containing the following keys: + - formula: str + formula to be used for transformation + - symbols: list + list of symbols to be used in the formula + """ + + pv_mapping = config['variables'] + self.input_list = config['symbols'] + + logger.debug('Initializing SimpleTransformer') + logger.debug(f'PV Mapping: {pv_mapping}') + logger.debug(f'Symbol List: {self.input_list}') + self.pv_mapping = pv_mapping + + for key, value in self.pv_mapping.items(): + self.__validate_formulas(value['formula']) + self.latest_input = {symbol: None for symbol in self.input_list} + self.latest_transformed = {key: 0 for key in self.pv_mapping.keys()} + self.updated = False + self.handler_time = None + self.formulas = {} + self.lambdified_formulas = {} + for key, value in self.pv_mapping.items(): + self.formulas[key] = sp.sympify(value['formula'].replace(':', '_')) + input_list_renamed = [ + symbol.replace(':', '_') for symbol in self.input_list + ] + self.lambdified_formulas[key] = sp.lambdify( + input_list_renamed, self.formulas[key], modules='numpy' + ) + + self.handler_time = [] + + def __validate_formulas(self, formula: str): + try: + sp.sympify(formula.replace(':', '_')) + except Exception as e: + raise Exception(f'Invalid formula: {formula}: {e}') + +
+[docs] + def handler(self, pv_name, value): + # logger.debug(f"SimpleTransformer handler for {pv_name} with value {value}") + + # chek if pv_name is in sel.input_list + if pv_name in self.input_list: + # assert value is float + try: + if isinstance(value['value'], (float, int, np.float32)): + value = float(value['value']) + elif isinstance(value['value'], (np.ndarray, list)): + value = np.array(value['value']).astype(float) + else: + raise Exception( + f'Invalid type for value: {value}, type: {type(value["value"])}' + ) + except Exception as e: + logger.error(f'Error converting value to float: {e}') + raise e + + self.latest_input[pv_name] = value + + try: + if all([value is not None for value in self.latest_input.values()]): + time_start = time.time() + self.transform() + self.handler_time = time.time() - time_start + # logger.info(f'Handler time for {pv_name} is {self.handler_time}') + # if self.handler_time > 0.5: + # logger.warning(f'Handler time for {pv_name} is {self.handler_time}') + # print(f'self.latest_input: {self.latest_input}') + # print(f'self.latest_transformed: {self.latest_transformed}') + except Exception as e: + logger.error(f'Error transforming: {e}') + raise e + else: + logger.debug(f'PV name {pv_name} not in input list')
+ +
+ # def transform(self): + # # logger.debug("Transforming") + # transformed = {} + # pvs_renamed = { + # key.replace(':', '_'): value for key, value in self.latest_input.items() + # } + # pv_shapes = {} + + # # convert to sympy symbols + + # for key, value in pvs_renamed.items(): + # if isinstance(value, (np.ndarray, list)): + # pv_shapes[key] = value.shape + # pvs_renamed[key] = sp.Matrix(value) + # elif isinstance(value, (float, int)): + # pvs_renamed[key] = value + # else: + # raise Exception(f'Invalid type for value: {value}') + + # for key, value in self.pv_mapping.items(): + # try: + # # formula = value['formula'].replace(':', '_') + + # # formula = sp.sympify(formula) + # formula = self.formulas[key] + # transformed[key] = formula.subs(pvs_renamed) + # # print(transformed[key]) + # # converted to float + # if isinstance(transformed[key], sp.Matrix | sp.ImmutableDenseMatrix): + # # bit hacky but casuse sympy is meant to be symbolic only and not numerical + # s = sp.symbols('s') + # numpy_value = sp.lambdify(s, transformed[key], modules='numpy') + # numpy_value = numpy_value(0) + # transformed[key] = numpy_value + # # drop last dim if it is 1 + # if transformed[key].shape[-1] == 1: + # transformed[key] = transformed[key].squeeze() + # else: + # transformed[key] = float(transformed[key]) + + # except Exception as e: + # logger.error(f'Error transforming: {e}') + # raise e + + # for key, value in transformed.items(): + # self.latest_transformed[key] = value + # self.updated = True +
+[docs] + def transform(self): + transformed = {} + pvs_renamed = { + key.replace(':', '_'): value for key, value in self.latest_input.items() + } + + for key, value in self.pv_mapping.items(): + try: + lambdified_formula = self.lambdified_formulas[key] + transformed[key] = lambdified_formula(*[ + pvs_renamed[symbol.replace(':', '_')] for symbol in self.input_list + ]) + + if isinstance(transformed[key], np.ndarray): + if transformed[key].shape[-1] == 1: + transformed[key] = transformed[key].squeeze() + else: + transformed[key] = float(transformed[key]) + + except Exception as e: + logger.error(f'Error transforming: {e}') + raise e + + for key, value in transformed.items(): + self.latest_transformed[key] = value + self.updated = True
+
+ +
+ +
+[docs] +class CAImageTransfomer(BaseTransformer): + """Input only image transformation""" + + def __init__(self, config) -> None: + self.img = config['variables'] + self.img_list = list(self.img.keys()) + + self.variables = {} + self.input_list = [] + for key, value in self.img.items(): + self.variables[key] = value['img_ch'] + self.variables[key + '_x'] = value['img_x_ch'] + self.variables[key + '_y'] = value['img_y_ch'] + if 'unfold' in value.keys(): + self.variables[key + '_unfolding'] = value['unfold'] + else: + self.variables[key + '_unfolding'] = 'row_major' + self.input_list.append(value['img_ch']) + self.input_list.append(value['img_x_ch']) + self.input_list.append(value['img_y_ch']) + + self.latest_input = {symbol: None for symbol in self.input_list} + self.latest_transformed = {key: 0 for key in self.variables.keys()} + + self.handler_time = None + self.updated = False + +
+[docs] + def handler(self, variable_name: str, value: dict): + logger.debug(f'CAImageTransfomer handler for {variable_name}') + try: + self.latest_input[variable_name] = value['value'] + if all([value is not None for value in self.latest_input.values()]): + time_start = time.time() + self.transform() + self.handler_time = time.time() - time_start + else: + logger.debug('Not all values are present') + except Exception as e: + logger.error(f'Error transforming: {e}') + raise e
+ +
+
+[docs] + def transform(self): + logger.debug('Transforming') + transformed = {} + for key in self.img_list: + value = self.latest_input[self.variables[key]] + # print x and y + try: + transformed[key] = np.array(value).reshape( + ( + int( + self.latest_input[self.variables[key + '_y']] + ), # note the order, we are going from x,y to y,x (rows, columns) in numpy + int(self.latest_input[self.variables[key + '_x']]), + ), + order='F' + if self.variables[key + '_unfolding'] == 'column_major' + else 'C', + ) + + if self.variables[key + '_unfolding'] == 'column_major': + transformed[key] = transformed[key].T + except Exception as e: + logger.error(f'Error transforming: {e}') + for key, value in transformed.items(): + self.latest_transformed[key] = value + self.updated = True
+
+ +
+ +
+[docs] +class PassThroughTransformer(BaseTransformer): + def __init__(self, config): + # config is a dictionary of output:intput pairs + pv_mapping = config['variables'] + self.latest_input = {} + self.latest_transformed = {} + self.updated = False + self.input_list = list(pv_mapping.values()) + + for key, value in pv_mapping.items(): + self.latest_input[value] = None + self.latest_transformed[key] = None + self.pv_mapping = pv_mapping + + self.handler_time = 0 + +
+[docs] + def handler(self, pv_name, value): + time_start = time.time() + logger.debug(f'PassThroughTransformer handler for {pv_name}') + self.latest_input[pv_name] = value['value'] + if all([value is not None for value in self.latest_input.values()]): + self.transform() + self.updated = True + time_end = time.time() + self.handler_time = time_end - time_start
+ +
+
+[docs] + def transform(self): + logger.debug('Transforming') + for key, value in self.pv_mapping.items(): + self.latest_transformed[key] = self.latest_input[value] + + if isinstance(self.latest_input[value], np.ndarray): + if self.latest_input[value].shape != self.latest_transformed[key].shape: + logger.error(f'Shape mismatch between input and output for {key}') + self.updated = True
+
+ +
+ # for key, value in self.latest_input.items(): + # logger.debug(f"{key}: {value.shape}") + # for key, value in self.latest_transformed.items(): + # logger.debug(f"{key}: {value.shape}") +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/transformers/CompoundTransformer.html b/docs/build/html/_modules/poly_lithic/src/transformers/CompoundTransformer.html new file mode 100644 index 0000000..87a30ae --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/transformers/CompoundTransformer.html @@ -0,0 +1,256 @@ + + + + + + + + + poly_lithic.src.transformers.CompoundTransformer | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.transformers.CompoundTransformer

+# defines a compound transformer that can be used add multiple transformers together
+import time
+
+from poly_lithic.src.logging_utils.make_logger import get_logger
+from poly_lithic.src.transformers import registered_transformers
+
+from poly_lithic.src.transformers.BaseTransformer import BaseTransformer
+
+logger = get_logger()
+
+
+# config4 = {
+#     "transformers": {
+#         "transformer_1": {"type": "SimpleTransformer", "config": config2},
+#         "transformer_2": {"type": "CAImageTransfomer", "config": config3},
+#     }
+# }
+
+
+
+[docs] +class CompoundTransformer(BaseTransformer): + def __init__(self, config): + logger.debug('Initializing CompoundTransformer') + self.transformers = [] + self.latest_input = {} + self.latest_transformed = {} + self.input_list = [] + for transformer in config['transformers']: + # print(transformer, config["transformers"][transformer]) + transformer_type = config['transformers'][transformer]['type'] + transformer_config = config['transformers'][transformer]['config'] + self.transformers.append( + registered_transformers[transformer_type](transformer_config) + ) + self.input_list += self.transformers[-1].input_list + # merge dicts + self.latest_transformed = { + **self.latest_transformed, + **self.transformers[-1].latest_transformed, + } + self.latest_input = { + **self.latest_input, + **self.transformers[-1].latest_input, + } + self.updated = False + self.handler_time = 0 + +
+[docs] + def transform(self, data): + for transformer in self.transformers: + data = transformer.transform(data) + + return data
+ +
+
+[docs] + def handler(self, name, data): + time_start = time.time() + logger.debug(f'CompoundTransformer handler for {name}') + for transformer in self.transformers: + if name in transformer.input_list: + transformer.handler(name, data) + if transformer.updated: + self.updated = True + self.latest_transformed = { + **self.latest_transformed, + **transformer.latest_transformed, + } + self.latest_input = { + **self.latest_input, + **transformer.latest_input, + } + time_end = time.time() + self.handler_time = time_end - time_start
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/utils/builder.html b/docs/build/html/_modules/poly_lithic/src/utils/builder.html new file mode 100644 index 0000000..f8869c2 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/utils/builder.html @@ -0,0 +1,305 @@ + + + + + + + + + poly_lithic.src.utils.builder | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.utils.builder

+from poly_lithic.src.config import ConfigParser
+from poly_lithic.src.interfaces import registered_interfaces
+from poly_lithic.src.logging_utils import get_logger
+from poly_lithic.src.transformers import registered_transformers
+from poly_lithic.src.utils.messaging import (
+    ModelObserver,
+    InterfaceObserver,
+    MessageBroker,
+    TransformerObserver,
+)
+
+logger = get_logger()
+
+
+
+[docs] +class MockModel: + def __init__(self): + """placeholder for model""" + +
+[docs] + def evaluate(self, value): + """placeholder for model prediction""" + return {'not_initialized': {'value': -99999999999}}
+
+ +
+ +
+[docs] +class Builder: + def __init__(self, config_path): + self.config = self.__initailise_config(config_path) + self.logger = get_logger() + + def __initailise_config(self, config_path): + """Initialise the configuration.""" + try: + config_parser = ConfigParser(config_path) + return config_parser.parse() + except Exception as e: + logger.error(f'Error initializing configuration: {e}') + raise e + +
+[docs] + def build(self) -> MessageBroker: + """Build the model manager.""" + + self.__build_observers() + self.__build_broker() + + for name, observer in self.loaded_observers.items(): + self.broker.attach(observer, self.config.modules[name].sub) + + return self.broker
+ +
+ def __build_observers(self): + """Build the observers.""" + + loaded_observers: dict[str, object] = {} + for module in self.config.modules: + try: + module_type, module_subtype = self.config.modules[module].type.split( + '.' + ) + except ValueError: + raise ValueError( + f"Invalid module type: {self.config.modules[module].type} must be of the form 'type.subtype'" + ) + + if module_type == 'interface': + if self.config.modules[module].module_args is not None: + args = self.config.modules[module].module_args + else: + args = {} + interface = InterfaceObserver( + registered_interfaces[module_subtype]( + self.config.modules[module].config + ), + self.config.modules[module].pub, + *args, + ) + loaded_observers[module] = interface + elif module_type == 'transformer': + if self.config.modules[module].module_args is not None: + args = self.config.modules[module].module_args + else: + args = {} + transformer = TransformerObserver( + registered_transformers[module_subtype]( + self.config.modules[module].config + ), + self.config.modules[module].pub, + *args, + ) + loaded_observers[module] = transformer + elif module_type == 'model': + if self.config.modules[module].module_args is not None: + args = self.config.modules[module].module_args + else: + args = {} + observer = ModelObserver( + config=self.config.modules[module].config, + topic=self.config.modules[module].pub, + *args, + ) + loaded_observers[module] = observer + else: + raise ValueError(f'Invalid module type: {module_type}') + logger.debug(f'Loaded observers: {loaded_observers}') + + # lets validate all of them are Observers + for observer in loaded_observers: + if not isinstance( + loaded_observers[observer], + (ModelObserver, InterfaceObserver, TransformerObserver), + ): + raise ValueError( + f'Invalid observer: {observer} must be of type ModelObserver, InterfaceObserver or TransformerObserver' + ) + self.loaded_observers = loaded_observers + return None + + def __build_broker(self): + """Build the message broker.""" + self.broker = MessageBroker() + logger.debug(f'Built broker: {self.broker}')
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/utils/lazyInterfaceLoader.html b/docs/build/html/_modules/poly_lithic/src/utils/lazyInterfaceLoader.html new file mode 100644 index 0000000..6a2ace3 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/utils/lazyInterfaceLoader.html @@ -0,0 +1,217 @@ + + + + + + + + + poly_lithic.src.utils.lazyInterfaceLoader | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.utils.lazyInterfaceLoader

+import importlib
+from abc import ABC, abstractmethod
+
+
+
+[docs] +class AbstractInterfaceLoader(ABC): + """Abstract base class to manage lazy loading of interfaces.""" + + def __init__(self): + self._interfaces = {} + + def __getitem__(self, key): + if key not in self._interfaces: + self._interfaces[key] = self._load_interface(key) + return self._interfaces[key] + +
+[docs] + @abstractmethod + def keys(self): + """Abstract method to return a list of keys for all available interfaces."""
+ +
+ @abstractmethod + def _load_interface(self, key): + """Abstract method to load an interface dynamically. + Subclasses must override this method to provide custom loading logic. + """ + +
+[docs] + def import_module(self, module_name, class_name): + """Utility function to dynamically import a module and class.""" + try: + # Assuming 'src.interfaces' is the parent package for all your modules + module = importlib.import_module(module_name, package='poly_lithic.src') + return getattr(module, class_name) + except ImportError as e: + print(f'Error importing {module_name}: {e}') + raise e
+
+ +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/poly_lithic/src/utils/messaging.html b/docs/build/html/_modules/poly_lithic/src/utils/messaging.html new file mode 100644 index 0000000..b281484 --- /dev/null +++ b/docs/build/html/_modules/poly_lithic/src/utils/messaging.html @@ -0,0 +1,697 @@ + + + + + + + + + poly_lithic.src.utils.messaging | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+

Source code for poly_lithic.src.utils.messaging

+from abc import ABC, abstractmethod
+from typing import Dict, Any, Optional, Union
+from pydantic import (
+    BaseModel,
+    Field,
+    field_validator,
+    computed_field,
+)
+import time
+from poly_lithic.src.logging_utils import get_logger
+from poly_lithic.src.transformers import BaseTransformer
+from poly_lithic.src.interfaces import BaseInterface
+from poly_lithic.src.model_utils import registered_model_getters
+import os
+# from deepdiff import DeepDiff
+import hashlib
+logger = get_logger()
+
+import cProfile
+
+
+
+[docs] +def profileit(func): + def wrapper(*args, **kwargs): + start_time = time.time() + datafn = func.__name__ + '.profile' # Name the data file sensibly + prof = cProfile.Profile() + retval = prof.runcall(func, *args, **kwargs) + end = time.time() + if end - start_time > 0.3: + prof.dump_stats(datafn) + return retval + + return wrapper
+ +
+ +
+[docs] +class Message(BaseModel): + topic: Union[str, list[str]] + source: str + ## key: str made a mess of this by including a key, no need to include a key + value: dict = Field(default_factory=dict) + timestamp: float = Field(default_factory=time.time) + # optional + allow_unsafe: Optional[bool] = False + +
+[docs] + @field_validator('topic') + @classmethod + def check_topic(cls, topic): + if not isinstance(topic, (str, list)): + raise ValueError('topic must be a string or list of strings') + elif isinstance(topic, list): + t_len = len(topic) + if t_len == 0 or t_len > 1: + raise ValueError('topic list must contain one element') + else: + return topic[0] + else: + return topic
+ +
+
+[docs] + @field_validator('value') + @classmethod + def check_value(cls, value): + if not isinstance(value, dict): + if cls.allow_unsafe: + logger.warning(f'allowing unsafe value {value}') + return {'value': value} + else: + raise ValueError('value must be a dictionary') + # structs must be + # {name : {"value": value, "timestamp": timestamp, "metadata": metadata}} value is mandatory, timestamp is optional, metadata is optional + # can have multiple structs in a dictionary {name1: struct1, name2: struct2} + for key, struct in value.items(): + if not isinstance(struct, dict): + raise ValueError('struct must be a dictionary') + if 'value' not in struct: + raise ValueError('struct must contain a value') + if 'timestamp' in struct: + if not isinstance(struct['timestamp'], (int, float)): + raise ValueError('timestamp must be an int or float') + if 'metadata' in struct: + if not isinstance(struct['metadata'], dict): + raise ValueError('metadata must be a dictionary') + return value
+ +
+ @computed_field + def keys(self) -> list[str]: + return list(self.value.keys()) + + @computed_field + def values(self) -> list[Any]: + return list(self.value.values()) + + @computed_field + def uid(self) -> str: + """return a unique id for the message""" + items = [] + for key, value in self.value.items(): + value_items = frozenset((k, str(v)) for k, v in value.items()) + items.append((key, value_items)) + + return hashlib.md5(str(frozenset(items)).encode()).hexdigest() + + + def __str__(self): + return f'Message(topic={self.topic}, source={self.source}, value={self.value}, timestamp={self.timestamp})' + + def __repr__(self): + return f'Message(topic={self.topic}, source={self.source}, value={self.value}, timestamp={self.timestamp})' + + def __eq__(self, value): + # value timestamp source and topic must be the same + if ( + self.topic == value.topic + and self.source == value.source + and self.timestamp == value.timestamp + and self.value == value.value + ): + return True + else: + return False
+ +
+ +
+[docs] +class Observer(ABC): +
+[docs] + @abstractmethod + def update(self, message: Message) -> Message: + # all updates should return a message + pass
+
+ +
+ +
+[docs] +class MessageBroker: + def __init__(self): + """initialize the message broker""" + self._observers: Dict[str, list[Observer]] = {} + self._stats = {} + self._stats_cnt = {} + self.queue = [] + self.last_update = time.time() + +
+[docs] + def attach(self, observer: Observer, topic: str | list[str]) -> None: + """add observer to topic""" + logger.debug(f'attaching {observer} to {topic}') + + if isinstance(topic, list): + for t in topic: + if t not in self._observers: + self._observers[t] = [] + self._observers[t].append(observer) + + else: + if topic not in self._observers: + self._observers[topic] = [] + self._observers[topic].append(observer)
+ +
+
+[docs] + def detach(self, observer: Observer, topic: str | list[str]) -> None: + """remove observer from topic, we will probably never use this""" + + if isinstance(topic, list): + for t in topic: + if t in self._observers: + self._observers[t].remove(observer) + else: + self._observers[topic].remove(observer)
+ +
+ # @profileit +
+[docs] + def notify(self, message: Message) -> None: + """notify all observers of a message""" + if message.topic in self._observers: + + # logger.debug(f"notifying observers of {message}") + + for observer in self._observers[message.topic]: + logger.debug(f'notifying {observer}') + start = time.time() + result = observer.update(message) + end = time.time() + + if str(observer) not in self._stats: + self._stats[str(observer)] = 0 + self._stats_cnt[str(observer)] = 0 + self._stats[str(observer)] += (end - start) * 1000 + self._stats_cnt[str(observer)] += 1 + + if result is not None: + # if list of messages + if isinstance(result, list): + for r in result: + self.queue.append(r) + else: + self.queue.append(result) + + + if time.time() - self.last_update > 1: + self.last_update = time.time() + fmt_stats = {k: v / self._stats_cnt[k] for k, v in self._stats.items()} + '\n\t\n' + '\t\n'.join([ + f'{k}: {v:.2f}ms' for k, v in fmt_stats.items() + ]) + # sum all _stats + sum_time = sum([v for v in self._stats.values()]) + cnt = sum([v for v in self._stats_cnt.values()]) + logger.info( + f'real time factor: {sum_time / 1000:.2f} must be less than 1, time spent updating this cycle : {sum_time:.2f}ms' + ) + # print(self._stats) + # print(self._stats_cnt) + self._stats = {} + self._stats_cnt = {} + + else: + logger.error(f'no observers for {message.topic}')
+ +
+
+[docs] + def get_stats(self): + return self._stats
+ +
+
+[docs] + def get_all(self) -> None: + refresh_msg = Message( + topic='get_all', source='clock', value={'dummy': {'value': 1}} + ) + self.notify(refresh_msg) + return None
+ +
+
+[docs] + def parse_queue(self): + queue_snapshot = self.queue.copy() + for message in queue_snapshot: + self.notify(message) + self.queue.remove(message) + logger.debug(f'queue length: {len(self.queue)}')
+
+ +
# logger.debug(f"queue: {self.queue}") + + +
+[docs] +class TransformerObserver(Observer): + def __init__( + self, transformer: BaseTransformer, topic: str, unpack_output: bool = False + ): + """wraps around the transformer.handler method""" + self.transformer = transformer + self.topic = topic + self.unpack_output = unpack_output + +
+[docs] + def update(self, message: Message) -> Message | list[Message]: + for key, value in message.value.items(): + self.transformer.handler(key, value) + + if self.transformer.updated: + values = self.transformer.latest_transformed + message_dict = {} + for key, value in values.items(): + message_dict[key] = {'value': value} + + self.transformer.updated = False + return Message(topic=self.topic, source=str(self), value=message_dict)
+
+ +
+ +
+[docs] +class InterfaceObserver(Observer): + def __init__(self, interface: BaseInterface, topic: str, sanitise: bool = True): + """wraps around the interface.put_many method""" + self.interface: BaseInterface = interface + self.topic: str = topic + self.sanitise = sanitise + self.last_get_all = None + +
+[docs] + def update(self, message: Message) -> Message | list[Message]: + if message.topic == 'get_all': + messages = self.get_all() + # compare to last_get_all if not None + if self.last_get_all is not None: + # compare uid for each message + diff = False + for m in messages: + if m.uid not in [msg.uid for msg in self.last_get_all]: + diff = True + break + # print(self.last_get_all, messages) + if diff: + self.last_get_all = messages + return messages + else: + logger.debug('no diff') + return None + else: + self.last_get_all = messages + return messages + + + + return messages + else: + logger.debug(f'updating {self}') + if os.environ['PUBLISH'] == 'True': + self.interface.put_many(message.value) + else: + logger.warning( + 'PUBLISH is set to False, this will not publish to the interface' + )
+ +
+
+[docs] + def get(self, message: Message) -> list[Message]: + """get a single variable from the interface""" + messages = [] + for key in message.keys: + key, value = self.interface.get(key) + messages.append( + Message(topic=self.topic, source=str(self), value={key: value}) + ) + return messages
+ +
+
+[docs] + def get_all(self) -> list[Message]: + """get all variables from the interface based on internal variable list""" + messages = [] + output_dict = {} + + self.interface.get_many(self.interface.variable_list) + # print(f"values: {values}") + for key in self.interface.variable_list: + key, value = self.interface.get(key) + if value is not None: + output_dict[key] = value + + messages.append(Message(topic=self.topic, source=str(self), value=output_dict)) + return messages
+ +
+ # if self.last_get_all is not None: + # diff = DeepDiff(self.last_get_all, output_dict) + # self.last_get_all = output_dict + # if diff: + # messages.append( + # Message(topic=self.topic, source=str(self), value=output_dict) + # ) + # else: + # logger.debug("no diff") + # else: + # self.last_get_all = output_dict + # messages.append( + # Message(topic=self.topic, source=str(self), value=output_dict) + # ) + # return messages + +
+[docs] + def get_many(self, message: Message) -> list[Message]: + """get many variables from the interface""" + keys, values = self.interface.get_many(message.value) + + messages = [] + for key, value in values.items(): + messages.append( + Message(topic=self.topic, source=str(self), value={key: value}) + ) + return messages
+ +
+
+[docs] + def put(self, message: Message) -> None: + """put a single variable into the interface""" + if not isinstance(message.value, dict): + raise ValueError('message value must be a dictionary') + + for key, value in zip(message.keys, message.values): + self.interface.put(key, value)
+ +
+
+[docs] + def put_many(self, message: Message) -> None: + """put many variables into the interface""" + if not isinstance(message.value, dict): + raise ValueError('message value must be a dictionary') + self.interface.put_many(message.value)
+
+ +
+ +
+[docs] +class MockModel: + def __init__(self): + """placeholder for model""" + +
+[docs] + def evaluate(self, value): + """placeholder for model prediction""" + return {'not_initialized': {'value': -99999999999}}
+
+ +
+ +
+[docs] +class ModelObserver(Observer): + def __init__( + self, + model=None, + config=None, + topic: str = 'model', + unpack_input: bool = True, + pack_output: bool = True, + ): + """wraps around the model.predict method""" + self.model = model + self.topic = topic + self.config = config + self.unpack_input = unpack_input + self.pack_output = pack_output + + if self.model is None and self.config is not None: + self.model = self.__get_model() + if not hasattr(self.model, 'evaluate'): + raise ValueError('model must have a .evaluate() method') + elif self.model is not None: + self.model = model + else: + raise ValueError('model must be provided or a config to load a model') + + def __get_model(self): + """load the model from the config""" + if self.config['type'] == 'mock': + return MockModel() + if self.config['type'] == 'MlflowModelGetter': + model_getter = registered_model_getters['mlflow']( + self.config['args'] + ) # legacy name well make it consistent across the board in the future + model = model_getter.get_model() + # check model is not None + if model is None: + raise ValueError('model is None') + return model + + else: + raise ValueError(f'model type not recognised: {self.config["type"]}') + +
+[docs] + def update(self, message: Message) -> list[Message]: + messages = [] + logger.debug(f'updating {self}') + + if self.unpack_input: + # logger.debug(f"unpacking input: {message.value}") + value = {v: message.value[v]['value'] for v in message.value} + else: + # logger.debug(f"not unpacking input passign raw: {message.value}") + value = message.value + pred = self.model.evaluate(value) + output = {} + + if self.pack_output: + # logger.debug(f"packing output: {pred}") + for key, value in pred.items(): + output[key] = {'value': value} + else: + # logger.debug(f"not packing output passign raw: {pred}") + output = pred + + messages.append(Message(topic=self.topic, source=str(self), value=output)) + + return messages
+
+ +
+ +# class GenericObserver(Observer): +# def __init__(self, callback): +# """wraps around the callback method, a catch all observer""" +# self.callback = callback + +# def update(self, message: Message) -> None: +# self.callback(message) +
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/_sources/api/poly_lithic.pre_install.rst.txt b/docs/build/html/_sources/api/poly_lithic.pre_install.rst.txt new file mode 100644 index 0000000..a91a98f --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.pre_install.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.pre\_install module +================================ + +.. automodule:: poly_lithic.pre_install + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.rst.txt b/docs/build/html/_sources/api/poly_lithic.rst.txt new file mode 100644 index 0000000..0f6359b --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.rst.txt @@ -0,0 +1,27 @@ +poly\_lithic package +==================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.scripts + poly_lithic.src + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.pre_install + +Module contents +--------------- + +.. automodule:: poly_lithic + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.scripts.main.rst.txt b/docs/build/html/_sources/api/poly_lithic.scripts.main.rst.txt new file mode 100644 index 0000000..1949029 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.scripts.main.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.scripts.main module +================================ + +.. automodule:: poly_lithic.scripts.main + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.scripts.rst.txt b/docs/build/html/_sources/api/poly_lithic.scripts.rst.txt new file mode 100644 index 0000000..ab04525 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.scripts.rst.txt @@ -0,0 +1,18 @@ +poly\_lithic.scripts package +============================ + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.scripts.main + +Module contents +--------------- + +.. automodule:: poly_lithic.scripts + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.cli.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.cli.rst.txt new file mode 100644 index 0000000..17c352e --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.cli.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.cli module +=========================== + +.. automodule:: poly_lithic.src.cli + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.config.config_object.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.config.config_object.rst.txt new file mode 100644 index 0000000..d9e1a13 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.config.config_object.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.config.config\_object module +============================================= + +.. automodule:: poly_lithic.src.config.config_object + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.config.parser.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.config.parser.rst.txt new file mode 100644 index 0000000..1d49c11 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.config.parser.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.config.parser module +===================================== + +.. automodule:: poly_lithic.src.config.parser + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.config.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.config.rst.txt new file mode 100644 index 0000000..b7e10e0 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.config.rst.txt @@ -0,0 +1,19 @@ +poly\_lithic.src.config package +=============================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.config.config_object + poly_lithic.src.config.parser + +Module contents +--------------- + +.. automodule:: poly_lithic.src.config + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.interfaces.BaseInterface.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.interfaces.BaseInterface.rst.txt new file mode 100644 index 0000000..089a0ca --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.interfaces.BaseInterface.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.interfaces.BaseInterface module +================================================ + +.. automodule:: poly_lithic.src.interfaces.BaseInterface + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.interfaces.file_interface.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.interfaces.file_interface.rst.txt new file mode 100644 index 0000000..4078678 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.interfaces.file_interface.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.interfaces.file\_interface module +================================================== + +.. automodule:: poly_lithic.src.interfaces.file_interface + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.interfaces.k2eg_interface.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.interfaces.k2eg_interface.rst.txt new file mode 100644 index 0000000..d6c7672 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.interfaces.k2eg_interface.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.interfaces.k2eg\_interface module +================================================== + +.. automodule:: poly_lithic.src.interfaces.k2eg_interface + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.interfaces.p4p_interface.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.interfaces.p4p_interface.rst.txt new file mode 100644 index 0000000..698314b --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.interfaces.p4p_interface.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.interfaces.p4p\_interface module +================================================= + +.. automodule:: poly_lithic.src.interfaces.p4p_interface + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.interfaces.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.interfaces.rst.txt new file mode 100644 index 0000000..5d0cad2 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.interfaces.rst.txt @@ -0,0 +1,21 @@ +poly\_lithic.src.interfaces package +=================================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.interfaces.BaseInterface + poly_lithic.src.interfaces.file_interface + poly_lithic.src.interfaces.k2eg_interface + poly_lithic.src.interfaces.p4p_interface + +Module contents +--------------- + +.. automodule:: poly_lithic.src.interfaces + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.logging_utils.make_logger.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.logging_utils.make_logger.rst.txt new file mode 100644 index 0000000..38da117 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.logging_utils.make_logger.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.logging\_utils.make\_logger module +=================================================== + +.. automodule:: poly_lithic.src.logging_utils.make_logger + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.logging_utils.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.logging_utils.rst.txt new file mode 100644 index 0000000..88c003b --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.logging_utils.rst.txt @@ -0,0 +1,18 @@ +poly\_lithic.src.logging\_utils package +======================================= + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.logging_utils.make_logger + +Module contents +--------------- + +.. automodule:: poly_lithic.src.logging_utils + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.model_utils.LocalModelGetter.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.model_utils.LocalModelGetter.rst.txt new file mode 100644 index 0000000..54a42fc --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.model_utils.LocalModelGetter.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.model\_utils.LocalModelGetter module +===================================================== + +.. automodule:: poly_lithic.src.model_utils.LocalModelGetter + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.model_utils.MlflowModelGetter.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.model_utils.MlflowModelGetter.rst.txt new file mode 100644 index 0000000..fdea057 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.model_utils.MlflowModelGetter.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.model\_utils.MlflowModelGetter module +====================================================== + +.. automodule:: poly_lithic.src.model_utils.MlflowModelGetter + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.model_utils.ModelGetterBase.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.model_utils.ModelGetterBase.rst.txt new file mode 100644 index 0000000..bb4ffa8 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.model_utils.ModelGetterBase.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.model\_utils.ModelGetterBase module +==================================================== + +.. automodule:: poly_lithic.src.model_utils.ModelGetterBase + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.model_utils.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.model_utils.rst.txt new file mode 100644 index 0000000..227144a --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.model_utils.rst.txt @@ -0,0 +1,20 @@ +poly\_lithic.src.model\_utils package +===================================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.model_utils.LocalModelGetter + poly_lithic.src.model_utils.MlflowModelGetter + poly_lithic.src.model_utils.ModelGetterBase + +Module contents +--------------- + +.. automodule:: poly_lithic.src.model_utils + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.rst.txt new file mode 100644 index 0000000..4090afb --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.rst.txt @@ -0,0 +1,31 @@ +poly\_lithic.src package +======================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.config + poly_lithic.src.interfaces + poly_lithic.src.logging_utils + poly_lithic.src.model_utils + poly_lithic.src.transformers + poly_lithic.src.utils + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.cli + +Module contents +--------------- + +.. automodule:: poly_lithic.src + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.transformers.BaseTransformer.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.transformers.BaseTransformer.rst.txt new file mode 100644 index 0000000..56f9274 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.transformers.BaseTransformer.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.transformers.BaseTransformer module +==================================================== + +.. automodule:: poly_lithic.src.transformers.BaseTransformer + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.transformers.BaseTransformers.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.transformers.BaseTransformers.rst.txt new file mode 100644 index 0000000..6f962d2 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.transformers.BaseTransformers.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.transformers.BaseTransformers module +===================================================== + +.. automodule:: poly_lithic.src.transformers.BaseTransformers + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.transformers.CompoundTransformer.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.transformers.CompoundTransformer.rst.txt new file mode 100644 index 0000000..124879f --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.transformers.CompoundTransformer.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.transformers.CompoundTransformer module +======================================================== + +.. automodule:: poly_lithic.src.transformers.CompoundTransformer + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.transformers.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.transformers.rst.txt new file mode 100644 index 0000000..3f5b64d --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.transformers.rst.txt @@ -0,0 +1,20 @@ +poly\_lithic.src.transformers package +===================================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.transformers.BaseTransformer + poly_lithic.src.transformers.BaseTransformers + poly_lithic.src.transformers.CompoundTransformer + +Module contents +--------------- + +.. automodule:: poly_lithic.src.transformers + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.utils.builder.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.utils.builder.rst.txt new file mode 100644 index 0000000..7d5746a --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.utils.builder.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.utils.builder module +===================================== + +.. automodule:: poly_lithic.src.utils.builder + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.utils.lazyInterfaceLoader.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.utils.lazyInterfaceLoader.rst.txt new file mode 100644 index 0000000..0371afe --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.utils.lazyInterfaceLoader.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.utils.lazyInterfaceLoader module +================================================= + +.. automodule:: poly_lithic.src.utils.lazyInterfaceLoader + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.utils.messaging.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.utils.messaging.rst.txt new file mode 100644 index 0000000..0f60093 --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.utils.messaging.rst.txt @@ -0,0 +1,7 @@ +poly\_lithic.src.utils.messaging module +======================================= + +.. automodule:: poly_lithic.src.utils.messaging + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/api/poly_lithic.src.utils.rst.txt b/docs/build/html/_sources/api/poly_lithic.src.utils.rst.txt new file mode 100644 index 0000000..becb5ca --- /dev/null +++ b/docs/build/html/_sources/api/poly_lithic.src.utils.rst.txt @@ -0,0 +1,20 @@ +poly\_lithic.src.utils package +============================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.utils.builder + poly_lithic.src.utils.lazyInterfaceLoader + poly_lithic.src.utils.messaging + +Module contents +--------------- + +.. automodule:: poly_lithic.src.utils + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/build/html/_sources/config.rst.txt b/docs/build/html/_sources/config.rst.txt new file mode 100644 index 0000000..601fb1b --- /dev/null +++ b/docs/build/html/_sources/config.rst.txt @@ -0,0 +1,8 @@ +Configuration Guide +=================== + +This page explains how to configure your project. + +Overview +-------- +Configuration is handled through YAML files... \ No newline at end of file diff --git a/docs/build/html/_sources/index.rst.txt b/docs/build/html/_sources/index.rst.txt new file mode 100644 index 0000000..7932a7a --- /dev/null +++ b/docs/build/html/_sources/index.rst.txt @@ -0,0 +1,20 @@ +Poly-Lithic +========================= +Under Construction 🚧 + +Welcome to the Poly-Lithic documentation! + + +.. toctree:: + :maxdepth: 2 + :caption: User Guide: + + transformers + interfaces + config + +.. toctree:: + :maxdepth: 1 + :caption: API Reference: + + api/poly_lithic.src \ No newline at end of file diff --git a/docs/build/html/_sources/interfaces.rst.txt b/docs/build/html/_sources/interfaces.rst.txt new file mode 100644 index 0000000..0bd3b1f --- /dev/null +++ b/docs/build/html/_sources/interfaces.rst.txt @@ -0,0 +1,17 @@ +Interfaces Guide +================ + +This page explains how to use interfaces in your project. + +Overview +-------- +Interfaces are used to connect to different data sources... + +Available Interfaces +-------------------- +- :class:`~poly_lithic.src.interfaces.BaseInterface.BaseInterface` +- :class:`~poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface` + +Examples +-------- +Here are some examples of how to use interfaces... \ No newline at end of file diff --git a/docs/build/html/_sources/transformers.rst.txt b/docs/build/html/_sources/transformers.rst.txt new file mode 100644 index 0000000..9e0bf81 --- /dev/null +++ b/docs/build/html/_sources/transformers.rst.txt @@ -0,0 +1,19 @@ +Transformers Guide +================== + +This page explains how to use transformers in your project. + +Overview +-------- +Transformers are used to transform data from one format to another... + +Available Transformers +---------------------- +- :class:`~poly_lithic.src.transformers.BaseTransformers.SimpleTransformer` +- :class:`~poly_lithic.src.transformers.BaseTransformers.CAImageTransfomer` +- :class:`~poly_lithic.src.transformers.BaseTransformers.PassThroughTransformer` +- :class:`~poly_lithic.src.transformers.CompoundTransformer.CompoundTransformer` + +Examples +-------- +Here are some examples of how to use transformers... \ No newline at end of file diff --git a/docs/build/html/_static/0fc70aa4dfe4d16d7073.woff b/docs/build/html/_static/0fc70aa4dfe4d16d7073.woff new file mode 100644 index 0000000..2a9fff0 Binary files /dev/null and b/docs/build/html/_static/0fc70aa4dfe4d16d7073.woff differ diff --git a/docs/build/html/_static/583e3f428bf2362b546d.woff b/docs/build/html/_static/583e3f428bf2362b546d.woff new file mode 100644 index 0000000..e2e3d20 Binary files /dev/null and b/docs/build/html/_static/583e3f428bf2362b546d.woff differ diff --git a/docs/build/html/_static/5be6ec379613f10aea3f.woff b/docs/build/html/_static/5be6ec379613f10aea3f.woff new file mode 100644 index 0000000..dc524f7 Binary files /dev/null and b/docs/build/html/_static/5be6ec379613f10aea3f.woff differ diff --git a/docs/build/html/_static/76c1862325ea6f70eeff.woff2 b/docs/build/html/_static/76c1862325ea6f70eeff.woff2 new file mode 100644 index 0000000..3d3c5d7 Binary files /dev/null and b/docs/build/html/_static/76c1862325ea6f70eeff.woff2 differ diff --git a/docs/build/html/_static/83710c128240451d95af.woff b/docs/build/html/_static/83710c128240451d95af.woff new file mode 100644 index 0000000..f89b15f Binary files /dev/null and b/docs/build/html/_static/83710c128240451d95af.woff differ diff --git a/docs/build/html/_static/a63d39a1c104a2b3e87e.woff2 b/docs/build/html/_static/a63d39a1c104a2b3e87e.woff2 new file mode 100644 index 0000000..3149aac Binary files /dev/null and b/docs/build/html/_static/a63d39a1c104a2b3e87e.woff2 differ diff --git a/docs/build/html/_static/awesome-docsearch.css b/docs/build/html/_static/awesome-docsearch.css new file mode 100644 index 0000000..3332a0a --- /dev/null +++ b/docs/build/html/_static/awesome-docsearch.css @@ -0,0 +1 @@ +:root{--docsearch-primary-color:hsl(var(--primary));--docsearch-muted-color:hsl(var(--muted-foreground));--docsearch-key-gradient:transparent;--docsearch-key-shadow:transparent;--docsearch-text-color:hsl(var(--popover-foreground));--docsearch-modal-width:760px;--docsearch-modal-background:hsl(var(--popover));--docsearch-footer-background:hsl(var(--popover));--docsearch-searchbox-focus-background:hsl(var(--popover));--docsearch-container-background:hsl(var(--background)/0.8);--docsearch-spacing:0.5rem;--docsearch-hit-active-color:hsl(var(--accent-foreground));--docsearch-hit-background:transparent;--docsearch-searchbox-shadow:none;--docsearch-hit-shadow:none;--docsearch-modal-shadow:none;--docsearch-footer-shadow:none}.DocSearch-Button{background-color:initial;border-color:hsl(var(--input));border-radius:.5em;border-style:solid;border-width:1px;display:flex;font-size:.875rem;line-height:1.25rem;width:90%;--tw-ring-offset-color:hsl(var(--background));transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.DocSearch-Button:hover{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.DocSearch-Button:focus,.DocSearch-Button:hover{background-color:hsl(var(--accent));color:hsl(var(--accent-foreground))}.DocSearch-Button:focus-visible{outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:hsl(var(--ring));--tw-ring-offset-width:2px}.DocSearch-Button-Placeholder{display:block;font-size:.875rem;font-weight:500;line-height:1.25rem}.DocSearch-Button-Key{background-color:hsl(var(--muted));border-color:hsl(var(--border));border-radius:.25rem;border-style:solid;border-width:1px;color:hsl(var(--muted-foreground));font-size:12px}.DocSearch-Container{position:fixed;--tw-backdrop-blur:blur(4px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.DocSearch-Modal{border-color:hsl(var(--border));border-radius:var(--radius);border-width:1px}.DocSearch-SearchBar{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom-width:1px;border-color:hsl(var(--input));border-top-left-radius:var(--radius);border-top-right-radius:var(--radius);padding:0}.DocSearch-Form{border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.DocSearch-Cancel{color:hsl(var(--muted-foreground));font-size:.875rem;line-height:1.25rem;padding-left:.5rem;padding-right:.5rem}.DocSearch-MagnifierLabel,.DocSearch-Search-Icon{stroke-width:2;opacity:.5}.DocSearch-Hit-source{color:hsl(var(--muted-foreground))}.DocSearch-Hit,.DocSearch-Hit a{border-radius:calc(var(--radius) - 4px)}.DocSearch-Hit a:focus-visible{outline-offset:-2px}.DocSearch-Hit[aria-selected=true] a{background-color:hsl(var(--accent));color:hsl(var(--accent-foreground))}.DocSearch-Commands{display:none}.DocSearch-Footer{border-color:hsl(var(--border));border-top-width:1px} diff --git a/docs/build/html/_static/awesome-docsearch.js b/docs/build/html/_static/awesome-docsearch.js new file mode 100644 index 0000000..e69de29 diff --git a/docs/build/html/_static/awesome-sphinx-design.css b/docs/build/html/_static/awesome-sphinx-design.css new file mode 100644 index 0000000..a13c5d8 --- /dev/null +++ b/docs/build/html/_static/awesome-sphinx-design.css @@ -0,0 +1 @@ +:root{--sd-color-tabs-label-active:hsl(var(--foreground));--sd-color-tabs-underline-active:hsl(var(--accent-foreground));--sd-color-tabs-label-hover:hsl(var(--accent-foreground));--sd-color-tabs-overline:hsl(var(--border));--sd-color-tabs-underline:hsl(var(--border))}.sd-card{background-color:hsl(var(--card));border-color:hsl(var(--border));border-radius:var(--radius);border-width:1px;color:hsl(var(--card-foreground));margin-top:1.5rem}.sd-container-fluid{margin-bottom:1.5rem;margin-top:1.5rem}.sd-card-title{font-weight:600!important}.sd-summary-title{color:hsl(var(--muted-foreground));font-weight:500!important}.sd-card-footer,.sd-card-header{font-size:.875rem;line-height:1.25rem}.sd-tab-set{margin-top:1.5rem}.sd-tab-content>p{margin-bottom:1.5rem}.sd-tab-content pre:first-of-type{margin-top:0}.sd-tab-set>label{font-weight:500;letter-spacing:.05em}details.sd-dropdown,details.sd-dropdown:not([open])>.sd-card-header{border-color:hsl(var(--border))}details.sd-dropdown summary:focus{outline-style:solid}.sd-cards-carousel{overflow-x:auto}.sd-shadow-sm{--tw-shadow:0 0 #0000!important;--tw-shadow-colored:0 0 #0000!important;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)!important} diff --git a/docs/build/html/_static/awesome-sphinx-design.js b/docs/build/html/_static/awesome-sphinx-design.js new file mode 100644 index 0000000..e69de29 diff --git a/docs/build/html/_static/b659956119f91f2342bc.woff2 b/docs/build/html/_static/b659956119f91f2342bc.woff2 new file mode 100644 index 0000000..69e029c Binary files /dev/null and b/docs/build/html/_static/b659956119f91f2342bc.woff2 differ diff --git a/docs/build/html/_static/basic.css b/docs/build/html/_static/basic.css new file mode 100644 index 0000000..4738b2e --- /dev/null +++ b/docs/build/html/_static/basic.css @@ -0,0 +1,906 @@ +/* + * Sphinx stylesheet -- basic theme. + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin-top: 10px; +} + +ul.search li { + padding: 5px 0; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/build/html/_static/bb50084be2b43ba7b98c.woff2 b/docs/build/html/_static/bb50084be2b43ba7b98c.woff2 new file mode 100644 index 0000000..be878e6 Binary files /dev/null and b/docs/build/html/_static/bb50084be2b43ba7b98c.woff2 differ diff --git a/docs/build/html/_static/ce1e40901d7a0d88d483.woff2 b/docs/build/html/_static/ce1e40901d7a0d88d483.woff2 new file mode 100644 index 0000000..3a4e333 Binary files /dev/null and b/docs/build/html/_static/ce1e40901d7a0d88d483.woff2 differ diff --git a/docs/build/html/_static/d04352f240062b100fba.woff2 b/docs/build/html/_static/d04352f240062b100fba.woff2 new file mode 100644 index 0000000..5858873 Binary files /dev/null and b/docs/build/html/_static/d04352f240062b100fba.woff2 differ diff --git a/docs/build/html/_static/doctools.js b/docs/build/html/_static/doctools.js new file mode 100644 index 0000000..0398ebb --- /dev/null +++ b/docs/build/html/_static/doctools.js @@ -0,0 +1,149 @@ +/* + * Base JavaScript utilities for all Sphinx HTML documentation. + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/docs/build/html/_static/documentation_options.js b/docs/build/html/_static/documentation_options.js new file mode 100644 index 0000000..7e4c114 --- /dev/null +++ b/docs/build/html/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/docs/build/html/_static/f1cdf5c21de970ee0592.woff b/docs/build/html/_static/f1cdf5c21de970ee0592.woff new file mode 100644 index 0000000..4342415 Binary files /dev/null and b/docs/build/html/_static/f1cdf5c21de970ee0592.woff differ diff --git a/docs/build/html/_static/fd994e8d90d9cab651b0.woff b/docs/build/html/_static/fd994e8d90d9cab651b0.woff new file mode 100644 index 0000000..3c971ff Binary files /dev/null and b/docs/build/html/_static/fd994e8d90d9cab651b0.woff differ diff --git a/docs/build/html/_static/file.png b/docs/build/html/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/docs/build/html/_static/file.png differ diff --git a/docs/build/html/_static/language_data.js b/docs/build/html/_static/language_data.js new file mode 100644 index 0000000..c7fe6c6 --- /dev/null +++ b/docs/build/html/_static/language_data.js @@ -0,0 +1,192 @@ +/* + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/docs/build/html/_static/minus.png b/docs/build/html/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/docs/build/html/_static/minus.png differ diff --git a/docs/build/html/_static/plus.png b/docs/build/html/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/docs/build/html/_static/plus.png differ diff --git a/docs/build/html/_static/pygments.css b/docs/build/html/_static/pygments.css new file mode 100644 index 0000000..ffe220c --- /dev/null +++ b/docs/build/html/_static/pygments.css @@ -0,0 +1,45 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #ffffff; } +.highlight .c { font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #F00 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .ch { font-style: italic } /* Comment.Hashbang */ +.highlight .cm { font-style: italic } /* Comment.Multiline */ +.highlight .cpf { font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { font-style: italic } /* Comment.Single */ +.highlight .cs { font-style: italic } /* Comment.Special */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gh { font-weight: bold } /* Generic.Heading */ +.highlight .gp { font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { font-weight: bold } /* Generic.Subheading */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { font-weight: bold } /* Keyword.Namespace */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .s { font-style: italic } /* Literal.String */ +.highlight .nc { font-weight: bold } /* Name.Class */ +.highlight .ni { font-weight: bold } /* Name.Entity */ +.highlight .ne { font-weight: bold } /* Name.Exception */ +.highlight .nn { font-weight: bold } /* Name.Namespace */ +.highlight .nt { font-weight: bold } /* Name.Tag */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .sa { font-style: italic } /* Literal.String.Affix */ +.highlight .sb { font-style: italic } /* Literal.String.Backtick */ +.highlight .sc { font-style: italic } /* Literal.String.Char */ +.highlight .dl { font-style: italic } /* Literal.String.Delimiter */ +.highlight .sd { font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { font-style: italic } /* Literal.String.Double */ +.highlight .se { font-weight: bold; font-style: italic } /* Literal.String.Escape */ +.highlight .sh { font-style: italic } /* Literal.String.Heredoc */ +.highlight .si { font-weight: bold; font-style: italic } /* Literal.String.Interpol */ +.highlight .sx { font-style: italic } /* Literal.String.Other */ +.highlight .sr { font-style: italic } /* Literal.String.Regex */ +.highlight .s1 { font-style: italic } /* Literal.String.Single */ +.highlight .ss { font-style: italic } /* Literal.String.Symbol */ \ No newline at end of file diff --git a/docs/build/html/_static/searchtools.js b/docs/build/html/_static/searchtools.js new file mode 100644 index 0000000..91f4be5 --- /dev/null +++ b/docs/build/html/_static/searchtools.js @@ -0,0 +1,635 @@ +/* + * Sphinx JavaScript utilities for the full-text search. + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename, kind] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +// Global search result kind enum, used by themes to style search results. +class SearchResultKind { + static get index() { return "index"; } + static get object() { return "object"; } + static get text() { return "text"; } + static get title() { return "title"; } +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename, kind] = item; + + let listItem = document.createElement("li"); + // Add a class representing the item's type: + // can be used by a theme's CSS selector for styling + // See SearchResultKind for the class names. + listItem.classList.add(`kind-${kind}`); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = Documentation.ngettext( + "Search finished, found one page matching the search query.", + "Search finished, found ${resultCount} pages matching the search query.", + resultCount, + ).replace('${resultCount}', resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename, kind]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.setAttribute("role", "list"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename, kind]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + const score = Math.round(Scorer.title * queryLower.length / title.length); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + filenames[file], + SearchResultKind.title, + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round(100 * queryLower.length / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + SearchResultKind.index, + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); + const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + SearchResultKind.object, + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + // find documents, if any, containing the query word in their text/title term indices + // use Object.hasOwnProperty to avoid mismatching against prototype properties + const arr = [ + { files: terms.hasOwnProperty(word) ? terms[word] : undefined, score: Scorer.term }, + { files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, new Map()); + const fileScores = scoreMap.get(file); + fileScores.set(word, record.score); + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w))); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + SearchResultKind.text, + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/docs/build/html/_static/sphinx_highlight.js b/docs/build/html/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/docs/build/html/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/docs/build/html/_static/theme.css b/docs/build/html/_static/theme.css new file mode 100644 index 0000000..0cb8761 --- /dev/null +++ b/docs/build/html/_static/theme.css @@ -0,0 +1,9 @@ +@font-face{font-display:swap;font-family:JetBrains Mono;font-style:italic;font-weight:400;src:url(76c1862325ea6f70eeff.woff2) format("woff2"),url(fd994e8d90d9cab651b0.woff) format("woff")} +@font-face{font-display:swap;font-family:JetBrains Mono;font-style:normal;font-weight:400;src:url(d04352f240062b100fba.woff2) format("woff2"),url(0fc70aa4dfe4d16d7073.woff) format("woff")} +@font-face{font-display:swap;font-family:JetBrains Mono;font-style:italic;font-weight:500;src:url(a63d39a1c104a2b3e87e.woff2) format("woff2"),url(83710c128240451d95af.woff) format("woff")} +@font-face{font-display:swap;font-family:JetBrains Mono;font-style:normal;font-weight:500;src:url(bb50084be2b43ba7b98c.woff2) format("woff2"),url(f1cdf5c21de970ee0592.woff) format("woff")} +@font-face{font-display:swap;font-family:JetBrains Mono;font-style:italic;font-weight:700;src:url(b659956119f91f2342bc.woff2) format("woff2"),url(583e3f428bf2362b546d.woff) format("woff")} +@font-face{font-display:swap;font-family:JetBrains Mono;font-style:normal;font-weight:700;src:url(ce1e40901d7a0d88d483.woff2) format("woff2"),url(5be6ec379613f10aea3f.woff) format("woff")} +*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: } + +/*! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:JetBrains\ Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background:0 0% 100%;--foreground:222.2 47.4% 11.2%;--muted:210 40% 96.1%;--muted-foreground:215.4 16.3% 46.9%;--popover:0 0% 100%;--popover-foreground:222.2 47.4% 11.2%;--border:214.3 31.8% 91.4%;--input:214.3 31.8% 91.4%;--card:0 0% 100%;--card-foreground:222.2 47.4% 11.2%;--primary:222.2 47.4% 11.2%;--primary-foreground:210 40% 98%;--secondary:210 40% 96.1%;--secondary-foreground:222.2 47.4% 11.2%;--accent:210 40% 96.1%;--accent-foreground:222.2 47.4% 11.2%;--destructive:0 100% 50%;--destructive-foreground:210 40% 98%;--ring:215 20.2% 65.1%;--radius:0.5rem}.dark{--background:224 71% 4%;--foreground:213 31% 91%;--muted:223 47% 11%;--muted-foreground:215.4 16.3% 56.9%;--accent:216 34% 17%;--accent-foreground:210 40% 98%;--popover:224 71% 4%;--popover-foreground:215 20.2% 65.1%;--border:216 34% 17%;--input:216 34% 17%;--card:224 71% 4%;--card-foreground:213 31% 91%;--primary:210 40% 98%;--primary-foreground:222.2 47.4% 1.2%;--secondary:222.2 47.4% 11.2%;--secondary-foreground:210 40% 98%;--destructive:0 63% 31%;--destructive-foreground:210 40% 98%;--ring:216 34% 17%;--radius:0.5rem}.container{margin-left:auto;margin-right:auto;padding-left:2rem;padding-right:2rem;width:100%}@media (min-width:1400px){.container{max-width:1400px}}#content svg{display:inline}#content hr{border-color:hsl(var(--border));margin-bottom:1rem;margin-top:1rem}@media (min-width:768px){#content hr{margin-bottom:1.5rem;margin-top:1.5rem}}#content h1{font-size:2.25rem;font-weight:700;line-height:2.5rem;margin-bottom:.5rem}#content h2{border-bottom-width:1px;border-color:hsl(var(--border));font-size:1.875rem;font-weight:600;line-height:2.25rem;margin-top:3rem;padding-bottom:.5rem}#content h3{font-size:1.5rem;font-weight:600;line-height:2rem;margin-top:2rem}#content .rubric,#content h4{font-size:1.25rem;font-weight:600;line-height:1.75rem;margin-top:2rem}#content section{scroll-margin:5rem}#content section>p{line-height:1.75rem;margin-top:1.5rem}#content section>p:first-child{margin-top:0}#content section>p.lead{color:hsl(var(--muted-foreground));font-size:1.125rem;line-height:1.75rem}#content .centered{text-align:center}#content a.viewcode-back{color:hsl(var(--muted-foreground))!important;position:absolute;right:0}#content a:not(.toc-backref){color:hsl(var(--primary));font-weight:500;text-decoration-line:underline;text-decoration-thickness:from-font;text-underline-offset:4px}#content ul:not(.search){list-style-type:disc;margin-left:1.5rem;margin-top:1.5rem}#content ul:not(.search) p,#content ul:not(.search)>li{margin-top:1.5rem}#content ul:not(.search) ul{margin-top:0}#content ol{list-style-type:decimal;margin-left:1.5rem;margin-top:1.5rem}#content ol ::marker{font-weight:500}#content ol::marker{font-weight:500}#content ol p,#content ol>li{margin-top:1.5rem}#content ol ol{margin-top:0}#content dl{margin-top:1.5rem}#content dl dt:not(.sig){font-weight:500;margin-top:1.5rem}#content dl dt:not(.sig):first-child{margin-bottom:0;margin-top:0}#content dl dd{margin-left:1.5rem}#content dl p{margin-bottom:.5rem;margin-top:.5rem}#content .align-center{margin-left:auto;margin-right:auto;text-align:center}#content .align-right{margin-left:auto;text-align:right}#content img{margin-top:1.5rem}#content figure img{display:inline-block}#content figcaption{color:hsl(var(--muted-foreground));font-size:.875rem;line-height:1.25rem;margin-bottom:3rem}#content figcaption>*{margin-top:1rem}blockquote{border-left-width:2px;font-style:italic;margin-bottom:1.5rem;margin-top:1.5rem;padding-left:1.5rem}blockquote .attribution{font-style:normal;margin-top:.5rem}table{font-size:.875rem;line-height:1.25rem;margin-bottom:1.5rem;margin-top:1.5rem;width:100%}table caption{color:hsl(var(--muted-foreground));margin-bottom:1.5rem;text-align:left}table thead{border-bottom-width:1px;border-color:hsl(var(--border))}table th{font-weight:500;padding-bottom:.5rem;padding-left:.5rem;text-align:left}table th:first-child{padding-left:0}table th:is(.dark *){font-weight:600}table tbody tr{border-bottom-width:1px;border-color:hsl(var(--border))}table tbody td{padding:.5rem}table tbody td:first-child{padding-left:0}.footnote>.label{float:left;padding-right:.5rem}.footnote>:not(.label){margin-bottom:1.5rem;margin-left:2rem;margin-top:1.5rem}.footnote .footnote-reference,.footnote [role=doc-backlink]{text-decoration-line:none!important}.admonition{background-color:hsl(var(--background));border-color:hsl(var(--border));border-radius:var(--radius);border-width:1px;color:hsl(var(--foreground));font-size:.875rem;line-height:1.25rem;margin-bottom:1.5rem;margin-top:1.5rem;padding:1rem}.admonition p:not(.admonition-title){margin-top:.5rem}.admonition .admonition-title{margin-top:0!important}.admonition-title{font-weight:500}.admonition-title:is(.dark *){font-weight:600;letter-spacing:.025em}.note{--tw-border-opacity:1;border-color:rgb(2 132 199/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(240 249 255/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(12 74 110/var(--tw-text-opacity))}.note:is(.dark *){background-color:rgba(96,165,250,.15);--tw-text-opacity:1;color:rgb(224 242 254/var(--tw-text-opacity))}.hint,.tip{--tw-border-opacity:1;border-color:rgb(22 163 74/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity))}.hint:is(.dark *),.tip:is(.dark *){background-color:rgba(74,222,128,.15);--tw-text-opacity:1;color:rgb(220 252 231/var(--tw-text-opacity))}.danger,.error{--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity))}.danger:is(.dark *),.error:is(.dark *){background-color:hsla(0,91%,71%,.15);--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity))}.attention,.caution,.important,.warning{--tw-border-opacity:1;border-color:rgb(202 138 4/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(113 63 18/var(--tw-text-opacity))}.attention:is(.dark *),.caution:is(.dark *),.important:is(.dark *),.warning:is(.dark *){background-color:rgba(250,204,21,.15);--tw-text-opacity:1;color:rgb(254 249 195/var(--tw-text-opacity))}div.versionadded{border-left-width:3px;margin-top:1rem;--tw-border-opacity:1;border-color:rgb(22 163 74/var(--tw-border-opacity));font-size:.875rem;line-height:1.25rem;padding:.25rem 1rem}div.versionadded p{margin-top:0!important}div.versionadded p:last-child{margin-bottom:0!important}div.versionadded .versionmodified{font-weight:500;--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity))}div.versionadded .versionmodified:is(.dark *){letter-spacing:.025em;--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity))}div.versionchanged{border-left-width:3px;margin-top:1rem;--tw-border-opacity:1;border-color:rgb(202 138 4/var(--tw-border-opacity));font-size:.875rem;line-height:1.25rem;padding:.25rem 1rem}div.versionchanged p{margin-top:0!important}div.versionchanged p:last-child{margin-bottom:0!important}div.versionchanged .versionmodified{font-weight:500;--tw-text-opacity:1;color:rgb(113 63 18/var(--tw-text-opacity))}div.versionchanged .versionmodified:is(.dark *){letter-spacing:.025em;--tw-text-opacity:1;color:rgb(234 179 8/var(--tw-text-opacity))}div.deprecated{border-left-width:3px;margin-top:1rem;--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity));font-size:.875rem;line-height:1.25rem;padding:.25rem 1rem}div.deprecated p{margin-top:0!important}div.deprecated p:last-child{margin-bottom:0!important}div.deprecated .versionmodified{font-weight:500;--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity))}div.deprecated .versionmodified:is(.dark *){letter-spacing:.025em;--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity))}.highlight{background-color:initial!important;position:relative}.highlight:hover .copy{opacity:1}.highlight .gp,.highlight-pycon .go,.highlight-python .go{-webkit-user-select:none;-moz-user-select:none;user-select:none}.literal-block-wrapper{border-color:hsl(var(--border));border-radius:var(--radius);border-width:1px;margin-left:0;margin-right:0;margin-top:1.5rem;max-width:none;padding-left:0;padding-right:0}.literal-block-wrapper pre{border-radius:0;border-style:none;margin-top:0}.literal-block-wrapper .code-block-caption{border-bottom-width:1px;border-color:hsl(var(--border));border-top-left-radius:var(--radius);border-top-right-radius:var(--radius);color:hsl(var(--muted-foreground));font-size:.875rem;letter-spacing:.025em;line-height:1.25rem;padding:.5rem 1rem}code{background-color:hsl(var(--muted));border-radius:.25rem;font-family:JetBrains\ Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.875rem;line-height:1.25rem;padding:.2em .3em;position:relative;white-space:nowrap}code .ge,code em{color:hsl(var(--accent-foreground));font-weight:700;letter-spacing:.025em}:where(h1,h2,h3,h4,h5,h6) code{font-size:inherit}pre{border-color:hsl(var(--border));border-radius:var(--radius);border-width:1px;font-size:.875rem;line-height:1.25rem;margin-top:1.5rem;overflow-x:auto;padding-bottom:1rem;padding-top:1rem}pre[data-theme=dark]{background-color:hsl(var(--background))}pre[data-theme=light]{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}pre.literal-block{padding-left:1rem;padding-right:1rem}pre code{background-color:initial;padding:0;white-space:pre}pre code>[id^=line-]{display:block;padding-left:1rem;padding-right:1rem}pre code [id^=line-]:has(.gd),pre code [id^=line-]:has(.gi),pre code [id^=line-]:has(del),pre code [id^=line-]:has(ins),pre code [id^=line-]:has(mark){padding-left:0;padding-right:0}pre code [id^=line-] del,pre code [id^=line-] ins,pre code [id^=line-] mark{display:block;padding-left:1rem;padding-right:1rem;position:relative}pre code [id^=line-] mark{background-color:hsl(var(--muted));color:inherit;--tw-shadow:2px 0 currentColor inset;--tw-shadow-colored:inset 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}pre code [id^=line-] mark:is(.dark *){--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity));--tw-shadow:3px 0 currentColor inset;--tw-shadow-colored:inset 3px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}pre code [id^=line-] ins{background-color:rgba(34,197,94,.3);--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity));text-decoration-line:none}pre code [id^=line-] ins:before{left:2px;position:absolute;--tw-content:"\002b";content:var(--tw-content)}pre code [id^=line-] ins:is(.dark *){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity))}pre code [id^=line-] del{background-color:rgba(239,68,68,.3);--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity));text-decoration-line:none}pre code [id^=line-] del:before{left:2px;position:absolute;--tw-content:"\2212";content:var(--tw-content)}pre code [id^=line-] del:is(.dark *){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity))}pre span.linenos{background-color:initial!important;padding-left:0;padding-right:1rem;-webkit-user-select:none;-moz-user-select:none;user-select:none}.highlight-diff .gi{background-color:rgba(34,197,94,.3);display:inline-block;padding-left:1rem;padding-right:1rem;width:100%;--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity))}.highlight-diff .gi:is(.dark *){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity))}.highlight-diff .gd{background-color:rgba(239,68,68,.3);display:inline-block;padding-left:1rem;padding-right:1rem;width:100%;--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity))}.highlight-diff .gd:is(.dark *){--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity))}.guilabel,.menuselection{border-color:hsl(var(--border));border-radius:calc(var(--radius) - 4px);border-width:1px;color:hsl(var(--accent-foreground));font-weight:500;padding:1px .5rem}#content kbd:not(.compound){background-color:hsl(var(--muted));border-radius:.25rem;border-width:1px;font-size:.875rem;font-weight:500;letter-spacing:.025em;line-height:1.25rem;padding:1px .25rem}.sig{border-color:hsl(var(--border));border-top-width:1px;font-family:JetBrains\ Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-weight:700;padding-top:1.5rem;scroll-margin:5rem}.sig-name{color:hsl(var(--accent-foreground))}em.property{color:hsl(var(--muted-foreground))}.option .sig-prename{font-style:italic}.viewcode-link{color:hsl(var(--muted-foreground));float:right}.option-list kbd{background-color:initial!important;border-style:none!important;font-size:1em!important;font-weight:700!important}dt .classifier{font-style:italic}dt .classifier:before{margin-right:.5rem;--tw-content:":";content:var(--tw-content)}.headerlink{align-items:center;display:inline-flex;margin-left:.25rem;position:relative;vertical-align:middle}.headerlink:after{z-index:1000000;-webkit-font-smoothing:subpixel-antialiased;letter-spacing:normal;text-shadow:none;text-transform:none;word-wrap:break-word;background-color:hsl(var(--muted));border-radius:calc(var(--radius) - 4px);content:attr(data-tooltip);display:none;pointer-events:none;position:absolute;white-space:pre;--tw-bg-opacity:0.75;color:hsl(var(--muted-foreground));font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:.75rem;font-weight:400;line-height:1rem;opacity:0;padding:.25rem;text-align:center;text-decoration-line:none}.headerlink:focus:after,.headerlink:focus:before,.headerlink:hover:after,.headerlink:hover:before{animation-delay:.2s;animation-duration:.4s;animation-fill-mode:forwards;animation-name:tooltip-appear;animation-timing-function:ease-in;display:inline-block;-webkit-text-decoration:none;text-decoration:none}.headerlink:after{margin-top:6px;right:50%;top:100%}.headerlink:before{border-bottom-color:#1a202c;bottom:-7px;margin-right:-6px;right:50%;top:auto}.headerlink:after{margin-right:-16px}.headerlink>*{visibility:hidden;fill:currentColor;color:hsl(var(--muted-foreground))}.headerlink:focus>*{visibility:visible}:is(h1,h2,h3,h4,table,.admonition-title,figure,dt,.code-block-caption):hover .headerlink{visibility:visible}:is(h1,h2,h3,h4,table,.admonition-title,figure,dt,.code-block-caption):hover .headerlink>*{visibility:visible}#left-sidebar .caption{border-radius:calc(var(--radius) - 2px);font-size:.875rem;font-weight:600;line-height:1.25rem;margin-bottom:.25rem;padding:1.5rem .5rem .25rem}#left-sidebar .caption:first-child{padding-top:0}#left-sidebar ul{display:grid;font-size:.875rem;grid-auto-flow:row;grid-auto-rows:max-content;line-height:1.25rem;overflow:hidden;transform:translate3d(var(--tw-translate-x),var(--tw-translate-y),0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));transition-duration:.3s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}@media (prefers-reduced-motion:reduce){#left-sidebar ul{transition-property:none}}#left-sidebar ul ul{margin-left:.75rem;opacity:1;padding:.5rem 0 .5rem .75rem;position:relative;transition-duration:.5s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}#left-sidebar ul ul:before{bottom:.25rem;left:0;position:absolute;top:.25rem;width:1px;--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity));--tw-content:"";content:var(--tw-content)}#left-sidebar ul ul:is(.dark *):before{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(38 38 38/var(--tw-bg-opacity))}#left-sidebar a{align-items:center;border-color:transparent;border-radius:calc(var(--radius) - 2px);border-width:1px;display:flex;padding:.375rem .5rem;width:100%}#left-sidebar a:hover{text-decoration-line:underline}#left-sidebar a:focus-visible{outline-offset:-1px}#left-sidebar a>button{border-radius:.25rem;color:hsl(var(--muted-foreground))}#left-sidebar a>button:hover{background-color:hsl(var(--primary)/.1)}#left-sidebar a>button>svg{transform:translate3d(var(--tw-translate-x),var(--tw-translate-y),0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));transform-origin:center;transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}#left-sidebar a.current{background-color:hsl(var(--accent));border-color:hsl(var(--border));border-width:1px;color:hsl(var(--accent-foreground));font-weight:500}#left-sidebar a.expandable{justify-content:space-between}#left-sidebar a.expandable.expanded>button>svg{--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}#right-sidebar ul{margin:0}#right-sidebar ul li{margin-top:0;padding-top:.5rem}#right-sidebar ul li a{color:hsl(var(--muted-foreground));display:inline-block;text-decoration-line:none;transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}#right-sidebar ul li a:hover{color:hsl(var(--foreground))}#right-sidebar ul li a:focus-visible{outline-offset:-1px}#right-sidebar ul li a[data-current=true]{color:hsl(var(--foreground));font-weight:500}#right-sidebar ul li ul{padding-left:1rem}#right-sidebar ul:not(:last-child){padding-bottom:.5rem}.contents>:not([hidden])~:not([hidden]),.toctree-wrapper>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.contents,.toctree-wrapper{font-size:.875rem;line-height:1.25rem}.contents .caption,.contents .topic-title,.toctree-wrapper .caption,.toctree-wrapper .topic-title{font-weight:500;padding-top:1.5rem}.contents ul,.toctree-wrapper ul{list-style-type:none!important;margin:0!important}.contents ul li a.reference,.toctree-wrapper ul li a.reference{color:hsl(var(--muted-foreground))!important;display:inline-block;font-weight:400!important;text-decoration-line:none!important;transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.contents ul li a.reference:hover,.toctree-wrapper ul li a.reference:hover{color:hsl(var(--foreground))}.contents ul li ul,.toctree-wrapper ul li ul{padding-left:1rem}.contents ul:not(:last-child),.toctree-wrapper ul:not(:last-child){padding-bottom:.5rem}#search-results .search-summary{color:hsl(var(--muted-foreground));font-size:1.25rem;line-height:1.75rem;margin-top:1.5rem}#search-results ul.search,#search-results ul.search li{margin-top:1.5rem}#search-results ul.search .context{color:hsl(var(--muted-foreground));font-size:.875rem;line-height:1.25rem;margin-top:.5rem}.highlighted{background-color:hsl(var(--accent));text-decoration-line:underline;text-decoration-thickness:2px}.highlight-link{border-color:hsl(var(--border));border-radius:var(--radius);border-width:1px;font-size:.875rem;line-height:1.25rem;padding:.5rem 1rem;position:fixed;right:.5rem;top:4rem}.highlight-link:hover{background-color:hsl(var(--accent))}@media (min-width:1024px){.highlight-link{right:4rem}}.tooltipped{position:relative}.tooltipped:after{z-index:1000000;-webkit-font-smoothing:subpixel-antialiased;letter-spacing:normal;text-shadow:none;text-transform:none;word-wrap:break-word;background-color:hsl(var(--muted));border-radius:calc(var(--radius) - 4px);content:attr(data-tooltip);display:none;pointer-events:none;position:absolute;white-space:pre;--tw-bg-opacity:0.75;color:hsl(var(--muted-foreground));font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-size:.75rem;font-weight:400;line-height:1rem;opacity:0;padding:.25rem;text-align:center;text-decoration-line:none}@keyframes tooltip-appear{0%{opacity:0}to{opacity:1}}.tooltipped:focus:after,.tooltipped:focus:before,.tooltipped:hover:after,.tooltipped:hover:before{animation-delay:.2s;animation-duration:.4s;animation-fill-mode:forwards;animation-name:tooltip-appear;animation-timing-function:ease-in;display:inline-block;-webkit-text-decoration:none;text-decoration:none}.tooltipped-no-delay:focus:after,.tooltipped-no-delay:focus:before,.tooltipped-no-delay:hover:after,.tooltipped-no-delay:hover:before{animation-delay:0s}.tooltipped-multiline:focus:after,.tooltipped-multiline:hover:after{display:table-cell}.tooltipped-s:after,.tooltipped-se:after,.tooltipped-sw:after{margin-top:6px;right:50%;top:100%}.tooltipped-s:before,.tooltipped-se:before,.tooltipped-sw:before{border-bottom-color:#1a202c;bottom:-7px;margin-right:-6px;right:50%;top:auto}.tooltipped-se:after{left:50%;margin-left:-16px;right:auto}.tooltipped-sw:after{margin-right:-16px}.tooltipped-n:after,.tooltipped-ne:after,.tooltipped-nw:after{bottom:100%;margin-bottom:6px;right:50%}.tooltipped-n:before,.tooltipped-ne:before,.tooltipped-nw:before{border-top-color:#1a202c;bottom:auto;margin-right:-6px;right:50%;top:-7px}.tooltipped-ne:after{left:50%;margin-left:-16px;right:auto}.tooltipped-nw:after{margin-right:-16px}.tooltipped-n:after,.tooltipped-s:after{transform:translateX(50%)}.tooltipped-w:after{bottom:50%;margin-right:6px;right:100%;transform:translateY(50%)}.tooltipped-w:before{border-left-color:#1a202c;bottom:50%;left:-7px;margin-top:-6px;top:50%}.tooltipped-e:after{bottom:50%;left:100%;margin-left:6px;transform:translateY(50%)}.tooltipped-e:before{border-right-color:#1a202c;bottom:50%;margin-top:-6px;right:-7px;top:50%}.sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border-width:0;white-space:nowrap}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.collapse{visibility:collapse}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-y-0{bottom:0;top:0}.bottom-8{bottom:2rem}.left-0{left:0}.right-1\.5{right:.375rem}.right-4{right:1rem}.right-8{right:2rem}.top-0{top:0}.top-16{top:4rem}.top-2{top:.5rem}.top-4{top:1rem}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.mx-auto{margin-left:auto;margin-right:auto}.my-4{margin-bottom:1rem;margin-top:1rem}.my-6{margin-bottom:1.5rem;margin-top:1.5rem}.my-8{margin-bottom:2rem;margin-top:2rem}.-mt-10{margin-top:-2.5rem}.mb-4{margin-bottom:1rem}.mb-\[2px\]{margin-bottom:2px}.ml-0{margin-left:0}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-4{margin-right:1rem}.mr-6{margin-right:1.5rem}.mr-auto{margin-right:auto}.mt-12{margin-top:3rem}.mt-4{margin-top:1rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.hidden{display:none}.h-10{height:2.5rem}.h-14{height:3.5rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-9{height:2.25rem}.h-\[14px\]{height:14px}.h-\[calc\(100vh-8rem\)\]{height:calc(100vh - 8rem)}.h-full{height:100%}.max-h-\[calc\(100vh-5rem\)\]{max-height:calc(100vh - 5rem)}.max-h-\[calc\(var\(--vh\)-4rem\)\]{max-height:calc(var(--vh) - 4rem)}.min-h-screen{min-height:100vh}.w-4{width:1rem}.w-5\/6{width:83.333333%}.w-6{width:1.5rem}.w-9{width:2.25rem}.w-\[14px\]{width:14px}.w-full{width:100%}.min-w-0{min-width:0}.min-w-full{min-width:100%}.max-w-prose{max-width:65ch}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.translate-x-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x:0px}.rotate-0{--tw-rotate:0deg}.rotate-0,.rotate-90{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-90{--tw-rotate:90deg}.scale-0{--tw-scale-x:0;--tw-scale-y:0}.scale-0,.scale-100{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.\!justify-start{justify-content:flex-start!important}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-4{gap:1rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.5rem*var(--tw-space-x-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.scroll-smooth{scroll-behavior:smooth}.text-ellipsis{text-overflow:ellipsis}.text-clip{text-overflow:clip}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-\[0\.5rem\]{border-radius:.5rem}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-border{border-color:hsl(var(--border))}.border-input{border-color:hsl(var(--input))}.bg-background{background-color:hsl(var(--background))}.bg-background\/80{background-color:hsl(var(--background)/.8)}.bg-background\/95{background-color:hsl(var(--background)/.95)}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.bg-muted{background-color:hsl(var(--muted))}.bg-transparent{background-color:initial}.fill-current{fill:currentColor}.p-2{padding:.5rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-0{padding-left:0;padding-right:0}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.pr-6{padding-right:1.5rem}.pt-2{padding-top:.5rem}.pt-6{padding-top:1.5rem}.text-center{text-align:center}.font-mono{font-family:JetBrains\ Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.text-\[10px\]{font-size:10px}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.leading-loose{line-height:2}.text-foreground{color:hsl(var(--foreground))}.text-foreground\/60{color:hsl(var(--foreground)/.6)}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.underline{text-decoration-line:underline}.no-underline{text-decoration-line:none}.underline-offset-4{text-underline-offset:4px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-70{opacity:.7}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.ring-offset-background{--tw-ring-offset-color:hsl(var(--background))}.backdrop-blur{--tw-backdrop-blur:blur(8px)}.backdrop-blur,.backdrop-blur-sm{backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-1000{transition-duration:1s}[x-cloak]{display:none!important}@media (max-width:640px){.container{padding-left:1rem;padding-right:1rem}}.hover\:bg-accent:hover{background-color:hsl(var(--accent))}.hover\:bg-gray-950:hover{--tw-bg-opacity:1;background-color:rgb(3 7 18/var(--tw-bg-opacity))}.hover\:bg-muted:hover{background-color:hsl(var(--muted))}.hover\:bg-transparent:hover{background-color:initial}.hover\:text-accent-foreground:hover{color:hsl(var(--accent-foreground))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-foreground\/80:hover{color:hsl(var(--foreground)/.8)}.hover\:placeholder-accent-foreground:hover::-moz-placeholder{color:hsl(var(--accent-foreground))}.hover\:placeholder-accent-foreground:hover::placeholder{color:hsl(var(--accent-foreground))}.hover\:opacity-100:hover{opacity:1}.focus\:translate-x-0:focus{--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.focus\:bg-accent:focus{background-color:hsl(var(--accent))}.focus\:bg-gray-950:focus{--tw-bg-opacity:1;background-color:rgb(3 7 18/var(--tw-bg-opacity))}.focus\:text-accent-foreground:focus{color:hsl(var(--accent-foreground))}.focus\:opacity-100:focus{opacity:1}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:outline-offset-\[-1px\]:focus-visible{outline-offset:-1px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:hsl(var(--ring))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:bg-accent{background-color:hsl(var(--accent))}.group:hover .group-hover\:text-accent-foreground{color:hsl(var(--accent-foreground))}.dark\:block:is(.dark *){display:block}.dark\:hidden:is(.dark *){display:none}.dark\:-rotate-90:is(.dark *){--tw-rotate:-90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:rotate-0:is(.dark *){--tw-rotate:0deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:scale-0:is(.dark *){--tw-scale-x:0;--tw-scale-y:0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:scale-100:is(.dark *){--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:invert:is(.dark *){--tw-invert:invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}@media (min-width:640px){.sm\:inline-block{display:inline-block}.sm\:flex{display:flex}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.sm\:pr-12{padding-right:3rem}}@media (min-width:768px){.md\:sticky{position:sticky}.md\:top-14{top:3.5rem}.md\:z-30{z-index:30}.md\:my-0{margin-bottom:0;margin-top:0}.md\:-ml-2{margin-left:-.5rem}.md\:inline{display:inline}.md\:flex{display:flex}.md\:grid{display:grid}.md\:\!hidden{display:none!important}.md\:hidden{display:none}.md\:h-24{height:6rem}.md\:h-\[calc\(100vh-3\.5rem\)\]{height:calc(100vh - 3.5rem)}.md\:h-auto{height:auto}.md\:w-40{width:10rem}.md\:w-auto{width:auto}.md\:w-full{width:100%}.md\:flex-none{flex:none}.md\:translate-x-0{--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.md\:grid-cols-\[220px_minmax\(0\2c 1fr\)\]{grid-template-columns:220px minmax(0,1fr)}.md\:flex-row{flex-direction:row}.md\:justify-end{justify-content:flex-end}.md\:gap-2{gap:.5rem}.md\:gap-6{gap:1.5rem}.md\:overflow-auto{overflow:auto}.md\:bg-transparent{background-color:initial}.md\:p-0{padding:0}.md\:px-0{padding-left:0;padding-right:0}.md\:py-0{padding-bottom:0;padding-top:0}.md\:text-left{text-align:left}}@media (min-width:1024px){.lg\:my-8{margin-bottom:2rem;margin-top:2rem}.lg\:w-64{width:16rem}.lg\:grid-cols-\[240px_minmax\(0\2c 1fr\)\]{grid-template-columns:240px minmax(0,1fr)}.lg\:gap-10{gap:2.5rem}.lg\:py-8{padding-bottom:2rem;padding-top:2rem}}@media (min-width:1280px){.xl\:block{display:block}.xl\:grid{display:grid}.xl\:grid-cols-\[1fr_300px\]{grid-template-columns:1fr 300px}} diff --git a/docs/build/html/_static/theme.js b/docs/build/html/_static/theme.js new file mode 100644 index 0000000..830e8d8 --- /dev/null +++ b/docs/build/html/_static/theme.js @@ -0,0 +1,2 @@ +/*! For license information please see theme.js.LICENSE.txt */ +(()=>{var e={122:function(e){var t;t=function(){return function(){var e={686:function(e,t,n){"use strict";n.d(t,{default:function(){return x}});var r=n(279),i=n.n(r),o=n(370),a=n.n(o),s=n(817),l=n.n(s);function c(e){try{return document.execCommand(e)}catch(e){return!1}}var u=function(e){var t=l()(e);return c("cut"),t},f=function(e,t){var n=function(e){var t="rtl"===document.documentElement.getAttribute("dir"),n=document.createElement("textarea");n.style.fontSize="12pt",n.style.border="0",n.style.padding="0",n.style.margin="0",n.style.position="absolute",n.style[t?"right":"left"]="-9999px";var r=window.pageYOffset||document.documentElement.scrollTop;return n.style.top="".concat(r,"px"),n.setAttribute("readonly",""),n.value=e,n}(e);t.container.appendChild(n);var r=l()(n);return c("copy"),n.remove(),r},d=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{container:document.body},n="";return"string"==typeof e?n=f(e,t):e instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(null==e?void 0:e.type)?n=f(e.value,t):(n=l()(e),c("copy")),n};function p(e){return p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},p(e)}function _(e){return _="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},_(e)}function h(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof e.action?e.action:this.defaultAction,this.target="function"==typeof e.target?e.target:this.defaultTarget,this.text="function"==typeof e.text?e.text:this.defaultText,this.container="object"===_(e.container)?e.container:document.body}},{key:"listenClick",value:function(e){var t=this;this.listener=a()(e,"click",(function(e){return t.onClick(e)}))}},{key:"onClick",value:function(e){var t=e.delegateTarget||e.currentTarget,n=this.action(t)||"copy",r=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.action,n=void 0===t?"copy":t,r=e.container,i=e.target,o=e.text;if("copy"!==n&&"cut"!==n)throw new Error('Invalid "action" value, use either "copy" or "cut"');if(void 0!==i){if(!i||"object"!==p(i)||1!==i.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===n&&i.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===n&&(i.hasAttribute("readonly")||i.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes')}return o?d(o,{container:r}):i?"cut"===n?u(i):d(i,{container:r}):void 0}({action:n,container:this.container,target:this.target(t),text:this.text(t)});this.emit(r?"success":"error",{action:n,text:r,trigger:t,clearSelection:function(){t&&t.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(e){return v("action",e)}},{key:"defaultTarget",value:function(e){var t=v("target",e);if(t)return document.querySelector(t)}},{key:"defaultText",value:function(e){return v("text",e)}},{key:"destroy",value:function(){this.listener.destroy()}}],r=[{key:"copy",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{container:document.body};return d(e,t)}},{key:"cut",value:function(e){return u(e)}},{key:"isSupported",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],t="string"==typeof e?[e]:e,n=!!document.queryCommandSupported;return t.forEach((function(e){n=n&&!!document.queryCommandSupported(e)})),n}}],n&&h(t.prototype,n),r&&h(t,r),l}(i()),x=g},828:function(e){if("undefined"!=typeof Element&&!Element.prototype.matches){var t=Element.prototype;t.matches=t.matchesSelector||t.mozMatchesSelector||t.msMatchesSelector||t.oMatchesSelector||t.webkitMatchesSelector}e.exports=function(e,t){for(;e&&9!==e.nodeType;){if("function"==typeof e.matches&&e.matches(t))return e;e=e.parentNode}}},438:function(e,t,n){var r=n(828);function i(e,t,n,r,i){var a=o.apply(this,arguments);return e.addEventListener(n,a,i),{destroy:function(){e.removeEventListener(n,a,i)}}}function o(e,t,n,i){return function(n){n.delegateTarget=r(n.target,t),n.delegateTarget&&i.call(e,n)}}e.exports=function(e,t,n,r,o){return"function"==typeof e.addEventListener?i.apply(null,arguments):"function"==typeof n?i.bind(null,document).apply(null,arguments):("string"==typeof e&&(e=document.querySelectorAll(e)),Array.prototype.map.call(e,(function(e){return i(e,t,n,r,o)})))}},879:function(e,t){t.node=function(e){return void 0!==e&&e instanceof HTMLElement&&1===e.nodeType},t.nodeList=function(e){var n=Object.prototype.toString.call(e);return void 0!==e&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in e&&(0===e.length||t.node(e[0]))},t.string=function(e){return"string"==typeof e||e instanceof String},t.fn=function(e){return"[object Function]"===Object.prototype.toString.call(e)}},370:function(e,t,n){var r=n(879),i=n(438);e.exports=function(e,t,n){if(!e&&!t&&!n)throw new Error("Missing required arguments");if(!r.string(t))throw new TypeError("Second argument must be a String");if(!r.fn(n))throw new TypeError("Third argument must be a Function");if(r.node(e))return function(e,t,n){return e.addEventListener(t,n),{destroy:function(){e.removeEventListener(t,n)}}}(e,t,n);if(r.nodeList(e))return function(e,t,n){return Array.prototype.forEach.call(e,(function(e){e.addEventListener(t,n)})),{destroy:function(){Array.prototype.forEach.call(e,(function(e){e.removeEventListener(t,n)}))}}}(e,t,n);if(r.string(e))return function(e,t,n){return i(document.body,e,t,n)}(e,t,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}},817:function(e){e.exports=function(e){var t;if("SELECT"===e.nodeName)e.focus(),t=e.value;else if("INPUT"===e.nodeName||"TEXTAREA"===e.nodeName){var n=e.hasAttribute("readonly");n||e.setAttribute("readonly",""),e.select(),e.setSelectionRange(0,e.value.length),n||e.removeAttribute("readonly"),t=e.value}else{e.hasAttribute("contenteditable")&&e.focus();var r=window.getSelection(),i=document.createRange();i.selectNodeContents(e),r.removeAllRanges(),r.addRange(i),t=r.toString()}return t}},279:function(e){function t(){}t.prototype={on:function(e,t,n){var r=this.e||(this.e={});return(r[e]||(r[e]=[])).push({fn:t,ctx:n}),this},once:function(e,t,n){var r=this;function i(){r.off(e,i),t.apply(n,arguments)}return i._=t,this.on(e,i,n)},emit:function(e){for(var t=[].slice.call(arguments,1),n=((this.e||(this.e={}))[e]||[]).slice(),r=0,i=n.length;r{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";function e(e){if(e.includes("full"))return.99;if(e.includes("half"))return.5;if(!e.includes("threshold"))return 0;let t=e[e.indexOf("threshold")+1];return"100"===t?1:"0"===t?0:Number(`.${t}`)}function t(e){let t=e.match(/^(-?[0-9]+)(px|%)?$/);return t?t[1]+(t[2]||"px"):void 0}function r(e){const n="0px 0px 0px 0px",r=e.indexOf("margin");if(-1===r)return n;let i=[];for(let n=1;n<5;n++)i.push(t(e[r+n]||""));return i=i.filter((e=>void 0!==e)),i.length?i.join(" ").trim():n}var i,o,a,s,l=!1,c=!1,u=[],f=-1;function d(e){let t=u.indexOf(e);-1!==t&&t>f&&u.splice(t,1)}function p(){l=!1,c=!0;for(let e=0;e{let i=e();JSON.stringify(i),r?n=i:queueMicrotask((()=>{t(i,n),n=i})),r=!1}));return()=>a(i)}var y=[],v=[],g=[];function x(e,t){"function"==typeof t?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,v.push(t))}function b(e){y.push(e)}function w(e,t,n){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(n)}function E(e,t){e._x_attributeCleanups&&Object.entries(e._x_attributeCleanups).forEach((([n,r])=>{(void 0===t||t.includes(n))&&(r.forEach((e=>e())),delete e._x_attributeCleanups[n])}))}var S=new MutationObserver(L),A=!1;function O(){S.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),A=!0}function k(){!function(){let e=S.takeRecords();j.push((()=>e.length>0&&L(e)));let t=j.length;queueMicrotask((()=>{if(j.length===t)for(;j.length>0;)j.shift()()}))}(),S.disconnect(),A=!1}var j=[];function C(e){if(!A)return e();k();let t=e();return O(),t}var T=!1,$=[];function L(e){if(T)return void($=$.concat(e));let t=new Set,n=new Set,r=new Map,i=new Map;for(let o=0;o1===e.nodeType&&t.add(e))),e[o].removedNodes.forEach((e=>1===e.nodeType&&n.add(e)))),"attributes"===e[o].type)){let t=e[o].target,n=e[o].attributeName,a=e[o].oldValue,s=()=>{r.has(t)||r.set(t,[]),r.get(t).push({name:n,value:t.getAttribute(n)})},l=()=>{i.has(t)||i.set(t,[]),i.get(t).push(n)};t.hasAttribute(n)&&null===a?s():t.hasAttribute(n)?(l(),s()):l()}i.forEach(((e,t)=>{E(t,e)})),r.forEach(((e,t)=>{y.forEach((n=>n(t,e)))}));for(let e of n)t.has(e)||v.forEach((t=>t(e)));t.forEach((e=>{e._x_ignoreSelf=!0,e._x_ignore=!0}));for(let e of t)n.has(e)||e.isConnected&&(delete e._x_ignoreSelf,delete e._x_ignore,g.forEach((t=>t(e))),e._x_ignore=!0,e._x_ignoreSelf=!0);t.forEach((e=>{delete e._x_ignoreSelf,delete e._x_ignore})),t=null,n=null,r=null,i=null}function N(e){return R(P(e))}function M(e,t,n){return e._x_dataStack=[t,...P(n||e)],()=>{e._x_dataStack=e._x_dataStack.filter((e=>e!==t))}}function P(e){return e._x_dataStack?e._x_dataStack:"function"==typeof ShadowRoot&&e instanceof ShadowRoot?P(e.host):e.parentNode?P(e.parentNode):[]}function R(e){return new Proxy({objects:e},q)}var q={ownKeys:({objects:e})=>Array.from(new Set(e.flatMap((e=>Object.keys(e))))),has:({objects:e},t)=>t!=Symbol.unscopables&&e.some((e=>Object.prototype.hasOwnProperty.call(e,t)||Reflect.has(e,t))),get:({objects:e},t,n)=>"toJSON"==t?I:Reflect.get(e.find((e=>Reflect.has(e,t)))||{},t,n),set({objects:e},t,n,r){const i=e.find((e=>Object.prototype.hasOwnProperty.call(e,t)))||e[e.length-1],o=Object.getOwnPropertyDescriptor(i,t);return o?.set&&o?.get?o.set.call(r,n)||!0:Reflect.set(i,t,n)}};function I(){return Reflect.ownKeys(this).reduce(((e,t)=>(e[t]=Reflect.get(this,t),e)),{})}function z(e){let t=(n,r="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach((([i,{value:o,enumerable:a}])=>{if(!1===a||void 0===o)return;if("object"==typeof o&&null!==o&&o.__v_skip)return;let s=""===r?i:`${r}.${i}`;var l;"object"==typeof o&&null!==o&&o._x_interceptor?n[i]=o.initialize(e,s,i):"object"!=typeof(l=o)||Array.isArray(l)||null===l||o===n||o instanceof Element||t(o,s)}))};return t(e)}function D(e,t=()=>{}){let n={initialValue:void 0,_x_interceptor:!0,initialize(t,n,r){return e(this.initialValue,(()=>function(e,t){return t.split(".").reduce(((e,t)=>e[t]),e)}(t,n)),(e=>B(t,n,e)),n,r)}};return t(n),e=>{if("object"==typeof e&&null!==e&&e._x_interceptor){let t=n.initialize.bind(n);n.initialize=(r,i,o)=>{let a=e.initialize(r,i,o);return n.initialValue=a,t(r,i,o)}}else n.initialValue=e;return n}}function B(e,t,n){if("string"==typeof t&&(t=t.split(".")),1!==t.length){if(0===t.length)throw error;return e[t[0]]||(e[t[0]]={}),B(e[t[0]],t.slice(1),n)}e[t[0]]=n}var F={};function H(e,t){F[e]=t}function W(e,t){let n=function(e){let[t,n]=ue(e),r={interceptor:D,...t};return x(e,n),r}(t);return Object.entries(F).forEach((([r,i])=>{Object.defineProperty(e,`$${r}`,{get:()=>i(t,n),enumerable:!1})})),e}function V(e,t,n,...r){try{return n(...r)}catch(n){U(n,e,t)}}function U(e,t,n=void 0){e=Object.assign(e??{message:"No error message given."},{el:t,expression:n}),console.warn(`Alpine Expression Error: ${e.message}\n\n${n?'Expression: "'+n+'"\n\n':""}`,t),setTimeout((()=>{throw e}),0)}var K=!0;function Z(e){let t=K;K=!1;let n=e();return K=t,n}function J(e,t,n={}){let r;return X(e,t)((e=>r=e),n),r}function X(...e){return Y(...e)}var Y=G;function G(e,t){let n={};W(n,e);let r=[n,...P(e)],i="function"==typeof t?function(e,t){return(n=()=>{},{scope:r={},params:i=[]}={})=>{ee(n,t.apply(R([r,...e]),i))}}(r,t):function(e,t,n){let r=function(e,t){if(Q[e])return Q[e];let n=Object.getPrototypeOf((async function(){})).constructor,r=/^[\n\s]*if.*\(.*\)/.test(e.trim())||/^(let|const)\s/.test(e.trim())?`(async()=>{ ${e} })()`:e;let i=(()=>{try{let t=new n(["__self","scope"],`with (scope) { __self.result = ${r} }; __self.finished = true; return __self.result;`);return Object.defineProperty(t,"name",{value:`[Alpine] ${e}`}),t}catch(n){return U(n,t,e),Promise.resolve()}})();return Q[e]=i,i}(t,n);return(i=()=>{},{scope:o={},params:a=[]}={})=>{r.result=void 0,r.finished=!1;let s=R([o,...e]);if("function"==typeof r){let e=r(r,s).catch((e=>U(e,n,t)));r.finished?(ee(i,r.result,s,a,n),r.result=void 0):e.then((e=>{ee(i,e,s,a,n)})).catch((e=>U(e,n,t))).finally((()=>r.result=void 0))}}}(r,t,e);return V.bind(null,e,t,i)}var Q={};function ee(e,t,n,r,i){if(K&&"function"==typeof t){let o=t.apply(n,r);o instanceof Promise?o.then((t=>ee(e,t,n,r))).catch((e=>U(e,i,t))):e(o)}else"object"==typeof t&&t instanceof Promise?t.then((t=>e(t))):e(t)}var te="x-";function ne(e=""){return te+e}var re={};function ie(e,t){return re[e]=t,{before(t){if(!re[t])return void console.warn(String.raw`Cannot find directive \`${t}\`. \`${e}\` will use the default order of execution`);const n=ve.indexOf(t);ve.splice(n>=0?n:ve.indexOf("DEFAULT"),0,e)}}}function oe(e,t,n){if(t=Array.from(t),e._x_virtualDirectives){let n=Object.entries(e._x_virtualDirectives).map((([e,t])=>({name:e,value:t}))),r=ae(n);n=n.map((e=>r.find((t=>t.name===e.name))?{name:`x-bind:${e.name}`,value:`"${e.value}"`}:e)),t=t.concat(n)}let r={},i=t.map(de(((e,t)=>r[e]=t))).filter(he).map(function(e,t){return({name:n,value:r})=>{let i=n.match(me()),o=n.match(/:([a-zA-Z0-9\-_:]+)/),a=n.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],s=t||e[n]||n;return{type:i?i[1]:null,value:o?o[1]:null,modifiers:a.map((e=>e.replace(".",""))),expression:r,original:s}}}(r,n)).sort(ge);return i.map((t=>function(e,t){let n=re[t.type]||(()=>{}),[r,i]=ue(e);w(e,t.original,i);let o=()=>{e._x_ignore||e._x_ignoreSelf||(n.inline&&n.inline(e,t,r),n=n.bind(n,e,t,r),se?le.get(ce).push(n):n())};return o.runCleanups=i,o}(e,t)))}function ae(e){return Array.from(e).map(de()).filter((e=>!he(e)))}var se=!1,le=new Map,ce=Symbol();function ue(e){let t=[],[n,r]=function(e){let t=()=>{};return[n=>{let r=o(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach((e=>e()))}),e._x_effects.add(r),t=()=>{void 0!==r&&(e._x_effects.delete(r),a(r))},r},()=>{t()}]}(e);return t.push(r),[{Alpine:yt,effect:n,cleanup:e=>t.push(e),evaluateLater:X.bind(X,e),evaluate:J.bind(J,e)},()=>t.forEach((e=>e()))]}var fe=(e,t)=>({name:n,value:r})=>(n.startsWith(e)&&(n=n.replace(e,t)),{name:n,value:r});function de(e=()=>{}){return({name:t,value:n})=>{let{name:r,value:i}=pe.reduce(((e,t)=>t(e)),{name:t,value:n});return r!==t&&e(r,t),{name:r,value:i}}}var pe=[];function _e(e){pe.push(e)}function he({name:e}){return me().test(e)}var me=()=>new RegExp(`^${te}([^:^.]+)\\b`),ye="DEFAULT",ve=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",ye,"teleport"];function ge(e,t){let n=-1===ve.indexOf(e.type)?ye:e.type,r=-1===ve.indexOf(t.type)?ye:t.type;return ve.indexOf(n)-ve.indexOf(r)}function xe(e,t,n={}){e.dispatchEvent(new CustomEvent(t,{detail:n,bubbles:!0,composed:!0,cancelable:!0}))}function be(e,t){if("function"==typeof ShadowRoot&&e instanceof ShadowRoot)return void Array.from(e.children).forEach((e=>be(e,t)));let n=!1;if(t(e,(()=>n=!0)),n)return;let r=e.firstElementChild;for(;r;)be(r,t),r=r.nextElementSibling}function we(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var Ee=!1,Se=[],Ae=[];function Oe(){return Se.map((e=>e()))}function ke(){return Se.concat(Ae).map((e=>e()))}function je(e){Se.push(e)}function Ce(e){Ae.push(e)}function Te(e,t=!1){return $e(e,(e=>{if((t?ke():Oe()).some((t=>e.matches(t))))return!0}))}function $e(e,t){if(e){if(t(e))return e;if(e._x_teleportBack&&(e=e._x_teleportBack),e.parentElement)return $e(e.parentElement,t)}}var Le=[];function Ne(e,t=be,n=()=>{}){!function(){se=!0;let r=Symbol();ce=r,le.set(r,[]);let i=()=>{for(;le.get(r).length;)le.get(r).shift()();le.delete(r)};t(e,((e,t)=>{n(e,t),Le.forEach((n=>n(e,t))),oe(e,e.attributes).forEach((e=>e())),e._x_ignore&&t()})),se=!1,i()}()}function Me(e,t=be){t(e,(e=>{!function(e){for(e._x_effects?.forEach(d);e._x_cleanups?.length;)e._x_cleanups.pop()()}(e),E(e)}))}var Pe=[],Re=!1;function qe(e=()=>{}){return queueMicrotask((()=>{Re||setTimeout((()=>{Ie()}))})),new Promise((t=>{Pe.push((()=>{e(),t()}))}))}function Ie(){for(Re=!1;Pe.length;)Pe.shift()()}function ze(e,t){return Array.isArray(t)?De(e,t.join(" ")):"object"==typeof t&&null!==t?function(e,t){let n=e=>e.split(" ").filter(Boolean),r=Object.entries(t).flatMap((([e,t])=>!!t&&n(e))).filter(Boolean),i=Object.entries(t).flatMap((([e,t])=>!t&&n(e))).filter(Boolean),o=[],a=[];return i.forEach((t=>{e.classList.contains(t)&&(e.classList.remove(t),a.push(t))})),r.forEach((t=>{e.classList.contains(t)||(e.classList.add(t),o.push(t))})),()=>{a.forEach((t=>e.classList.add(t))),o.forEach((t=>e.classList.remove(t)))}}(e,t):"function"==typeof t?ze(e,t()):De(e,t)}function De(e,t){return t=!0===t?t="":t||"",n=t.split(" ").filter((t=>!e.classList.contains(t))).filter(Boolean),e.classList.add(...n),()=>{e.classList.remove(...n)};var n}function Be(e,t){return"object"==typeof t&&null!==t?function(e,t){let n={};return Object.entries(t).forEach((([t,r])=>{n[t]=e.style[t],t.startsWith("--")||(t=t.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()),e.style.setProperty(t,r)})),setTimeout((()=>{0===e.style.length&&e.removeAttribute("style")})),()=>{Be(e,n)}}(e,t):function(e,t){let n=e.getAttribute("style",t);return e.setAttribute("style",t),()=>{e.setAttribute("style",n||"")}}(e,t)}function Fe(e,t=()=>{}){let n=!1;return function(){n?t.apply(this,arguments):(n=!0,e.apply(this,arguments))}}function He(e,t,n={}){e._x_transition||(e._x_transition={enter:{during:n,start:n,end:n},leave:{during:n,start:n,end:n},in(n=()=>{},r=()=>{}){Ve(e,t,{during:this.enter.during,start:this.enter.start,end:this.enter.end},n,r)},out(n=()=>{},r=()=>{}){Ve(e,t,{during:this.leave.during,start:this.leave.start,end:this.leave.end},n,r)}})}function We(e){let t=e.parentNode;if(t)return t._x_hidePromise?t:We(t)}function Ve(e,t,{during:n,start:r,end:i}={},o=()=>{},a=()=>{}){if(e._x_transitioning&&e._x_transitioning.cancel(),0===Object.keys(n).length&&0===Object.keys(r).length&&0===Object.keys(i).length)return o(),void a();let s,l,c;!function(e,t){let n,r,i,o=Fe((()=>{C((()=>{n=!0,r||t.before(),i||(t.end(),Ie()),t.after(),e.isConnected&&t.cleanup(),delete e._x_transitioning}))}));e._x_transitioning={beforeCancels:[],beforeCancel(e){this.beforeCancels.push(e)},cancel:Fe((function(){for(;this.beforeCancels.length;)this.beforeCancels.shift()();o()})),finish:o},C((()=>{t.start(),t.during()})),Re=!0,requestAnimationFrame((()=>{if(n)return;let o=1e3*Number(getComputedStyle(e).transitionDuration.replace(/,.*/,"").replace("s","")),a=1e3*Number(getComputedStyle(e).transitionDelay.replace(/,.*/,"").replace("s",""));0===o&&(o=1e3*Number(getComputedStyle(e).animationDuration.replace("s",""))),C((()=>{t.before()})),r=!0,requestAnimationFrame((()=>{n||(C((()=>{t.end()})),Ie(),setTimeout(e._x_transitioning.finish,o+a),i=!0)}))}))}(e,{start(){s=t(e,r)},during(){l=t(e,n)},before:o,end(){s(),c=t(e,i)},after:a,cleanup(){l(),c()}})}function Ue(e,t,n){if(-1===e.indexOf(t))return n;const r=e[e.indexOf(t)+1];if(!r)return n;if("scale"===t&&isNaN(r))return n;if("duration"===t||"delay"===t){let e=r.match(/([0-9]+)ms/);if(e)return e[1]}return"origin"===t&&["top","right","left","center","bottom"].includes(e[e.indexOf(t)+2])?[r,e[e.indexOf(t)+2]].join(" "):r}ie("transition",((e,{value:t,modifiers:n,expression:r},{evaluate:i})=>{"function"==typeof r&&(r=i(r)),!1!==r&&(r&&"boolean"!=typeof r?function(e,t,n){He(e,ze,""),{enter:t=>{e._x_transition.enter.during=t},"enter-start":t=>{e._x_transition.enter.start=t},"enter-end":t=>{e._x_transition.enter.end=t},leave:t=>{e._x_transition.leave.during=t},"leave-start":t=>{e._x_transition.leave.start=t},"leave-end":t=>{e._x_transition.leave.end=t}}[n](t)}(e,r,t):function(e,t,n){He(e,Be);let r=!t.includes("in")&&!t.includes("out")&&!n,i=r||t.includes("in")||["enter"].includes(n),o=r||t.includes("out")||["leave"].includes(n);t.includes("in")&&!r&&(t=t.filter(((e,n)=>nn>t.indexOf("out"))));let a=!t.includes("opacity")&&!t.includes("scale"),s=a||t.includes("opacity")?0:1,l=a||t.includes("scale")?Ue(t,"scale",95)/100:1,c=Ue(t,"delay",0)/1e3,u=Ue(t,"origin","center"),f="opacity, transform",d=Ue(t,"duration",150)/1e3,p=Ue(t,"duration",75)/1e3,_="cubic-bezier(0.4, 0.0, 0.2, 1)";i&&(e._x_transition.enter.during={transformOrigin:u,transitionDelay:`${c}s`,transitionProperty:f,transitionDuration:`${d}s`,transitionTimingFunction:_},e._x_transition.enter.start={opacity:s,transform:`scale(${l})`},e._x_transition.enter.end={opacity:1,transform:"scale(1)"}),o&&(e._x_transition.leave.during={transformOrigin:u,transitionDelay:`${c}s`,transitionProperty:f,transitionDuration:`${p}s`,transitionTimingFunction:_},e._x_transition.leave.start={opacity:1,transform:"scale(1)"},e._x_transition.leave.end={opacity:s,transform:`scale(${l})`})}(e,n,t))})),window.Element.prototype._x_toggleAndCascadeWithTransitions=function(e,t,n,r){const i="visible"===document.visibilityState?requestAnimationFrame:setTimeout;let o=()=>i(n);t?e._x_transition&&(e._x_transition.enter||e._x_transition.leave)?e._x_transition.enter&&(Object.entries(e._x_transition.enter.during).length||Object.entries(e._x_transition.enter.start).length||Object.entries(e._x_transition.enter.end).length)?e._x_transition.in(n):o():e._x_transition?e._x_transition.in(n):o():(e._x_hidePromise=e._x_transition?new Promise(((t,n)=>{e._x_transition.out((()=>{}),(()=>t(r))),e._x_transitioning&&e._x_transitioning.beforeCancel((()=>n({isFromCancelledTransition:!0})))})):Promise.resolve(r),queueMicrotask((()=>{let t=We(e);t?(t._x_hideChildren||(t._x_hideChildren=[]),t._x_hideChildren.push(e)):i((()=>{let t=e=>{let n=Promise.all([e._x_hidePromise,...(e._x_hideChildren||[]).map(t)]).then((([e])=>e?.()));return delete e._x_hidePromise,delete e._x_hideChildren,n};t(e).catch((e=>{if(!e.isFromCancelledTransition)throw e}))}))})))};var Ke=!1;function Ze(e,t=()=>{}){return(...n)=>Ke?t(...n):e(...n)}var Je=[];function Xe(e){Je.push(e)}var Ye=!1;function Ge(e){let t=o;h(((e,n)=>{let r=t(e);return a(r),()=>{}})),e(),h(t)}function Qe(e,t,n,r=[]){switch(e._x_bindings||(e._x_bindings=i({})),e._x_bindings[t]=n,t=r.includes("camel")?t.toLowerCase().replace(/-(\w)/g,((e,t)=>t.toUpperCase())):t){case"value":!function(e,t){if(st(e))void 0===e.attributes.value&&(e.value=t),window.fromModel&&(e.checked="boolean"==typeof t?nt(e.value)===t:tt(e.value,t));else if(at(e))Number.isInteger(t)?e.value=t:Array.isArray(t)||"boolean"==typeof t||[null,void 0].includes(t)?Array.isArray(t)?e.checked=t.some((t=>tt(t,e.value))):e.checked=!!t:e.value=String(t);else if("SELECT"===e.tagName)!function(e,t){const n=[].concat(t).map((e=>e+""));Array.from(e.options).forEach((e=>{e.selected=n.includes(e.value)}))}(e,t);else{if(e.value===t)return;e.value=void 0===t?"":t}}(e,n);break;case"style":!function(e,t){e._x_undoAddedStyles&&e._x_undoAddedStyles(),e._x_undoAddedStyles=Be(e,t)}(e,n);break;case"class":!function(e,t){e._x_undoAddedClasses&&e._x_undoAddedClasses(),e._x_undoAddedClasses=ze(e,t)}(e,n);break;case"selected":case"checked":!function(e,t,n){et(e,t,n),function(e,t,n){e[t]!==n&&(e[t]=n)}(e,t,n)}(e,t,n);break;default:et(e,t,n)}}function et(e,t,n){[null,void 0,!1].includes(n)&&function(e){return!["aria-pressed","aria-checked","aria-expanded","aria-selected"].includes(e)}(t)?e.removeAttribute(t):(it(t)&&(n=t),function(e,t,n){e.getAttribute(t)!=n&&e.setAttribute(t,n)}(e,t,n))}function tt(e,t){return e==t}function nt(e){return!![1,"1","true","on","yes",!0].includes(e)||![0,"0","false","off","no",!1].includes(e)&&(e?Boolean(e):null)}var rt=new Set(["allowfullscreen","async","autofocus","autoplay","checked","controls","default","defer","disabled","formnovalidate","inert","ismap","itemscope","loop","multiple","muted","nomodule","novalidate","open","playsinline","readonly","required","reversed","selected","shadowrootclonable","shadowrootdelegatesfocus","shadowrootserializable"]);function it(e){return rt.has(e)}function ot(e,t,n){let r=e.getAttribute(t);return null===r?"function"==typeof n?n():n:""===r||(it(t)?!![t,"true"].includes(r):r)}function at(e){return"checkbox"===e.type||"ui-checkbox"===e.localName||"ui-switch"===e.localName}function st(e){return"radio"===e.type||"ui-radio"===e.localName}function lt(e,t){var n;return function(){var r=this,i=arguments;clearTimeout(n),n=setTimeout((function(){n=null,e.apply(r,i)}),t)}}function ct(e,t){let n;return function(){let r=arguments;n||(e.apply(this,r),n=!0,setTimeout((()=>n=!1),t))}}function ut({get:e,set:t},{get:n,set:r}){let i,s,l=!0,c=o((()=>{let o=e(),a=n();if(l)r(ft(o)),l=!1;else{let e=JSON.stringify(o),n=JSON.stringify(a);e!==i?r(ft(o)):e!==n&&t(ft(a))}i=JSON.stringify(e()),s=JSON.stringify(n())}));return()=>{a(c)}}function ft(e){return"object"==typeof e?JSON.parse(JSON.stringify(e)):e}var dt={},pt=!1,_t={};function ht(e,t,n){let r=[];for(;r.length;)r.pop()();let i=Object.entries(t).map((([e,t])=>({name:e,value:t}))),o=ae(i);return i=i.map((e=>o.find((t=>t.name===e.name))?{name:`x-bind:${e.name}`,value:`"${e.value}"`}:e)),oe(e,i,n).map((e=>{r.push(e.runCleanups),e()})),()=>{for(;r.length;)r.pop()()}}var mt={},yt={get reactive(){return i},get release(){return a},get effect(){return o},get raw(){return s},version:"3.14.3",flushAndStopDeferringMutations:function(){T=!1,L($),$=[]},dontAutoEvaluateFunctions:Z,disableEffectScheduling:function(e){_=!1,e(),_=!0},startObservingMutations:O,stopObservingMutations:k,setReactivityEngine:function(e){i=e.reactive,a=e.release,o=t=>e.effect(t,{scheduler:e=>{_?function(e){var t;t=e,u.includes(t)||u.push(t),c||l||(l=!0,queueMicrotask(p))}(e):e()}}),s=e.raw},onAttributeRemoved:w,onAttributesAdded:b,closestDataStack:P,skipDuringClone:Ze,onlyDuringClone:function(e){return(...t)=>Ke&&e(...t)},addRootSelector:je,addInitSelector:Ce,interceptClone:Xe,addScopeToNode:M,deferMutations:function(){T=!0},mapAttributes:_e,evaluateLater:X,interceptInit:function(e){Le.push(e)},setEvaluator:function(e){Y=e},mergeProxies:R,extractProp:function(e,t,n,r=!0){if(e._x_bindings&&void 0!==e._x_bindings[t])return e._x_bindings[t];if(e._x_inlineBindings&&void 0!==e._x_inlineBindings[t]){let n=e._x_inlineBindings[t];return n.extract=r,Z((()=>J(e,n.expression)))}return ot(e,t,n)},findClosest:$e,onElRemoved:x,closestRoot:Te,destroyTree:Me,interceptor:D,transition:Ve,setStyles:Be,mutateDom:C,directive:ie,entangle:ut,throttle:ct,debounce:lt,evaluate:J,initTree:Ne,nextTick:qe,prefixed:ne,prefix:function(e){te=e},plugin:function(e){(Array.isArray(e)?e:[e]).forEach((e=>e(yt)))},magic:H,store:function(e,t){if(pt||(dt=i(dt),pt=!0),void 0===t)return dt[e];dt[e]=t,z(dt[e]),"object"==typeof t&&null!==t&&t.hasOwnProperty("init")&&"function"==typeof t.init&&dt[e].init()},start:function(){var e;Ee&&we("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),Ee=!0,document.body||we("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.pre_install.html b/docs/build/html/api/poly_lithic.pre_install.html new file mode 100644 index 0000000..f1285ee --- /dev/null +++ b/docs/build/html/api/poly_lithic.pre_install.html @@ -0,0 +1,174 @@ + + + + + + + + + + poly_lithic.pre_install module | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.pre_install moduleπŸ”—

+
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.scripts.html b/docs/build/html/api/poly_lithic.scripts.html new file mode 100644 index 0000000..75f886e --- /dev/null +++ b/docs/build/html/api/poly_lithic.scripts.html @@ -0,0 +1,195 @@ + + + + + + + + + + poly_lithic.scripts package | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.scripts packageπŸ”—

+
+

SubmodulesπŸ”—

+ +
+
+

Module contentsπŸ”—

+
+
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.scripts.main.html b/docs/build/html/api/poly_lithic.scripts.main.html new file mode 100644 index 0000000..bfb2b5b --- /dev/null +++ b/docs/build/html/api/poly_lithic.scripts.main.html @@ -0,0 +1,185 @@ + + + + + + + + + + poly_lithic.scripts.main module | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.scripts.main moduleπŸ”—

+
+
+poly_lithic.scripts.main.main()[source]πŸ”—
+
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.cli.html b/docs/build/html/api/poly_lithic.src.cli.html new file mode 100644 index 0000000..833bdef --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.cli.html @@ -0,0 +1,211 @@ + + + + + + + + + + poly_lithic.src.cli module | Poly-Lithic documentation + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.cli moduleπŸ”—

+
+
+poly_lithic.src.cli.initailize_config(config_path)[source]πŸ”—
+

Initialize the configuration.

+
+ +
+
+poly_lithic.src.cli.env_config(env_config)[source]πŸ”—
+

Set the environment variables.

+
+ +
+
+poly_lithic.src.cli.setup()[source]πŸ”—
+

Setup the model manager.

+
+ +
+
+async poly_lithic.src.cli.model_main(args, config, broker)[source]πŸ”—
+

Main.

+
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.config.config_object.html b/docs/build/html/api/poly_lithic.src.config.config_object.html new file mode 100644 index 0000000..0bbf319 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.config.config_object.html @@ -0,0 +1,362 @@ + + + + + + + + + + poly_lithic.src.config.config_object module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.config.config_object moduleπŸ”—

+
+
+class poly_lithic.src.config.config_object.ModuleConfig(**data)[source]πŸ”—
+

Bases: BaseModel

+
+
Parameters:
+
    +
  • type (str)

  • +
  • name (str)

  • +
  • pub (str | list | None)

  • +
  • sub (str | list | None)

  • +
  • module_args (dict[str, str | dict | bool] | str | None)

  • +
  • config (Any)

  • +
+
+
+
+
+type: strπŸ”—
+
+ +
+
+name: strπŸ”—
+
+ +
+
+pub: Union[str, list, None]πŸ”—
+
+ +
+
+sub: Union[str, list, None]πŸ”—
+
+ +
+
+module_args: Union[dict[str, Union[str, dict, bool]], str, None]πŸ”—
+
+ +
+
+config: AnyπŸ”—
+
+ +
+
+classmethod validate_module_args(v)[source]πŸ”—
+
+ +
+
+model_config: ClassVar[ConfigDict] = {}πŸ”—
+

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

+
+ +
+ +
+
+class poly_lithic.src.config.config_object.DeploymentConfig(**data)[source]πŸ”—
+

Bases: BaseModel

+
+
Parameters:
+
    +
  • type (str)

  • +
  • rate (float | int | None)

  • +
+
+
+
+
+type: strπŸ”—
+
+ +
+
+rate: Union[float, int, None]πŸ”—
+
+ +
+
+model_config: ClassVar[ConfigDict] = {}πŸ”—
+

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

+
+ +
+ +
+
+class poly_lithic.src.config.config_object.ConfigObject(**data)[source]πŸ”—
+

Bases: BaseModel

+
+
Parameters:
+
+
+
+
+
+deployment: DeploymentConfigπŸ”—
+
+ +
+
+modules: dict[str, ModuleConfig]πŸ”—
+
+ +
+
+class Config[source]πŸ”—
+

Bases: object

+
+
+arbitrary_types_allowed = TrueπŸ”—
+
+ +
+ +
+
+property graphπŸ”—
+
+ +
+
+classmethod check_routing(v)[source]πŸ”—
+
+ +
+
+draw_routing_graph()[source]πŸ”—
+
+ +
+
+model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True}πŸ”—
+

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.config.html b/docs/build/html/api/poly_lithic.src.config.html new file mode 100644 index 0000000..1ec4e99 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.config.html @@ -0,0 +1,236 @@ + + + + + + + + + + poly_lithic.src.config package | Poly-Lithic documentation + + + + + + + + + + + + +
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.config.parser.html b/docs/build/html/api/poly_lithic.src.config.parser.html new file mode 100644 index 0000000..fc71ad2 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.config.parser.html @@ -0,0 +1,202 @@ + + + + + + + + + + poly_lithic.src.config.parser module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.config.parser moduleπŸ”—

+
+
+class poly_lithic.src.config.parser.ConfigParser(config_path)[source]πŸ”—
+

Bases: object

+
+
+parse()[source]πŸ”—
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.html b/docs/build/html/api/poly_lithic.src.html new file mode 100644 index 0000000..802d50a --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.html @@ -0,0 +1,342 @@ + + + + + + + + + + poly_lithic.src package | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src packageπŸ”—

+
+

SubpackagesπŸ”—

+
+ +
+
+
+

SubmodulesπŸ”—

+ +
+
+

Module contentsπŸ”—

+
+
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.interfaces.BaseInterface.html b/docs/build/html/api/poly_lithic.src.interfaces.BaseInterface.html new file mode 100644 index 0000000..ab7c3e3 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.interfaces.BaseInterface.html @@ -0,0 +1,247 @@ + + + + + + + + + + poly_lithic.src.interfaces.BaseInterface module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.interfaces.BaseInterface moduleπŸ”—

+
+
+class poly_lithic.src.interfaces.BaseInterface.BaseDataInterface(config, **kwargs)[source]πŸ”—
+

Bases: ABC

+
+
+abstractmethod load(**kwargs)[source]πŸ”—
+
+ +
+
+abstractmethod save(data, **kwargs)[source]πŸ”—
+
+ +
+ +
+
+class poly_lithic.src.interfaces.BaseInterface.BaseInterface(config)[source]πŸ”—
+

Bases: ABC

+
+
+abstractmethod monitor(name, handler, **kwargs)[source]πŸ”—
+
+ +
+
+abstractmethod get(name, **kwargs)[source]πŸ”—
+
+ +
+
+abstractmethod put(name, value, **kwargs)[source]πŸ”—
+
+ +
+
+abstractmethod put_many(data, **kwargs)[source]πŸ”—
+
+ +
+
+abstractmethod get_many(data, **kwargs)[source]πŸ”—
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.interfaces.file_interface.html b/docs/build/html/api/poly_lithic.src.interfaces.file_interface.html new file mode 100644 index 0000000..87ef430 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.interfaces.file_interface.html @@ -0,0 +1,214 @@ + + + + + + + + + + poly_lithic.src.interfaces.file_interface module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.interfaces.file_interface moduleπŸ”—

+
+
+class poly_lithic.src.interfaces.file_interface.h5dfInterface(config)[source]πŸ”—
+

Bases: BaseDataInterface

+
+
+load(**kwargs)[source]πŸ”—
+
+ +
+
+save(data, **kwargs)[source]πŸ”—
+
+ +
+
+monitor(handler, **kwargs)[source]πŸ”—
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.interfaces.html b/docs/build/html/api/poly_lithic.src.interfaces.html new file mode 100644 index 0000000..fbd7eef --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.interfaces.html @@ -0,0 +1,271 @@ + + + + + + + + + + poly_lithic.src.interfaces package | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.interfaces.k2eg_interface.html b/docs/build/html/api/poly_lithic.src.interfaces.k2eg_interface.html new file mode 100644 index 0000000..61afd74 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.interfaces.k2eg_interface.html @@ -0,0 +1,232 @@ + + + + + + + + + + poly_lithic.src.interfaces.k2eg_interface module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.interfaces.k2eg_interface moduleπŸ”—

+
+
+class poly_lithic.src.interfaces.k2eg_interface.K2EGInterface(config)[source]πŸ”—
+

Bases: BaseInterface

+
+
+monitor(handler, **kwargs)[source]πŸ”—
+
+ +
+
+get(name, **kwargs)[source]πŸ”—
+
+ +
+
+put(name, value, **kwargs)[source]πŸ”—
+
+ +
+
+put_many(data, **kwargs)[source]πŸ”—
+
+ +
+
+get_many(data, **kwargs)[source]πŸ”—
+
+ +
+
+close()[source]πŸ”—
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.interfaces.p4p_interface.html b/docs/build/html/api/poly_lithic.src.interfaces.p4p_interface.html new file mode 100644 index 0000000..eeada1d --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.interfaces.p4p_interface.html @@ -0,0 +1,272 @@ + + + + + + + + + + poly_lithic.src.interfaces.p4p_interface module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.interfaces.p4p_interface moduleπŸ”—

+
+
+class poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface(config)[source]πŸ”—
+

Bases: BaseInterface

+
+
+monitor(handler, **kwargs)[source]πŸ”—
+
+ +
+
+get(name, **kwargs)[source]πŸ”—
+
+ +
+
+put(name, value, **kwargs)[source]πŸ”—
+
+ +
+
+put_many(data, **kwargs)[source]πŸ”—
+
+ +
+
+get_many(data, **kwargs)[source]πŸ”—
+
+ +
+
+close()[source]πŸ”—
+
+ +
+ +
+
+class poly_lithic.src.interfaces.p4p_interface.SimlePVAInterfaceServer(config)[source]πŸ”—
+

Bases: SimplePVAInterface

+

Simple PVA integfcae with a server rather than just a client, this will host the PVs provided in the config

+
+
+close()[source]πŸ”—
+
+ +
+
+put(name, value, **kwargs)[source]πŸ”—
+
+ +
+
+get(name, **kwargs)[source]πŸ”—
+
+ +
+
+put_many(data, **kwargs)[source]πŸ”—
+
+ +
+
+get_many(data, **kwargs)[source]πŸ”—
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.logging_utils.html b/docs/build/html/api/poly_lithic.src.logging_utils.html new file mode 100644 index 0000000..e0f58c1 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.logging_utils.html @@ -0,0 +1,202 @@ + + + + + + + + + + poly_lithic.src.logging_utils package | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.logging_utils packageπŸ”—

+
+

SubmodulesπŸ”—

+ +
+
+

Module contentsπŸ”—

+
+
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.logging_utils.make_logger.html b/docs/build/html/api/poly_lithic.src.logging_utils.make_logger.html new file mode 100644 index 0000000..5fd0d02 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.logging_utils.make_logger.html @@ -0,0 +1,205 @@ + + + + + + + + + + poly_lithic.src.logging_utils.make_logger module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.logging_utils.make_logger moduleπŸ”—

+
+
+poly_lithic.src.logging_utils.make_logger.make_logger(name='model_manager', level=20)[source]πŸ”—
+
+ +
+
+poly_lithic.src.logging_utils.make_logger.get_logger()[source]πŸ”—
+
+ +
+
+poly_lithic.src.logging_utils.make_logger.reset_logging()[source]πŸ”—
+
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.model_utils.LocalModelGetter.html b/docs/build/html/api/poly_lithic.src.model_utils.LocalModelGetter.html new file mode 100644 index 0000000..96de7f6 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.model_utils.LocalModelGetter.html @@ -0,0 +1,208 @@ + + + + + + + + + + poly_lithic.src.model_utils.LocalModelGetter module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.model_utils.LocalModelGetter moduleπŸ”—

+
+
+class poly_lithic.src.model_utils.LocalModelGetter.LocalModelGetter(config)[source]πŸ”—
+

Bases: ModelGetterBase

+
+
+get_model()[source]πŸ”—
+
+ +
+
+get_requirements()[source]πŸ”—
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.model_utils.MlflowModelGetter.html b/docs/build/html/api/poly_lithic.src.model_utils.MlflowModelGetter.html new file mode 100644 index 0000000..e2f4489 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.model_utils.MlflowModelGetter.html @@ -0,0 +1,220 @@ + + + + + + + + + + poly_lithic.src.model_utils.MlflowModelGetter module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.model_utils.MlflowModelGetter moduleπŸ”—

+
+
+class poly_lithic.src.model_utils.MlflowModelGetter.MLflowModelGetter(config)[source]πŸ”—
+

Bases: ModelGetterBase

+
+
+get_config()[source]πŸ”—
+
+ +
+
+get_tags()[source]πŸ”—
+
+ +
+
+get_requirements()[source]πŸ”—
+
+ +
+
+get_model()[source]πŸ”—
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.model_utils.ModelGetterBase.html b/docs/build/html/api/poly_lithic.src.model_utils.ModelGetterBase.html new file mode 100644 index 0000000..544d808 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.model_utils.ModelGetterBase.html @@ -0,0 +1,208 @@ + + + + + + + + + + poly_lithic.src.model_utils.ModelGetterBase module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.model_utils.ModelGetterBase moduleπŸ”—

+
+
+class poly_lithic.src.model_utils.ModelGetterBase.ModelGetterBase[source]πŸ”—
+

Bases: ABC

+
+
+abstractmethod get_model()[source]πŸ”—
+
+ +
+
+abstractmethod get_requirements()[source]πŸ”—
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.model_utils.html b/docs/build/html/api/poly_lithic.src.model_utils.html new file mode 100644 index 0000000..4349578 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.model_utils.html @@ -0,0 +1,222 @@ + + + + + + + + + + poly_lithic.src.model_utils package | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.transformers.BaseTransformer.html b/docs/build/html/api/poly_lithic.src.transformers.BaseTransformer.html new file mode 100644 index 0000000..7eea5d1 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.transformers.BaseTransformer.html @@ -0,0 +1,224 @@ + + + + + + + + + + poly_lithic.src.transformers.BaseTransformer module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.transformers.BaseTransformer moduleπŸ”—

+
+
+class poly_lithic.src.transformers.BaseTransformer.BaseTransformer(config)[source]πŸ”—
+

Bases: object

+
+
Parameters:
+

config (dict)

+
+
+
+
+abstractmethod transform()[source]πŸ”—
+

Call transform function to transform the input data, see SimpleTransformer in model_manager/src/transformers/BaseTransformers.py for an example.

+
+ +
+
+abstractmethod handler(pv_name, value)[source]πŸ”—
+

Handler function to handle the input data, in most cases it initiates the transform function when all the input data is available. +Handler is the only function exposed to the main loop of the program aside from initial configuration.

+
+
Parameters:
+
    +
  • pv_name (str)

  • +
  • value (dict | float | int)

  • +
+
+
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.transformers.BaseTransformers.html b/docs/build/html/api/poly_lithic.src.transformers.BaseTransformers.html new file mode 100644 index 0000000..336878a --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.transformers.BaseTransformers.html @@ -0,0 +1,270 @@ + + + + + + + + + + poly_lithic.src.transformers.BaseTransformers module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.transformers.BaseTransformers moduleπŸ”—

+
+
+class poly_lithic.src.transformers.BaseTransformers.SimpleTransformer(config)[source]πŸ”—
+

Bases: BaseTransformer

+
+
+handler(pv_name, value)[source]πŸ”—
+

Handler function to handle the input data, in most cases it initiates the transform function when all the input data is available. +Handler is the only function exposed to the main loop of the program aside from initial configuration.

+
+ +
+
+transform()[source]πŸ”—
+

Call transform function to transform the input data, see SimpleTransformer in model_manager/src/transformers/BaseTransformers.py for an example.

+
+ +
+ +
+
+class poly_lithic.src.transformers.BaseTransformers.CAImageTransfomer(config)[source]πŸ”—
+

Bases: BaseTransformer

+

Input only image transformation

+
+
+
+
+handler(variable_name, value)[source]πŸ”—
+

Handler function to handle the input data, in most cases it initiates the transform function when all the input data is available. +Handler is the only function exposed to the main loop of the program aside from initial configuration.

+
+
Parameters:
+
    +
  • variable_name (str)

  • +
  • value (dict)

  • +
+
+
+
+ +
+
+transform()[source]πŸ”—
+

Call transform function to transform the input data, see SimpleTransformer in model_manager/src/transformers/BaseTransformers.py for an example.

+
+ +
+ +
+
+class poly_lithic.src.transformers.BaseTransformers.PassThroughTransformer(config)[source]πŸ”—
+

Bases: BaseTransformer

+
+
+handler(pv_name, value)[source]πŸ”—
+

Handler function to handle the input data, in most cases it initiates the transform function when all the input data is available. +Handler is the only function exposed to the main loop of the program aside from initial configuration.

+
+ +
+
+transform()[source]πŸ”—
+

Call transform function to transform the input data, see SimpleTransformer in model_manager/src/transformers/BaseTransformers.py for an example.

+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.transformers.CompoundTransformer.html b/docs/build/html/api/poly_lithic.src.transformers.CompoundTransformer.html new file mode 100644 index 0000000..21a65e8 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.transformers.CompoundTransformer.html @@ -0,0 +1,211 @@ + + + + + + + + + + poly_lithic.src.transformers.CompoundTransformer module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.transformers.CompoundTransformer moduleπŸ”—

+
+
+class poly_lithic.src.transformers.CompoundTransformer.CompoundTransformer(config)[source]πŸ”—
+

Bases: BaseTransformer

+
+
+transform(data)[source]πŸ”—
+

Call transform function to transform the input data, see SimpleTransformer in model_manager/src/transformers/BaseTransformers.py for an example.

+
+ +
+
+handler(name, data)[source]πŸ”—
+

Handler function to handle the input data, in most cases it initiates the transform function when all the input data is available. +Handler is the only function exposed to the main loop of the program aside from initial configuration.

+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.transformers.html b/docs/build/html/api/poly_lithic.src.transformers.html new file mode 100644 index 0000000..114104f --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.transformers.html @@ -0,0 +1,248 @@ + + + + + + + + + + poly_lithic.src.transformers package | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.utils.builder.html b/docs/build/html/api/poly_lithic.src.utils.builder.html new file mode 100644 index 0000000..704c365 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.utils.builder.html @@ -0,0 +1,224 @@ + + + + + + + + + + poly_lithic.src.utils.builder module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.utils.builder moduleπŸ”—

+
+
+class poly_lithic.src.utils.builder.MockModel[source]πŸ”—
+

Bases: object

+
+
+evaluate(value)[source]πŸ”—
+

placeholder for model prediction

+
+ +
+ +
+
+class poly_lithic.src.utils.builder.Builder(config_path)[source]πŸ”—
+

Bases: object

+
+
+build()[source]πŸ”—
+

Build the model manager.

+
+
Return type:
+

MessageBroker

+
+
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.utils.html b/docs/build/html/api/poly_lithic.src.utils.html new file mode 100644 index 0000000..b23aff4 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.utils.html @@ -0,0 +1,267 @@ + + + + + + + + + + poly_lithic.src.utils package | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+ +
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.utils.lazyInterfaceLoader.html b/docs/build/html/api/poly_lithic.src.utils.lazyInterfaceLoader.html new file mode 100644 index 0000000..b88fde0 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.utils.lazyInterfaceLoader.html @@ -0,0 +1,211 @@ + + + + + + + + + + poly_lithic.src.utils.lazyInterfaceLoader module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.utils.lazyInterfaceLoader moduleπŸ”—

+
+
+class poly_lithic.src.utils.lazyInterfaceLoader.AbstractInterfaceLoader[source]πŸ”—
+

Bases: ABC

+

Abstract base class to manage lazy loading of interfaces.

+
+
+abstractmethod keys()[source]πŸ”—
+

Abstract method to return a list of keys for all available interfaces.

+
+ +
+
+import_module(module_name, class_name)[source]πŸ”—
+

Utility function to dynamically import a module and class.

+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/api/poly_lithic.src.utils.messaging.html b/docs/build/html/api/poly_lithic.src.utils.messaging.html new file mode 100644 index 0000000..56e3b85 --- /dev/null +++ b/docs/build/html/api/poly_lithic.src.utils.messaging.html @@ -0,0 +1,571 @@ + + + + + + + + + + poly_lithic.src.utils.messaging module | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

poly_lithic.src.utils.messaging moduleπŸ”—

+
+
+poly_lithic.src.utils.messaging.profileit(func)[source]πŸ”—
+
+ +
+
+class poly_lithic.src.utils.messaging.Message(**data)[source]πŸ”—
+

Bases: BaseModel

+
+
Parameters:
+
    +
  • topic (str | list[str])

  • +
  • source (str)

  • +
  • value (dict)

  • +
  • timestamp (float)

  • +
  • allow_unsafe (bool | None)

  • +
+
+
+
+
+topic: Union[str, list[str]]πŸ”—
+
+ +
+
+source: strπŸ”—
+
+ +
+
+value: dictπŸ”—
+
+ +
+
+timestamp: floatπŸ”—
+
+ +
+
+allow_unsafe: Optional[bool]πŸ”—
+
+ +
+
+classmethod check_topic(topic)[source]πŸ”—
+
+ +
+
+classmethod check_value(value)[source]πŸ”—
+
+ +
+
+property keys: list[str]πŸ”—
+
+ +
+
+property values: list[Any]πŸ”—
+
+ +
+
+property uid: strπŸ”—
+

return a unique id for the message

+
+ +
+
+model_config: ClassVar[ConfigDict] = {}πŸ”—
+

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

+
+ +
+ +
+
+class poly_lithic.src.utils.messaging.Observer[source]πŸ”—
+

Bases: ABC

+
+
+abstractmethod update(message)[source]πŸ”—
+
+
Return type:
+

Message

+
+
Parameters:
+

message (Message)

+
+
+
+ +
+ +
+
+class poly_lithic.src.utils.messaging.MessageBroker[source]πŸ”—
+

Bases: object

+
+
+attach(observer, topic)[source]πŸ”—
+

add observer to topic

+
+
Return type:
+

None

+
+
Parameters:
+
    +
  • observer (Observer)

  • +
  • topic (str | list[str])

  • +
+
+
+
+ +
+
+detach(observer, topic)[source]πŸ”—
+

remove observer from topic, we will probably never use this

+
+
Return type:
+

None

+
+
Parameters:
+
    +
  • observer (Observer)

  • +
  • topic (str | list[str])

  • +
+
+
+
+ +
+
+notify(message)[source]πŸ”—
+

notify all observers of a message

+
+
Return type:
+

None

+
+
Parameters:
+

message (Message)

+
+
+
+ +
+
+get_stats()[source]πŸ”—
+
+ +
+
+get_all()[source]πŸ”—
+
+
Return type:
+

None

+
+
+
+ +
+
+parse_queue()[source]πŸ”—
+
+ +
+ +
+
+class poly_lithic.src.utils.messaging.TransformerObserver(transformer, topic, unpack_output=False)[source]πŸ”—
+

Bases: Observer

+
+
Parameters:
+
    +
  • transformer (<module 'poly_lithic.src.transformers.BaseTransformer' from '/home/gbm96348/nfs_home/lume-deployment/poly_lithic/src/transformers/BaseTransformer.py'>)

  • +
  • topic (str)

  • +
  • unpack_output (bool)

  • +
+
+
+
+
+update(message)[source]πŸ”—
+
+
Return type:
+

Message | list[Message]

+
+
Parameters:
+

message (Message)

+
+
+
+ +
+ +
+
+class poly_lithic.src.utils.messaging.InterfaceObserver(interface, topic, sanitise=True)[source]πŸ”—
+

Bases: Observer

+
+
Parameters:
+
    +
  • interface (<module 'poly_lithic.src.interfaces.BaseInterface' from '/home/gbm96348/nfs_home/lume-deployment/poly_lithic/src/interfaces/BaseInterface.py'>)

  • +
  • topic (str)

  • +
  • sanitise (bool)

  • +
+
+
+
+
+update(message)[source]πŸ”—
+
+
Return type:
+

Message | list[Message]

+
+
Parameters:
+

message (Message)

+
+
+
+ +
+
+get(message)[source]πŸ”—
+

get a single variable from the interface

+
+
Return type:
+

list[Message]

+
+
Parameters:
+

message (Message)

+
+
+
+ +
+
+get_all()[source]πŸ”—
+

get all variables from the interface based on internal variable list

+
+
Return type:
+

list[Message]

+
+
+
+ +
+
+get_many(message)[source]πŸ”—
+

get many variables from the interface

+
+
Return type:
+

list[Message]

+
+
Parameters:
+

message (Message)

+
+
+
+ +
+
+put(message)[source]πŸ”—
+

put a single variable into the interface

+
+
Return type:
+

None

+
+
Parameters:
+

message (Message)

+
+
+
+ +
+
+put_many(message)[source]πŸ”—
+

put many variables into the interface

+
+
Return type:
+

None

+
+
Parameters:
+

message (Message)

+
+
+
+ +
+ +
+
+class poly_lithic.src.utils.messaging.MockModel[source]πŸ”—
+

Bases: object

+
+
+evaluate(value)[source]πŸ”—
+

placeholder for model prediction

+
+ +
+ +
+
+class poly_lithic.src.utils.messaging.ModelObserver(model=None, config=None, topic='model', unpack_input=True, pack_output=True)[source]πŸ”—
+

Bases: Observer

+
+
Parameters:
+
    +
  • topic (str)

  • +
  • unpack_input (bool)

  • +
  • pack_output (bool)

  • +
+
+
+
+
+update(message)[source]πŸ”—
+
+
Return type:
+

list[Message]

+
+
Parameters:
+

message (Message)

+
+
+
+ +
+ +
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/config.html b/docs/build/html/config.html new file mode 100644 index 0000000..7bc5587 --- /dev/null +++ b/docs/build/html/config.html @@ -0,0 +1,187 @@ + + + + + + + + + + Configuration Guide | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

Configuration GuideπŸ”—

+

This page explains how to configure your project.

+
+

OverviewπŸ”—

+

Configuration is handled through YAML files…

+
+
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/genindex.html b/docs/build/html/genindex.html new file mode 100644 index 0000000..fdda5cb --- /dev/null +++ b/docs/build/html/genindex.html @@ -0,0 +1,911 @@ + + + + + + + + + Index | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+ +

Index

+

A

+ + + + +
+ +

B

+ + + + +
+ +

C

+ + + + +
+ +

D

+ + + + +
+ +

E

+ + + + +
+ +

G

+ + + + +
+ +

H

+ + + +
+ +

I

+ + + + +
+ +

K

+ + + + +
+ +

L

+ + + + +
+ +

M

+ + + + +
+ +

N

+ + + + +
+ +

O

+ + + +
+ +

P

+ + + + +
+ +

R

+ + + + +
+ +

S

+ + + + +
+ +

T

+ + + + +
+ +

U

+ + + +
+ +

V

+ + + + +
+ + +
+ +
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/index.html b/docs/build/html/index.html new file mode 100644 index 0000000..850574c --- /dev/null +++ b/docs/build/html/index.html @@ -0,0 +1,184 @@ + + + + + + + + + + Poly-Lithic | Poly-Lithic documentation + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+

Poly-LithicπŸ”—

+

Under Construction 🚧

+

Welcome to the Poly-Lithic documentation!

+ +
+

API Reference:

+ +
+
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/interfaces.html b/docs/build/html/interfaces.html new file mode 100644 index 0000000..659a70d --- /dev/null +++ b/docs/build/html/interfaces.html @@ -0,0 +1,200 @@ + + + + + + + + + + Interfaces Guide | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

Interfaces GuideπŸ”—

+

This page explains how to use interfaces in your project.

+
+

OverviewπŸ”—

+

Interfaces are used to connect to different data sources…

+
+
+

Available InterfacesπŸ”—

+ +
+
+

ExamplesπŸ”—

+

Here are some examples of how to use interfaces…

+
+
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/objects.inv b/docs/build/html/objects.inv new file mode 100644 index 0000000..8edb220 Binary files /dev/null and b/docs/build/html/objects.inv differ diff --git a/docs/build/html/py-modindex.html b/docs/build/html/py-modindex.html new file mode 100644 index 0000000..45b53a2 --- /dev/null +++ b/docs/build/html/py-modindex.html @@ -0,0 +1,303 @@ + + + + + + + + + Python Module Index | Poly-Lithic documentation + + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+ +

Python Module Index

+ +
+ p +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ p
+ poly_lithic +
    + poly_lithic.pre_install +
    + poly_lithic.scripts +
    + poly_lithic.scripts.main +
    + poly_lithic.src +
    + poly_lithic.src.cli +
    + poly_lithic.src.config +
    + poly_lithic.src.config.config_object +
    + poly_lithic.src.config.parser +
    + poly_lithic.src.interfaces +
    + poly_lithic.src.interfaces.BaseInterface +
    + poly_lithic.src.interfaces.file_interface +
    + poly_lithic.src.interfaces.k2eg_interface +
    + poly_lithic.src.interfaces.p4p_interface +
    + poly_lithic.src.logging_utils +
    + poly_lithic.src.logging_utils.make_logger +
    + poly_lithic.src.model_utils +
    + poly_lithic.src.model_utils.LocalModelGetter +
    + poly_lithic.src.model_utils.MlflowModelGetter +
    + poly_lithic.src.model_utils.ModelGetterBase +
    + poly_lithic.src.transformers +
    + poly_lithic.src.transformers.BaseTransformer +
    + poly_lithic.src.transformers.BaseTransformers +
    + poly_lithic.src.transformers.CompoundTransformer +
    + poly_lithic.src.utils +
    + poly_lithic.src.utils.builder +
    + poly_lithic.src.utils.lazyInterfaceLoader +
    + poly_lithic.src.utils.messaging +
+ + +
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/build/html/search.html b/docs/build/html/search.html new file mode 100644 index 0000000..bff4b34 --- /dev/null +++ b/docs/build/html/search.html @@ -0,0 +1,178 @@ + + + + + + + + + Search | Poly-Lithic documentation + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+ Please activate Javascript to enable searching the documentation.
+
+
+
+ +
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/build/html/searchindex.js b/docs/build/html/searchindex.js new file mode 100644 index 0000000..349709a --- /dev/null +++ b/docs/build/html/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles":{"API Reference:":[[29,null]],"Available Interfaces":[[30,"available-interfaces"]],"Available Transformers":[[31,"available-transformers"]],"Configuration Guide":[[28,null]],"Examples":[[30,"examples"],[31,"examples"]],"Interfaces Guide":[[30,null]],"Module contents":[[0,"module-poly_lithic"],[2,"module-poly_lithic.scripts"],[4,"module-poly_lithic.src"],[6,"module-poly_lithic.src.config"],[9,"module-poly_lithic.src.interfaces"],[14,"module-poly_lithic.src.logging_utils"],[16,"module-poly_lithic.src.model_utils"],[20,"module-poly_lithic.src.transformers"],[24,"module-poly_lithic.src.utils"]],"Overview":[[28,"overview"],[30,"overview"],[31,"overview"]],"Poly-Lithic":[[29,null]],"Submodules":[[0,"submodules"],[2,"submodules"],[4,"submodules"],[6,"submodules"],[9,"submodules"],[14,"submodules"],[16,"submodules"],[20,"submodules"],[24,"submodules"]],"Subpackages":[[0,"subpackages"],[4,"subpackages"]],"Transformers Guide":[[31,null]],"User Guide:":[[29,null]],"poly_lithic package":[[0,null]],"poly_lithic.pre_install module":[[1,null]],"poly_lithic.scripts package":[[2,null]],"poly_lithic.scripts.main module":[[3,null]],"poly_lithic.src package":[[4,null]],"poly_lithic.src.cli module":[[5,null]],"poly_lithic.src.config package":[[6,null]],"poly_lithic.src.config.config_object module":[[7,null]],"poly_lithic.src.config.parser module":[[8,null]],"poly_lithic.src.interfaces package":[[9,null]],"poly_lithic.src.interfaces.BaseInterface module":[[10,null]],"poly_lithic.src.interfaces.file_interface module":[[11,null]],"poly_lithic.src.interfaces.k2eg_interface module":[[12,null]],"poly_lithic.src.interfaces.p4p_interface module":[[13,null]],"poly_lithic.src.logging_utils package":[[14,null]],"poly_lithic.src.logging_utils.make_logger module":[[15,null]],"poly_lithic.src.model_utils package":[[16,null]],"poly_lithic.src.model_utils.LocalModelGetter module":[[17,null]],"poly_lithic.src.model_utils.MlflowModelGetter module":[[18,null]],"poly_lithic.src.model_utils.ModelGetterBase module":[[19,null]],"poly_lithic.src.transformers package":[[20,null]],"poly_lithic.src.transformers.BaseTransformer module":[[21,null]],"poly_lithic.src.transformers.BaseTransformers module":[[22,null]],"poly_lithic.src.transformers.CompoundTransformer module":[[23,null]],"poly_lithic.src.utils package":[[24,null]],"poly_lithic.src.utils.builder module":[[25,null]],"poly_lithic.src.utils.lazyInterfaceLoader module":[[26,null]],"poly_lithic.src.utils.messaging module":[[27,null]]},"docnames":["api/poly_lithic","api/poly_lithic.pre_install","api/poly_lithic.scripts","api/poly_lithic.scripts.main","api/poly_lithic.src","api/poly_lithic.src.cli","api/poly_lithic.src.config","api/poly_lithic.src.config.config_object","api/poly_lithic.src.config.parser","api/poly_lithic.src.interfaces","api/poly_lithic.src.interfaces.BaseInterface","api/poly_lithic.src.interfaces.file_interface","api/poly_lithic.src.interfaces.k2eg_interface","api/poly_lithic.src.interfaces.p4p_interface","api/poly_lithic.src.logging_utils","api/poly_lithic.src.logging_utils.make_logger","api/poly_lithic.src.model_utils","api/poly_lithic.src.model_utils.LocalModelGetter","api/poly_lithic.src.model_utils.MlflowModelGetter","api/poly_lithic.src.model_utils.ModelGetterBase","api/poly_lithic.src.transformers","api/poly_lithic.src.transformers.BaseTransformer","api/poly_lithic.src.transformers.BaseTransformers","api/poly_lithic.src.transformers.CompoundTransformer","api/poly_lithic.src.utils","api/poly_lithic.src.utils.builder","api/poly_lithic.src.utils.lazyInterfaceLoader","api/poly_lithic.src.utils.messaging","config","index","interfaces","transformers"],"envversion":{"sphinx":65,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.todo":2,"sphinx.ext.viewcode":1},"filenames":["api/poly_lithic.rst","api/poly_lithic.pre_install.rst","api/poly_lithic.scripts.rst","api/poly_lithic.scripts.main.rst","api/poly_lithic.src.rst","api/poly_lithic.src.cli.rst","api/poly_lithic.src.config.rst","api/poly_lithic.src.config.config_object.rst","api/poly_lithic.src.config.parser.rst","api/poly_lithic.src.interfaces.rst","api/poly_lithic.src.interfaces.BaseInterface.rst","api/poly_lithic.src.interfaces.file_interface.rst","api/poly_lithic.src.interfaces.k2eg_interface.rst","api/poly_lithic.src.interfaces.p4p_interface.rst","api/poly_lithic.src.logging_utils.rst","api/poly_lithic.src.logging_utils.make_logger.rst","api/poly_lithic.src.model_utils.rst","api/poly_lithic.src.model_utils.LocalModelGetter.rst","api/poly_lithic.src.model_utils.MlflowModelGetter.rst","api/poly_lithic.src.model_utils.ModelGetterBase.rst","api/poly_lithic.src.transformers.rst","api/poly_lithic.src.transformers.BaseTransformer.rst","api/poly_lithic.src.transformers.BaseTransformers.rst","api/poly_lithic.src.transformers.CompoundTransformer.rst","api/poly_lithic.src.utils.rst","api/poly_lithic.src.utils.builder.rst","api/poly_lithic.src.utils.lazyInterfaceLoader.rst","api/poly_lithic.src.utils.messaging.rst","config.rst","index.rst","interfaces.rst","transformers.rst"],"indexentries":{"abstractinterfaceloader (class in poly_lithic.src.utils.lazyinterfaceloader)":[[26,"poly_lithic.src.utils.lazyInterfaceLoader.AbstractInterfaceLoader",false]],"allow_unsafe (poly_lithic.src.utils.messaging.message attribute)":[[27,"poly_lithic.src.utils.messaging.Message.allow_unsafe",false]],"arbitrary_types_allowed (poly_lithic.src.config.config_object.configobject.config attribute)":[[7,"poly_lithic.src.config.config_object.ConfigObject.Config.arbitrary_types_allowed",false]],"attach() (poly_lithic.src.utils.messaging.messagebroker method)":[[27,"poly_lithic.src.utils.messaging.MessageBroker.attach",false]],"basedatainterface (class in poly_lithic.src.interfaces.baseinterface)":[[10,"poly_lithic.src.interfaces.BaseInterface.BaseDataInterface",false]],"baseinterface (class in poly_lithic.src.interfaces.baseinterface)":[[10,"poly_lithic.src.interfaces.BaseInterface.BaseInterface",false]],"basetransformer (class in poly_lithic.src.transformers.basetransformer)":[[21,"poly_lithic.src.transformers.BaseTransformer.BaseTransformer",false]],"build() (poly_lithic.src.utils.builder.builder method)":[[25,"poly_lithic.src.utils.builder.Builder.build",false]],"builder (class in poly_lithic.src.utils.builder)":[[25,"poly_lithic.src.utils.builder.Builder",false]],"caimagetransfomer (class in poly_lithic.src.transformers.basetransformers)":[[22,"poly_lithic.src.transformers.BaseTransformers.CAImageTransfomer",false]],"check_routing() (poly_lithic.src.config.config_object.configobject class method)":[[7,"poly_lithic.src.config.config_object.ConfigObject.check_routing",false]],"check_topic() (poly_lithic.src.utils.messaging.message class method)":[[27,"poly_lithic.src.utils.messaging.Message.check_topic",false]],"check_value() (poly_lithic.src.utils.messaging.message class method)":[[27,"poly_lithic.src.utils.messaging.Message.check_value",false]],"close() (poly_lithic.src.interfaces.k2eg_interface.k2eginterface method)":[[12,"poly_lithic.src.interfaces.k2eg_interface.K2EGInterface.close",false]],"close() (poly_lithic.src.interfaces.p4p_interface.simlepvainterfaceserver method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimlePVAInterfaceServer.close",false]],"close() (poly_lithic.src.interfaces.p4p_interface.simplepvainterface method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface.close",false]],"compoundtransformer (class in poly_lithic.src.transformers.compoundtransformer)":[[23,"poly_lithic.src.transformers.CompoundTransformer.CompoundTransformer",false]],"config (poly_lithic.src.config.config_object.moduleconfig attribute)":[[7,"poly_lithic.src.config.config_object.ModuleConfig.config",false]],"configobject (class in poly_lithic.src.config.config_object)":[[7,"poly_lithic.src.config.config_object.ConfigObject",false]],"configobject.config (class in poly_lithic.src.config.config_object)":[[7,"poly_lithic.src.config.config_object.ConfigObject.Config",false]],"configparser (class in poly_lithic.src.config.parser)":[[8,"poly_lithic.src.config.parser.ConfigParser",false]],"deployment (poly_lithic.src.config.config_object.configobject attribute)":[[7,"poly_lithic.src.config.config_object.ConfigObject.deployment",false]],"deploymentconfig (class in poly_lithic.src.config.config_object)":[[7,"poly_lithic.src.config.config_object.DeploymentConfig",false]],"detach() (poly_lithic.src.utils.messaging.messagebroker method)":[[27,"poly_lithic.src.utils.messaging.MessageBroker.detach",false]],"draw_routing_graph() (poly_lithic.src.config.config_object.configobject method)":[[7,"poly_lithic.src.config.config_object.ConfigObject.draw_routing_graph",false]],"env_config() (in module poly_lithic.src.cli)":[[5,"poly_lithic.src.cli.env_config",false]],"evaluate() (poly_lithic.src.utils.builder.mockmodel method)":[[25,"poly_lithic.src.utils.builder.MockModel.evaluate",false]],"evaluate() (poly_lithic.src.utils.messaging.mockmodel method)":[[27,"poly_lithic.src.utils.messaging.MockModel.evaluate",false]],"get() (poly_lithic.src.interfaces.baseinterface.baseinterface method)":[[10,"poly_lithic.src.interfaces.BaseInterface.BaseInterface.get",false]],"get() (poly_lithic.src.interfaces.k2eg_interface.k2eginterface method)":[[12,"poly_lithic.src.interfaces.k2eg_interface.K2EGInterface.get",false]],"get() (poly_lithic.src.interfaces.p4p_interface.simlepvainterfaceserver method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimlePVAInterfaceServer.get",false]],"get() (poly_lithic.src.interfaces.p4p_interface.simplepvainterface method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface.get",false]],"get() (poly_lithic.src.utils.messaging.interfaceobserver method)":[[27,"poly_lithic.src.utils.messaging.InterfaceObserver.get",false]],"get_all() (poly_lithic.src.utils.messaging.interfaceobserver method)":[[27,"poly_lithic.src.utils.messaging.InterfaceObserver.get_all",false]],"get_all() (poly_lithic.src.utils.messaging.messagebroker method)":[[27,"poly_lithic.src.utils.messaging.MessageBroker.get_all",false]],"get_config() (poly_lithic.src.model_utils.mlflowmodelgetter.mlflowmodelgetter method)":[[18,"poly_lithic.src.model_utils.MlflowModelGetter.MLflowModelGetter.get_config",false]],"get_logger() (in module poly_lithic.src.logging_utils.make_logger)":[[15,"poly_lithic.src.logging_utils.make_logger.get_logger",false]],"get_many() (poly_lithic.src.interfaces.baseinterface.baseinterface method)":[[10,"poly_lithic.src.interfaces.BaseInterface.BaseInterface.get_many",false]],"get_many() (poly_lithic.src.interfaces.k2eg_interface.k2eginterface method)":[[12,"poly_lithic.src.interfaces.k2eg_interface.K2EGInterface.get_many",false]],"get_many() (poly_lithic.src.interfaces.p4p_interface.simlepvainterfaceserver method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimlePVAInterfaceServer.get_many",false]],"get_many() (poly_lithic.src.interfaces.p4p_interface.simplepvainterface method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface.get_many",false]],"get_many() (poly_lithic.src.utils.messaging.interfaceobserver method)":[[27,"poly_lithic.src.utils.messaging.InterfaceObserver.get_many",false]],"get_model() (poly_lithic.src.model_utils.localmodelgetter.localmodelgetter method)":[[17,"poly_lithic.src.model_utils.LocalModelGetter.LocalModelGetter.get_model",false]],"get_model() (poly_lithic.src.model_utils.mlflowmodelgetter.mlflowmodelgetter method)":[[18,"poly_lithic.src.model_utils.MlflowModelGetter.MLflowModelGetter.get_model",false]],"get_model() (poly_lithic.src.model_utils.modelgetterbase.modelgetterbase method)":[[19,"poly_lithic.src.model_utils.ModelGetterBase.ModelGetterBase.get_model",false]],"get_requirements() (poly_lithic.src.model_utils.localmodelgetter.localmodelgetter method)":[[17,"poly_lithic.src.model_utils.LocalModelGetter.LocalModelGetter.get_requirements",false]],"get_requirements() (poly_lithic.src.model_utils.mlflowmodelgetter.mlflowmodelgetter method)":[[18,"poly_lithic.src.model_utils.MlflowModelGetter.MLflowModelGetter.get_requirements",false]],"get_requirements() (poly_lithic.src.model_utils.modelgetterbase.modelgetterbase method)":[[19,"poly_lithic.src.model_utils.ModelGetterBase.ModelGetterBase.get_requirements",false]],"get_stats() (poly_lithic.src.utils.messaging.messagebroker method)":[[27,"poly_lithic.src.utils.messaging.MessageBroker.get_stats",false]],"get_tags() (poly_lithic.src.model_utils.mlflowmodelgetter.mlflowmodelgetter method)":[[18,"poly_lithic.src.model_utils.MlflowModelGetter.MLflowModelGetter.get_tags",false]],"graph (poly_lithic.src.config.config_object.configobject property)":[[7,"poly_lithic.src.config.config_object.ConfigObject.graph",false]],"h5dfinterface (class in poly_lithic.src.interfaces.file_interface)":[[11,"poly_lithic.src.interfaces.file_interface.h5dfInterface",false]],"handler() (poly_lithic.src.transformers.basetransformer.basetransformer method)":[[21,"poly_lithic.src.transformers.BaseTransformer.BaseTransformer.handler",false]],"handler() (poly_lithic.src.transformers.basetransformers.caimagetransfomer method)":[[22,"poly_lithic.src.transformers.BaseTransformers.CAImageTransfomer.handler",false]],"handler() (poly_lithic.src.transformers.basetransformers.passthroughtransformer method)":[[22,"poly_lithic.src.transformers.BaseTransformers.PassThroughTransformer.handler",false]],"handler() (poly_lithic.src.transformers.basetransformers.simpletransformer method)":[[22,"poly_lithic.src.transformers.BaseTransformers.SimpleTransformer.handler",false]],"handler() (poly_lithic.src.transformers.compoundtransformer.compoundtransformer method)":[[23,"poly_lithic.src.transformers.CompoundTransformer.CompoundTransformer.handler",false]],"import_module() (poly_lithic.src.utils.lazyinterfaceloader.abstractinterfaceloader method)":[[26,"poly_lithic.src.utils.lazyInterfaceLoader.AbstractInterfaceLoader.import_module",false]],"initailize_config() (in module poly_lithic.src.cli)":[[5,"poly_lithic.src.cli.initailize_config",false]],"interfaceloader (class in poly_lithic.src.interfaces)":[[9,"poly_lithic.src.interfaces.InterfaceLoader",false]],"interfaceobserver (class in poly_lithic.src.utils.messaging)":[[27,"poly_lithic.src.utils.messaging.InterfaceObserver",false]],"k2eginterface (class in poly_lithic.src.interfaces.k2eg_interface)":[[12,"poly_lithic.src.interfaces.k2eg_interface.K2EGInterface",false]],"keys (poly_lithic.src.utils.messaging.message property)":[[27,"poly_lithic.src.utils.messaging.Message.keys",false]],"keys() (poly_lithic.src.interfaces.interfaceloader method)":[[9,"poly_lithic.src.interfaces.InterfaceLoader.keys",false]],"keys() (poly_lithic.src.transformers.transformerloader method)":[[20,"poly_lithic.src.transformers.TransformerLoader.keys",false]],"keys() (poly_lithic.src.utils.lazyinterfaceloader.abstractinterfaceloader method)":[[26,"poly_lithic.src.utils.lazyInterfaceLoader.AbstractInterfaceLoader.keys",false]],"load() (poly_lithic.src.interfaces.baseinterface.basedatainterface method)":[[10,"poly_lithic.src.interfaces.BaseInterface.BaseDataInterface.load",false]],"load() (poly_lithic.src.interfaces.file_interface.h5dfinterface method)":[[11,"poly_lithic.src.interfaces.file_interface.h5dfInterface.load",false]],"localmodelgetter (class in poly_lithic.src.model_utils.localmodelgetter)":[[17,"poly_lithic.src.model_utils.LocalModelGetter.LocalModelGetter",false]],"main() (in module poly_lithic.scripts.main)":[[3,"poly_lithic.scripts.main.main",false]],"make_logger() (in module poly_lithic.src.logging_utils.make_logger)":[[15,"poly_lithic.src.logging_utils.make_logger.make_logger",false]],"message (class in poly_lithic.src.utils.messaging)":[[27,"poly_lithic.src.utils.messaging.Message",false]],"messagebroker (class in poly_lithic.src.utils.messaging)":[[27,"poly_lithic.src.utils.messaging.MessageBroker",false]],"mlflowmodelgetter (class in poly_lithic.src.model_utils.mlflowmodelgetter)":[[18,"poly_lithic.src.model_utils.MlflowModelGetter.MLflowModelGetter",false]],"mockmodel (class in poly_lithic.src.utils.builder)":[[25,"poly_lithic.src.utils.builder.MockModel",false]],"mockmodel (class in poly_lithic.src.utils.messaging)":[[27,"poly_lithic.src.utils.messaging.MockModel",false]],"model_config (poly_lithic.src.config.config_object.configobject attribute)":[[7,"poly_lithic.src.config.config_object.ConfigObject.model_config",false]],"model_config (poly_lithic.src.config.config_object.deploymentconfig attribute)":[[7,"poly_lithic.src.config.config_object.DeploymentConfig.model_config",false]],"model_config (poly_lithic.src.config.config_object.moduleconfig attribute)":[[7,"poly_lithic.src.config.config_object.ModuleConfig.model_config",false]],"model_config (poly_lithic.src.utils.messaging.message attribute)":[[27,"poly_lithic.src.utils.messaging.Message.model_config",false]],"model_main() (in module poly_lithic.src.cli)":[[5,"poly_lithic.src.cli.model_main",false]],"modelgetterbase (class in poly_lithic.src.model_utils.modelgetterbase)":[[19,"poly_lithic.src.model_utils.ModelGetterBase.ModelGetterBase",false]],"modelobserver (class in poly_lithic.src.utils.messaging)":[[27,"poly_lithic.src.utils.messaging.ModelObserver",false]],"module":[[0,"module-poly_lithic",false],[1,"module-poly_lithic.pre_install",false],[2,"module-poly_lithic.scripts",false],[3,"module-poly_lithic.scripts.main",false],[4,"module-poly_lithic.src",false],[5,"module-poly_lithic.src.cli",false],[6,"module-poly_lithic.src.config",false],[7,"module-poly_lithic.src.config.config_object",false],[8,"module-poly_lithic.src.config.parser",false],[9,"module-poly_lithic.src.interfaces",false],[10,"module-poly_lithic.src.interfaces.BaseInterface",false],[11,"module-poly_lithic.src.interfaces.file_interface",false],[12,"module-poly_lithic.src.interfaces.k2eg_interface",false],[13,"module-poly_lithic.src.interfaces.p4p_interface",false],[14,"module-poly_lithic.src.logging_utils",false],[15,"module-poly_lithic.src.logging_utils.make_logger",false],[16,"module-poly_lithic.src.model_utils",false],[17,"module-poly_lithic.src.model_utils.LocalModelGetter",false],[18,"module-poly_lithic.src.model_utils.MlflowModelGetter",false],[19,"module-poly_lithic.src.model_utils.ModelGetterBase",false],[20,"module-poly_lithic.src.transformers",false],[21,"module-poly_lithic.src.transformers.BaseTransformer",false],[22,"module-poly_lithic.src.transformers.BaseTransformers",false],[23,"module-poly_lithic.src.transformers.CompoundTransformer",false],[24,"module-poly_lithic.src.utils",false],[25,"module-poly_lithic.src.utils.builder",false],[26,"module-poly_lithic.src.utils.lazyInterfaceLoader",false],[27,"module-poly_lithic.src.utils.messaging",false]],"module_args (poly_lithic.src.config.config_object.moduleconfig attribute)":[[7,"poly_lithic.src.config.config_object.ModuleConfig.module_args",false]],"moduleconfig (class in poly_lithic.src.config.config_object)":[[7,"poly_lithic.src.config.config_object.ModuleConfig",false]],"modules (poly_lithic.src.config.config_object.configobject attribute)":[[7,"poly_lithic.src.config.config_object.ConfigObject.modules",false]],"monitor() (poly_lithic.src.interfaces.baseinterface.baseinterface method)":[[10,"poly_lithic.src.interfaces.BaseInterface.BaseInterface.monitor",false]],"monitor() (poly_lithic.src.interfaces.file_interface.h5dfinterface method)":[[11,"poly_lithic.src.interfaces.file_interface.h5dfInterface.monitor",false]],"monitor() (poly_lithic.src.interfaces.k2eg_interface.k2eginterface method)":[[12,"poly_lithic.src.interfaces.k2eg_interface.K2EGInterface.monitor",false]],"monitor() (poly_lithic.src.interfaces.p4p_interface.simplepvainterface method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface.monitor",false]],"name (poly_lithic.src.config.config_object.moduleconfig attribute)":[[7,"poly_lithic.src.config.config_object.ModuleConfig.name",false]],"notify() (poly_lithic.src.utils.messaging.messagebroker method)":[[27,"poly_lithic.src.utils.messaging.MessageBroker.notify",false]],"observer (class in poly_lithic.src.utils.messaging)":[[27,"poly_lithic.src.utils.messaging.Observer",false]],"parse() (poly_lithic.src.config.parser.configparser method)":[[8,"poly_lithic.src.config.parser.ConfigParser.parse",false]],"parse_queue() (poly_lithic.src.utils.messaging.messagebroker method)":[[27,"poly_lithic.src.utils.messaging.MessageBroker.parse_queue",false]],"passthroughtransformer (class in poly_lithic.src.transformers.basetransformers)":[[22,"poly_lithic.src.transformers.BaseTransformers.PassThroughTransformer",false]],"poly_lithic":[[0,"module-poly_lithic",false]],"poly_lithic.pre_install":[[1,"module-poly_lithic.pre_install",false]],"poly_lithic.scripts":[[2,"module-poly_lithic.scripts",false]],"poly_lithic.scripts.main":[[3,"module-poly_lithic.scripts.main",false]],"poly_lithic.src":[[4,"module-poly_lithic.src",false]],"poly_lithic.src.cli":[[5,"module-poly_lithic.src.cli",false]],"poly_lithic.src.config":[[6,"module-poly_lithic.src.config",false]],"poly_lithic.src.config.config_object":[[7,"module-poly_lithic.src.config.config_object",false]],"poly_lithic.src.config.parser":[[8,"module-poly_lithic.src.config.parser",false]],"poly_lithic.src.interfaces":[[9,"module-poly_lithic.src.interfaces",false]],"poly_lithic.src.interfaces.baseinterface":[[10,"module-poly_lithic.src.interfaces.BaseInterface",false]],"poly_lithic.src.interfaces.file_interface":[[11,"module-poly_lithic.src.interfaces.file_interface",false]],"poly_lithic.src.interfaces.k2eg_interface":[[12,"module-poly_lithic.src.interfaces.k2eg_interface",false]],"poly_lithic.src.interfaces.p4p_interface":[[13,"module-poly_lithic.src.interfaces.p4p_interface",false]],"poly_lithic.src.logging_utils":[[14,"module-poly_lithic.src.logging_utils",false]],"poly_lithic.src.logging_utils.make_logger":[[15,"module-poly_lithic.src.logging_utils.make_logger",false]],"poly_lithic.src.model_utils":[[16,"module-poly_lithic.src.model_utils",false]],"poly_lithic.src.model_utils.localmodelgetter":[[17,"module-poly_lithic.src.model_utils.LocalModelGetter",false]],"poly_lithic.src.model_utils.mlflowmodelgetter":[[18,"module-poly_lithic.src.model_utils.MlflowModelGetter",false]],"poly_lithic.src.model_utils.modelgetterbase":[[19,"module-poly_lithic.src.model_utils.ModelGetterBase",false]],"poly_lithic.src.transformers":[[20,"module-poly_lithic.src.transformers",false]],"poly_lithic.src.transformers.basetransformer":[[21,"module-poly_lithic.src.transformers.BaseTransformer",false]],"poly_lithic.src.transformers.basetransformers":[[22,"module-poly_lithic.src.transformers.BaseTransformers",false]],"poly_lithic.src.transformers.compoundtransformer":[[23,"module-poly_lithic.src.transformers.CompoundTransformer",false]],"poly_lithic.src.utils":[[24,"module-poly_lithic.src.utils",false]],"poly_lithic.src.utils.builder":[[25,"module-poly_lithic.src.utils.builder",false]],"poly_lithic.src.utils.lazyinterfaceloader":[[26,"module-poly_lithic.src.utils.lazyInterfaceLoader",false]],"poly_lithic.src.utils.messaging":[[27,"module-poly_lithic.src.utils.messaging",false]],"profileit() (in module poly_lithic.src.utils.messaging)":[[27,"poly_lithic.src.utils.messaging.profileit",false]],"pub (poly_lithic.src.config.config_object.moduleconfig attribute)":[[7,"poly_lithic.src.config.config_object.ModuleConfig.pub",false]],"put() (poly_lithic.src.interfaces.baseinterface.baseinterface method)":[[10,"poly_lithic.src.interfaces.BaseInterface.BaseInterface.put",false]],"put() (poly_lithic.src.interfaces.k2eg_interface.k2eginterface method)":[[12,"poly_lithic.src.interfaces.k2eg_interface.K2EGInterface.put",false]],"put() (poly_lithic.src.interfaces.p4p_interface.simlepvainterfaceserver method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimlePVAInterfaceServer.put",false]],"put() (poly_lithic.src.interfaces.p4p_interface.simplepvainterface method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface.put",false]],"put() (poly_lithic.src.utils.messaging.interfaceobserver method)":[[27,"poly_lithic.src.utils.messaging.InterfaceObserver.put",false]],"put_many() (poly_lithic.src.interfaces.baseinterface.baseinterface method)":[[10,"poly_lithic.src.interfaces.BaseInterface.BaseInterface.put_many",false]],"put_many() (poly_lithic.src.interfaces.k2eg_interface.k2eginterface method)":[[12,"poly_lithic.src.interfaces.k2eg_interface.K2EGInterface.put_many",false]],"put_many() (poly_lithic.src.interfaces.p4p_interface.simlepvainterfaceserver method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimlePVAInterfaceServer.put_many",false]],"put_many() (poly_lithic.src.interfaces.p4p_interface.simplepvainterface method)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface.put_many",false]],"put_many() (poly_lithic.src.utils.messaging.interfaceobserver method)":[[27,"poly_lithic.src.utils.messaging.InterfaceObserver.put_many",false]],"rate (poly_lithic.src.config.config_object.deploymentconfig attribute)":[[7,"poly_lithic.src.config.config_object.DeploymentConfig.rate",false]],"reset_logging() (in module poly_lithic.src.logging_utils.make_logger)":[[15,"poly_lithic.src.logging_utils.make_logger.reset_logging",false]],"save() (poly_lithic.src.interfaces.baseinterface.basedatainterface method)":[[10,"poly_lithic.src.interfaces.BaseInterface.BaseDataInterface.save",false]],"save() (poly_lithic.src.interfaces.file_interface.h5dfinterface method)":[[11,"poly_lithic.src.interfaces.file_interface.h5dfInterface.save",false]],"setup() (in module poly_lithic.src.cli)":[[5,"poly_lithic.src.cli.setup",false]],"simlepvainterfaceserver (class in poly_lithic.src.interfaces.p4p_interface)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimlePVAInterfaceServer",false]],"simplepvainterface (class in poly_lithic.src.interfaces.p4p_interface)":[[13,"poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface",false]],"simpletransformer (class in poly_lithic.src.transformers.basetransformers)":[[22,"poly_lithic.src.transformers.BaseTransformers.SimpleTransformer",false]],"source (poly_lithic.src.utils.messaging.message attribute)":[[27,"poly_lithic.src.utils.messaging.Message.source",false]],"sub (poly_lithic.src.config.config_object.moduleconfig attribute)":[[7,"poly_lithic.src.config.config_object.ModuleConfig.sub",false]],"timestamp (poly_lithic.src.utils.messaging.message attribute)":[[27,"poly_lithic.src.utils.messaging.Message.timestamp",false]],"topic (poly_lithic.src.utils.messaging.message attribute)":[[27,"poly_lithic.src.utils.messaging.Message.topic",false]],"transform() (poly_lithic.src.transformers.basetransformer.basetransformer method)":[[21,"poly_lithic.src.transformers.BaseTransformer.BaseTransformer.transform",false]],"transform() (poly_lithic.src.transformers.basetransformers.caimagetransfomer method)":[[22,"poly_lithic.src.transformers.BaseTransformers.CAImageTransfomer.transform",false]],"transform() (poly_lithic.src.transformers.basetransformers.passthroughtransformer method)":[[22,"poly_lithic.src.transformers.BaseTransformers.PassThroughTransformer.transform",false]],"transform() (poly_lithic.src.transformers.basetransformers.simpletransformer method)":[[22,"poly_lithic.src.transformers.BaseTransformers.SimpleTransformer.transform",false]],"transform() (poly_lithic.src.transformers.compoundtransformer.compoundtransformer method)":[[23,"poly_lithic.src.transformers.CompoundTransformer.CompoundTransformer.transform",false]],"transformerloader (class in poly_lithic.src.transformers)":[[20,"poly_lithic.src.transformers.TransformerLoader",false]],"transformerobserver (class in poly_lithic.src.utils.messaging)":[[27,"poly_lithic.src.utils.messaging.TransformerObserver",false]],"type (poly_lithic.src.config.config_object.deploymentconfig attribute)":[[7,"poly_lithic.src.config.config_object.DeploymentConfig.type",false]],"type (poly_lithic.src.config.config_object.moduleconfig attribute)":[[7,"poly_lithic.src.config.config_object.ModuleConfig.type",false]],"uid (poly_lithic.src.utils.messaging.message property)":[[27,"poly_lithic.src.utils.messaging.Message.uid",false]],"update() (poly_lithic.src.utils.messaging.interfaceobserver method)":[[27,"poly_lithic.src.utils.messaging.InterfaceObserver.update",false]],"update() (poly_lithic.src.utils.messaging.modelobserver method)":[[27,"poly_lithic.src.utils.messaging.ModelObserver.update",false]],"update() (poly_lithic.src.utils.messaging.observer method)":[[27,"poly_lithic.src.utils.messaging.Observer.update",false]],"update() (poly_lithic.src.utils.messaging.transformerobserver method)":[[27,"poly_lithic.src.utils.messaging.TransformerObserver.update",false]],"validate_module_args() (poly_lithic.src.config.config_object.moduleconfig class method)":[[7,"poly_lithic.src.config.config_object.ModuleConfig.validate_module_args",false]],"value (poly_lithic.src.utils.messaging.message attribute)":[[27,"poly_lithic.src.utils.messaging.Message.value",false]],"values (poly_lithic.src.utils.messaging.message property)":[[27,"poly_lithic.src.utils.messaging.Message.values",false]]},"objects":{"":[[0,0,0,"-","poly_lithic"]],"poly_lithic":[[1,0,0,"-","pre_install"],[2,0,0,"-","scripts"],[4,0,0,"-","src"]],"poly_lithic.scripts":[[3,0,0,"-","main"]],"poly_lithic.scripts.main":[[3,1,1,"","main"]],"poly_lithic.src":[[5,0,0,"-","cli"],[6,0,0,"-","config"],[9,0,0,"-","interfaces"],[14,0,0,"-","logging_utils"],[16,0,0,"-","model_utils"],[20,0,0,"-","transformers"],[24,0,0,"-","utils"]],"poly_lithic.src.cli":[[5,1,1,"","env_config"],[5,1,1,"","initailize_config"],[5,1,1,"","model_main"],[5,1,1,"","setup"]],"poly_lithic.src.config":[[7,0,0,"-","config_object"],[8,0,0,"-","parser"]],"poly_lithic.src.config.config_object":[[7,2,1,"","ConfigObject"],[7,2,1,"","DeploymentConfig"],[7,2,1,"","ModuleConfig"]],"poly_lithic.src.config.config_object.ConfigObject":[[7,2,1,"","Config"],[7,4,1,"","check_routing"],[7,3,1,"","deployment"],[7,4,1,"","draw_routing_graph"],[7,5,1,"","graph"],[7,3,1,"","model_config"],[7,3,1,"","modules"]],"poly_lithic.src.config.config_object.ConfigObject.Config":[[7,3,1,"","arbitrary_types_allowed"]],"poly_lithic.src.config.config_object.DeploymentConfig":[[7,3,1,"","model_config"],[7,3,1,"","rate"],[7,3,1,"","type"]],"poly_lithic.src.config.config_object.ModuleConfig":[[7,3,1,"","config"],[7,3,1,"","model_config"],[7,3,1,"","module_args"],[7,3,1,"","name"],[7,3,1,"","pub"],[7,3,1,"","sub"],[7,3,1,"","type"],[7,4,1,"","validate_module_args"]],"poly_lithic.src.config.parser":[[8,2,1,"","ConfigParser"]],"poly_lithic.src.config.parser.ConfigParser":[[8,4,1,"","parse"]],"poly_lithic.src.interfaces":[[10,0,0,"-","BaseInterface"],[9,2,1,"","InterfaceLoader"],[11,0,0,"-","file_interface"],[12,0,0,"-","k2eg_interface"],[13,0,0,"-","p4p_interface"]],"poly_lithic.src.interfaces.BaseInterface":[[10,2,1,"","BaseDataInterface"],[10,2,1,"","BaseInterface"]],"poly_lithic.src.interfaces.BaseInterface.BaseDataInterface":[[10,4,1,"","load"],[10,4,1,"","save"]],"poly_lithic.src.interfaces.BaseInterface.BaseInterface":[[10,4,1,"","get"],[10,4,1,"","get_many"],[10,4,1,"","monitor"],[10,4,1,"","put"],[10,4,1,"","put_many"]],"poly_lithic.src.interfaces.InterfaceLoader":[[9,4,1,"","keys"]],"poly_lithic.src.interfaces.file_interface":[[11,2,1,"","h5dfInterface"]],"poly_lithic.src.interfaces.file_interface.h5dfInterface":[[11,4,1,"","load"],[11,4,1,"","monitor"],[11,4,1,"","save"]],"poly_lithic.src.interfaces.k2eg_interface":[[12,2,1,"","K2EGInterface"]],"poly_lithic.src.interfaces.k2eg_interface.K2EGInterface":[[12,4,1,"","close"],[12,4,1,"","get"],[12,4,1,"","get_many"],[12,4,1,"","monitor"],[12,4,1,"","put"],[12,4,1,"","put_many"]],"poly_lithic.src.interfaces.p4p_interface":[[13,2,1,"","SimlePVAInterfaceServer"],[13,2,1,"","SimplePVAInterface"]],"poly_lithic.src.interfaces.p4p_interface.SimlePVAInterfaceServer":[[13,4,1,"","close"],[13,4,1,"","get"],[13,4,1,"","get_many"],[13,4,1,"","put"],[13,4,1,"","put_many"]],"poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface":[[13,4,1,"","close"],[13,4,1,"","get"],[13,4,1,"","get_many"],[13,4,1,"","monitor"],[13,4,1,"","put"],[13,4,1,"","put_many"]],"poly_lithic.src.logging_utils":[[15,0,0,"-","make_logger"]],"poly_lithic.src.logging_utils.make_logger":[[15,1,1,"","get_logger"],[15,1,1,"","make_logger"],[15,1,1,"","reset_logging"]],"poly_lithic.src.model_utils":[[17,0,0,"-","LocalModelGetter"],[18,0,0,"-","MlflowModelGetter"],[19,0,0,"-","ModelGetterBase"]],"poly_lithic.src.model_utils.LocalModelGetter":[[17,2,1,"","LocalModelGetter"]],"poly_lithic.src.model_utils.LocalModelGetter.LocalModelGetter":[[17,4,1,"","get_model"],[17,4,1,"","get_requirements"]],"poly_lithic.src.model_utils.MlflowModelGetter":[[18,2,1,"","MLflowModelGetter"]],"poly_lithic.src.model_utils.MlflowModelGetter.MLflowModelGetter":[[18,4,1,"","get_config"],[18,4,1,"","get_model"],[18,4,1,"","get_requirements"],[18,4,1,"","get_tags"]],"poly_lithic.src.model_utils.ModelGetterBase":[[19,2,1,"","ModelGetterBase"]],"poly_lithic.src.model_utils.ModelGetterBase.ModelGetterBase":[[19,4,1,"","get_model"],[19,4,1,"","get_requirements"]],"poly_lithic.src.transformers":[[21,0,0,"-","BaseTransformer"],[22,0,0,"-","BaseTransformers"],[23,0,0,"-","CompoundTransformer"],[20,2,1,"","TransformerLoader"]],"poly_lithic.src.transformers.BaseTransformer":[[21,2,1,"","BaseTransformer"]],"poly_lithic.src.transformers.BaseTransformer.BaseTransformer":[[21,4,1,"","handler"],[21,4,1,"","transform"]],"poly_lithic.src.transformers.BaseTransformers":[[22,2,1,"","CAImageTransfomer"],[22,2,1,"","PassThroughTransformer"],[22,2,1,"","SimpleTransformer"]],"poly_lithic.src.transformers.BaseTransformers.CAImageTransfomer":[[22,4,1,"","handler"],[22,4,1,"","transform"]],"poly_lithic.src.transformers.BaseTransformers.PassThroughTransformer":[[22,4,1,"","handler"],[22,4,1,"","transform"]],"poly_lithic.src.transformers.BaseTransformers.SimpleTransformer":[[22,4,1,"","handler"],[22,4,1,"","transform"]],"poly_lithic.src.transformers.CompoundTransformer":[[23,2,1,"","CompoundTransformer"]],"poly_lithic.src.transformers.CompoundTransformer.CompoundTransformer":[[23,4,1,"","handler"],[23,4,1,"","transform"]],"poly_lithic.src.transformers.TransformerLoader":[[20,4,1,"","keys"]],"poly_lithic.src.utils":[[25,0,0,"-","builder"],[26,0,0,"-","lazyInterfaceLoader"],[27,0,0,"-","messaging"]],"poly_lithic.src.utils.builder":[[25,2,1,"","Builder"],[25,2,1,"","MockModel"]],"poly_lithic.src.utils.builder.Builder":[[25,4,1,"","build"]],"poly_lithic.src.utils.builder.MockModel":[[25,4,1,"","evaluate"]],"poly_lithic.src.utils.lazyInterfaceLoader":[[26,2,1,"","AbstractInterfaceLoader"]],"poly_lithic.src.utils.lazyInterfaceLoader.AbstractInterfaceLoader":[[26,4,1,"","import_module"],[26,4,1,"","keys"]],"poly_lithic.src.utils.messaging":[[27,2,1,"","InterfaceObserver"],[27,2,1,"","Message"],[27,2,1,"","MessageBroker"],[27,2,1,"","MockModel"],[27,2,1,"","ModelObserver"],[27,2,1,"","Observer"],[27,2,1,"","TransformerObserver"],[27,1,1,"","profileit"]],"poly_lithic.src.utils.messaging.InterfaceObserver":[[27,4,1,"","get"],[27,4,1,"","get_all"],[27,4,1,"","get_many"],[27,4,1,"","put"],[27,4,1,"","put_many"],[27,4,1,"","update"]],"poly_lithic.src.utils.messaging.Message":[[27,3,1,"","allow_unsafe"],[27,4,1,"","check_topic"],[27,4,1,"","check_value"],[27,5,1,"","keys"],[27,3,1,"","model_config"],[27,3,1,"","source"],[27,3,1,"","timestamp"],[27,3,1,"","topic"],[27,5,1,"","uid"],[27,3,1,"","value"],[27,5,1,"","values"]],"poly_lithic.src.utils.messaging.MessageBroker":[[27,4,1,"","attach"],[27,4,1,"","detach"],[27,4,1,"","get_all"],[27,4,1,"","get_stats"],[27,4,1,"","notify"],[27,4,1,"","parse_queue"]],"poly_lithic.src.utils.messaging.MockModel":[[27,4,1,"","evaluate"]],"poly_lithic.src.utils.messaging.ModelObserver":[[27,4,1,"","update"]],"poly_lithic.src.utils.messaging.Observer":[[27,4,1,"","update"]],"poly_lithic.src.utils.messaging.TransformerObserver":[[27,4,1,"","update"]]},"objnames":{"0":["py","module","Python module"],"1":["py","function","Python function"],"2":["py","class","Python class"],"3":["py","attribute","Python attribute"],"4":["py","method","Python method"],"5":["py","property","Python property"]},"objtypes":{"0":"py:module","1":"py:function","2":"py:class","3":"py:attribute","4":"py:method","5":"py:property"},"terms":{"20":15,"abc":[10,19,26,27],"abstract":[9,20,26],"abstractinterfaceload":[4,9,20,24,26],"abstractmethod":[10,19,21,26,27],"add":27,"all":[9,20,21,22,23,26,27],"allow_unsaf":[24,27],"an":[21,22,23],"ani":[7,27],"anoth":31,"ar":[30,31],"arbitrary_types_allow":[6,7],"arg":5,"asid":[21,22,23],"async":5,"attach":[24,27],"avail":[9,20,21,22,23,26,29],"base":[7,8,9,10,11,12,13,17,18,19,20,21,22,23,25,26,27],"basedatainterfac":[4,9,10,11],"baseinterfac":[4,9,12,13,27,30],"basemodel":[7,27],"basetransform":[4,20,23,27],"bool":[7,27],"broker":5,"build":[24,25],"builder":[4,24],"caimagetransfom":[4,20,22,31],"call":[21,22,23],"case":[21,22,23],"check_rout":[6,7],"check_top":[24,27],"check_valu":[24,27],"class":[7,8,9,10,11,12,13,17,18,19,20,21,22,23,25,26,27],"class_nam":26,"classmethod":[7,27],"classvar":[7,27],"cli":[0,4],"client":13,"close":[9,12,13],"compoundtransform":[4,20,31],"config":[0,4,5,10,11,12,13,17,18,21,22,23,27],"config_object":[4,6],"config_path":[5,8,25],"configdict":[7,27],"configobject":[4,6,7],"configpars":[4,6,8],"configur":[5,7,21,22,23,27,29],"conform":[7,27],"connect":30,"construct":29,"data":[7,10,11,12,13,21,22,23,27,30,31],"deploy":[6,7,27],"deploymentconfig":[4,6,7],"detach":[24,27],"dict":[7,21,22,27],"dictionari":[7,27],"differ":30,"document":29,"draw_routing_graph":[6,7],"dynam":26,"env_config":[0,4,5],"environ":5,"evalu":[24,25,27],"exampl":[21,22,23,29],"explain":[28,30,31],"expos":[21,22,23],"fals":27,"file":28,"file_interfac":[4,9],"float":[7,21,27],"format":31,"from":[21,22,23,27,31],"func":27,"function":[21,22,23,26],"gbm96348":27,"get":[9,10,12,13,24,27],"get_al":[24,27],"get_config":[16,18],"get_logg":[4,14,15],"get_mani":[9,10,12,13,24,27],"get_model":[16,17,18,19],"get_requir":[16,17,18,19],"get_stat":[24,27],"get_tag":[16,18],"graph":[6,7],"h5dfinterfac":[4,9,11],"handl":[21,22,23,28],"handler":[10,11,12,13,20,21,22,23],"here":[30,31],"home":27,"host":13,"how":[28,30,31],"i":[21,22,23,28],"id":27,"imag":22,"import":26,"import_modul":[24,26],"initailize_config":[0,4,5],"initi":[5,21,22,23],"input":[21,22,23],"int":[7,21],"integfca":13,"interfac":[0,4,20,26,27,29],"interfaceload":[4,9],"interfaceobserv":[4,24,27],"intern":27,"just":13,"k2eg_interfac":[4,9],"k2eginterfac":[4,9,12],"kei":[4,9,20,24,26,27],"kwarg":[10,11,12,13],"lazi":26,"lazyinterfaceload":[4,24],"level":15,"list":[7,9,20,26,27],"load":[9,10,11,26],"localmodelgett":[4,16],"logging_util":[0,4],"loop":[21,22,23],"lume":27,"main":[0,2,5,21,22,23],"make_logg":[4,14],"manag":[5,25,26],"mani":27,"messag":[4,24],"messagebrok":[4,24,25,27],"method":[9,20,26],"mlflowmodelgett":[4,16],"mockmodel":[4,24,25,27],"model":[5,7,25,27],"model_config":[6,7,24,27],"model_main":[0,4,5],"model_manag":[15,21,22,23],"model_util":[0,4],"modelgetterbas":[4,16,17,18],"modelobserv":[4,24,27],"module_arg":[6,7],"module_nam":26,"moduleconfig":[4,6,7],"monitor":[9,10,11,12,13],"most":[21,22,23],"name":[6,7,10,12,13,15,23],"never":27,"nfs_home":27,"none":[7,27],"notifi":[24,27],"object":[7,8,21,25,27],"observ":[4,24,27],"one":31,"onli":[21,22,23],"option":27,"overview":29,"p4p_interfac":[4,9],"pack_output":27,"packag":29,"page":[28,30,31],"paramet":[7,21,22,27],"pars":[6,8],"parse_queu":[24,27],"parser":[4,6],"passthroughtransform":[4,20,22,31],"placehold":[25,27],"poly_lith":29,"pre_instal":0,"predict":[25,27],"probabl":27,"profileit":[4,24,27],"program":[21,22,23],"project":[28,30,31],"properti":[7,27],"provid":13,"pub":[6,7],"put":[9,10,12,13,24,27],"put_mani":[9,10,12,13,24,27],"pv":13,"pv_name":[21,22],"pva":13,"py":[21,22,23,27],"pydant":[7,27],"rate":[6,7],"rather":13,"remov":27,"reset_log":[4,14,15],"return":[9,20,25,26,27],"sanitis":27,"save":[9,10,11],"script":0,"see":[21,22,23],"server":13,"set":5,"setup":[0,4,5],"should":[7,27],"simlepvainterfaceserv":[4,9,13],"simpl":13,"simplepvainterfac":[4,9,13,30],"simpletransform":[4,20,21,22,23,31],"singl":27,"some":[30,31],"sourc":[3,5,7,8,9,10,11,12,13,15,17,18,19,20,21,22,23,24,25,26,27,30],"src":[0,29],"str":[7,21,22,27],"sub":[6,7],"than":13,"thi":[13,27,28,30,31],"through":28,"timestamp":[24,27],"topic":[24,27],"transform":[0,4,27,29],"transformerload":[4,20],"transformerobserv":[4,24,27],"true":[7,27],"type":[6,7,25,27],"uid":[24,27],"under":29,"union":[7,27],"uniqu":27,"unpack_input":27,"unpack_output":27,"updat":[24,27],"us":[27,30,31],"util":[0,4],"v":7,"validate_module_arg":[6,7],"valu":[10,12,13,21,22,24,25,27],"variabl":[5,27],"variable_nam":22,"we":27,"welcom":29,"when":[21,22,23],"yaml":28,"your":[28,30,31]},"titles":["poly_lithic package","poly_lithic.pre_install module","poly_lithic.scripts package","poly_lithic.scripts.main module","poly_lithic.src package","poly_lithic.src.cli module","poly_lithic.src.config package","poly_lithic.src.config.config_object module","poly_lithic.src.config.parser module","poly_lithic.src.interfaces package","poly_lithic.src.interfaces.BaseInterface module","poly_lithic.src.interfaces.file_interface module","poly_lithic.src.interfaces.k2eg_interface module","poly_lithic.src.interfaces.p4p_interface module","poly_lithic.src.logging_utils package","poly_lithic.src.logging_utils.make_logger module","poly_lithic.src.model_utils package","poly_lithic.src.model_utils.LocalModelGetter module","poly_lithic.src.model_utils.MlflowModelGetter module","poly_lithic.src.model_utils.ModelGetterBase module","poly_lithic.src.transformers package","poly_lithic.src.transformers.BaseTransformer module","poly_lithic.src.transformers.BaseTransformers module","poly_lithic.src.transformers.CompoundTransformer module","poly_lithic.src.utils package","poly_lithic.src.utils.builder module","poly_lithic.src.utils.lazyInterfaceLoader module","poly_lithic.src.utils.messaging module","Configuration Guide","Poly-Lithic","Interfaces Guide","Transformers Guide"],"titleterms":{"api":29,"avail":[30,31],"baseinterfac":10,"basetransform":[21,22],"builder":25,"cli":5,"compoundtransform":23,"config":[6,7,8],"config_object":7,"configur":28,"content":[0,2,4,6,9,14,16,20,24],"exampl":[30,31],"file_interfac":11,"guid":[28,29,30,31],"interfac":[9,10,11,12,13,30],"k2eg_interfac":12,"lazyinterfaceload":26,"lithic":29,"localmodelgett":17,"logging_util":[14,15],"main":3,"make_logg":15,"messag":27,"mlflowmodelgett":18,"model_util":[16,17,18,19],"modelgetterbas":19,"modul":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27],"overview":[28,30,31],"p4p_interfac":13,"packag":[0,2,4,6,9,14,16,20,24],"parser":8,"poli":29,"poly_lith":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27],"pre_instal":1,"refer":29,"script":[2,3],"src":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27],"submodul":[0,2,4,6,9,14,16,20,24],"subpackag":[0,4],"transform":[20,21,22,23,31],"user":29,"util":[24,25,26,27]}}) \ No newline at end of file diff --git a/docs/build/html/transformers.html b/docs/build/html/transformers.html new file mode 100644 index 0000000..158ea56 --- /dev/null +++ b/docs/build/html/transformers.html @@ -0,0 +1,202 @@ + + + + + + + + + + Transformers Guide | Poly-Lithic documentation + + + + + + + + + + + + +
+ Skip to content +
+ +
+
+
+ +
+
+
+ +
+
+
+ + +
+
+

Transformers GuideπŸ”—

+

This page explains how to use transformers in your project.

+
+

OverviewπŸ”—

+

Transformers are used to transform data from one format to another…

+
+
+

Available TransformersπŸ”—

+ +
+
+

ExamplesπŸ”—

+

Here are some examples of how to use transformers…

+
+
+ +
+
+
+
+
+
+

Β© 2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council Built with Sphinx 8.2.3

+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..747ffb7 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/api/poly_lithic.pre_install.rst b/docs/source/api/poly_lithic.pre_install.rst new file mode 100644 index 0000000..a91a98f --- /dev/null +++ b/docs/source/api/poly_lithic.pre_install.rst @@ -0,0 +1,7 @@ +poly\_lithic.pre\_install module +================================ + +.. automodule:: poly_lithic.pre_install + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.rst b/docs/source/api/poly_lithic.rst new file mode 100644 index 0000000..0f6359b --- /dev/null +++ b/docs/source/api/poly_lithic.rst @@ -0,0 +1,27 @@ +poly\_lithic package +==================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.scripts + poly_lithic.src + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.pre_install + +Module contents +--------------- + +.. automodule:: poly_lithic + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.scripts.main.rst b/docs/source/api/poly_lithic.scripts.main.rst new file mode 100644 index 0000000..1949029 --- /dev/null +++ b/docs/source/api/poly_lithic.scripts.main.rst @@ -0,0 +1,7 @@ +poly\_lithic.scripts.main module +================================ + +.. automodule:: poly_lithic.scripts.main + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.scripts.rst b/docs/source/api/poly_lithic.scripts.rst new file mode 100644 index 0000000..ab04525 --- /dev/null +++ b/docs/source/api/poly_lithic.scripts.rst @@ -0,0 +1,18 @@ +poly\_lithic.scripts package +============================ + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.scripts.main + +Module contents +--------------- + +.. automodule:: poly_lithic.scripts + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.cli.rst b/docs/source/api/poly_lithic.src.cli.rst new file mode 100644 index 0000000..17c352e --- /dev/null +++ b/docs/source/api/poly_lithic.src.cli.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.cli module +=========================== + +.. automodule:: poly_lithic.src.cli + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.config.config_object.rst b/docs/source/api/poly_lithic.src.config.config_object.rst new file mode 100644 index 0000000..d9e1a13 --- /dev/null +++ b/docs/source/api/poly_lithic.src.config.config_object.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.config.config\_object module +============================================= + +.. automodule:: poly_lithic.src.config.config_object + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.config.parser.rst b/docs/source/api/poly_lithic.src.config.parser.rst new file mode 100644 index 0000000..1d49c11 --- /dev/null +++ b/docs/source/api/poly_lithic.src.config.parser.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.config.parser module +===================================== + +.. automodule:: poly_lithic.src.config.parser + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.config.rst b/docs/source/api/poly_lithic.src.config.rst new file mode 100644 index 0000000..b7e10e0 --- /dev/null +++ b/docs/source/api/poly_lithic.src.config.rst @@ -0,0 +1,19 @@ +poly\_lithic.src.config package +=============================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.config.config_object + poly_lithic.src.config.parser + +Module contents +--------------- + +.. automodule:: poly_lithic.src.config + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.interfaces.BaseInterface.rst b/docs/source/api/poly_lithic.src.interfaces.BaseInterface.rst new file mode 100644 index 0000000..089a0ca --- /dev/null +++ b/docs/source/api/poly_lithic.src.interfaces.BaseInterface.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.interfaces.BaseInterface module +================================================ + +.. automodule:: poly_lithic.src.interfaces.BaseInterface + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.interfaces.file_interface.rst b/docs/source/api/poly_lithic.src.interfaces.file_interface.rst new file mode 100644 index 0000000..4078678 --- /dev/null +++ b/docs/source/api/poly_lithic.src.interfaces.file_interface.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.interfaces.file\_interface module +================================================== + +.. automodule:: poly_lithic.src.interfaces.file_interface + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.interfaces.k2eg_interface.rst b/docs/source/api/poly_lithic.src.interfaces.k2eg_interface.rst new file mode 100644 index 0000000..d6c7672 --- /dev/null +++ b/docs/source/api/poly_lithic.src.interfaces.k2eg_interface.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.interfaces.k2eg\_interface module +================================================== + +.. automodule:: poly_lithic.src.interfaces.k2eg_interface + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.interfaces.p4p_interface.rst b/docs/source/api/poly_lithic.src.interfaces.p4p_interface.rst new file mode 100644 index 0000000..698314b --- /dev/null +++ b/docs/source/api/poly_lithic.src.interfaces.p4p_interface.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.interfaces.p4p\_interface module +================================================= + +.. automodule:: poly_lithic.src.interfaces.p4p_interface + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.interfaces.rst b/docs/source/api/poly_lithic.src.interfaces.rst new file mode 100644 index 0000000..5d0cad2 --- /dev/null +++ b/docs/source/api/poly_lithic.src.interfaces.rst @@ -0,0 +1,21 @@ +poly\_lithic.src.interfaces package +=================================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.interfaces.BaseInterface + poly_lithic.src.interfaces.file_interface + poly_lithic.src.interfaces.k2eg_interface + poly_lithic.src.interfaces.p4p_interface + +Module contents +--------------- + +.. automodule:: poly_lithic.src.interfaces + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.logging_utils.make_logger.rst b/docs/source/api/poly_lithic.src.logging_utils.make_logger.rst new file mode 100644 index 0000000..38da117 --- /dev/null +++ b/docs/source/api/poly_lithic.src.logging_utils.make_logger.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.logging\_utils.make\_logger module +=================================================== + +.. automodule:: poly_lithic.src.logging_utils.make_logger + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.logging_utils.rst b/docs/source/api/poly_lithic.src.logging_utils.rst new file mode 100644 index 0000000..88c003b --- /dev/null +++ b/docs/source/api/poly_lithic.src.logging_utils.rst @@ -0,0 +1,18 @@ +poly\_lithic.src.logging\_utils package +======================================= + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.logging_utils.make_logger + +Module contents +--------------- + +.. automodule:: poly_lithic.src.logging_utils + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.model_utils.LocalModelGetter.rst b/docs/source/api/poly_lithic.src.model_utils.LocalModelGetter.rst new file mode 100644 index 0000000..54a42fc --- /dev/null +++ b/docs/source/api/poly_lithic.src.model_utils.LocalModelGetter.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.model\_utils.LocalModelGetter module +===================================================== + +.. automodule:: poly_lithic.src.model_utils.LocalModelGetter + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.model_utils.MlflowModelGetter.rst b/docs/source/api/poly_lithic.src.model_utils.MlflowModelGetter.rst new file mode 100644 index 0000000..fdea057 --- /dev/null +++ b/docs/source/api/poly_lithic.src.model_utils.MlflowModelGetter.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.model\_utils.MlflowModelGetter module +====================================================== + +.. automodule:: poly_lithic.src.model_utils.MlflowModelGetter + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.model_utils.ModelGetterBase.rst b/docs/source/api/poly_lithic.src.model_utils.ModelGetterBase.rst new file mode 100644 index 0000000..bb4ffa8 --- /dev/null +++ b/docs/source/api/poly_lithic.src.model_utils.ModelGetterBase.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.model\_utils.ModelGetterBase module +==================================================== + +.. automodule:: poly_lithic.src.model_utils.ModelGetterBase + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.model_utils.rst b/docs/source/api/poly_lithic.src.model_utils.rst new file mode 100644 index 0000000..227144a --- /dev/null +++ b/docs/source/api/poly_lithic.src.model_utils.rst @@ -0,0 +1,20 @@ +poly\_lithic.src.model\_utils package +===================================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.model_utils.LocalModelGetter + poly_lithic.src.model_utils.MlflowModelGetter + poly_lithic.src.model_utils.ModelGetterBase + +Module contents +--------------- + +.. automodule:: poly_lithic.src.model_utils + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.rst b/docs/source/api/poly_lithic.src.rst new file mode 100644 index 0000000..4090afb --- /dev/null +++ b/docs/source/api/poly_lithic.src.rst @@ -0,0 +1,31 @@ +poly\_lithic.src package +======================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.config + poly_lithic.src.interfaces + poly_lithic.src.logging_utils + poly_lithic.src.model_utils + poly_lithic.src.transformers + poly_lithic.src.utils + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.cli + +Module contents +--------------- + +.. automodule:: poly_lithic.src + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.transformers.BaseTransformer.rst b/docs/source/api/poly_lithic.src.transformers.BaseTransformer.rst new file mode 100644 index 0000000..56f9274 --- /dev/null +++ b/docs/source/api/poly_lithic.src.transformers.BaseTransformer.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.transformers.BaseTransformer module +==================================================== + +.. automodule:: poly_lithic.src.transformers.BaseTransformer + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.transformers.BaseTransformers.rst b/docs/source/api/poly_lithic.src.transformers.BaseTransformers.rst new file mode 100644 index 0000000..6f962d2 --- /dev/null +++ b/docs/source/api/poly_lithic.src.transformers.BaseTransformers.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.transformers.BaseTransformers module +===================================================== + +.. automodule:: poly_lithic.src.transformers.BaseTransformers + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.transformers.CompoundTransformer.rst b/docs/source/api/poly_lithic.src.transformers.CompoundTransformer.rst new file mode 100644 index 0000000..124879f --- /dev/null +++ b/docs/source/api/poly_lithic.src.transformers.CompoundTransformer.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.transformers.CompoundTransformer module +======================================================== + +.. automodule:: poly_lithic.src.transformers.CompoundTransformer + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.transformers.rst b/docs/source/api/poly_lithic.src.transformers.rst new file mode 100644 index 0000000..3f5b64d --- /dev/null +++ b/docs/source/api/poly_lithic.src.transformers.rst @@ -0,0 +1,20 @@ +poly\_lithic.src.transformers package +===================================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.transformers.BaseTransformer + poly_lithic.src.transformers.BaseTransformers + poly_lithic.src.transformers.CompoundTransformer + +Module contents +--------------- + +.. automodule:: poly_lithic.src.transformers + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.utils.builder.rst b/docs/source/api/poly_lithic.src.utils.builder.rst new file mode 100644 index 0000000..7d5746a --- /dev/null +++ b/docs/source/api/poly_lithic.src.utils.builder.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.utils.builder module +===================================== + +.. automodule:: poly_lithic.src.utils.builder + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.utils.lazyInterfaceLoader.rst b/docs/source/api/poly_lithic.src.utils.lazyInterfaceLoader.rst new file mode 100644 index 0000000..0371afe --- /dev/null +++ b/docs/source/api/poly_lithic.src.utils.lazyInterfaceLoader.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.utils.lazyInterfaceLoader module +================================================= + +.. automodule:: poly_lithic.src.utils.lazyInterfaceLoader + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.utils.messaging.rst b/docs/source/api/poly_lithic.src.utils.messaging.rst new file mode 100644 index 0000000..0f60093 --- /dev/null +++ b/docs/source/api/poly_lithic.src.utils.messaging.rst @@ -0,0 +1,7 @@ +poly\_lithic.src.utils.messaging module +======================================= + +.. automodule:: poly_lithic.src.utils.messaging + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/api/poly_lithic.src.utils.rst b/docs/source/api/poly_lithic.src.utils.rst new file mode 100644 index 0000000..becb5ca --- /dev/null +++ b/docs/source/api/poly_lithic.src.utils.rst @@ -0,0 +1,20 @@ +poly\_lithic.src.utils package +============================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + poly_lithic.src.utils.builder + poly_lithic.src.utils.lazyInterfaceLoader + poly_lithic.src.utils.messaging + +Module contents +--------------- + +.. automodule:: poly_lithic.src.utils + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..a4a4814 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,39 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'Poly-Lithic' +copyright = '2025, Matuesz Leputa - ISIS Neutron and Muon Source at RAL - Science and Technology Facilities Council' +author = 'Matuesz Leputa' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', + 'sphinx_autodoc_typehints', + 'sphinxemoji.sphinxemoji', + 'sphinx.ext.viewcode', + 'sphinx.ext.todo' +] + +templates_path = ['_templates'] +exclude_patterns = [] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +# Optional theme +html_permalinks_icon = 'πŸ”—' +html_theme = 'sphinxawesome_theme' # or 'alabaster', 'sphinx_rtd_theme', etc. +html_static_path = ['_static'] + +autodoc_member_order = 'bysource' # show functions in source order +autodoc_typehints = 'description' # show type hints in function descriptions diff --git a/docs/source/config.rst b/docs/source/config.rst new file mode 100644 index 0000000..601fb1b --- /dev/null +++ b/docs/source/config.rst @@ -0,0 +1,8 @@ +Configuration Guide +=================== + +This page explains how to configure your project. + +Overview +-------- +Configuration is handled through YAML files... \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..7932a7a --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,20 @@ +Poly-Lithic +========================= +Under Construction 🚧 + +Welcome to the Poly-Lithic documentation! + + +.. toctree:: + :maxdepth: 2 + :caption: User Guide: + + transformers + interfaces + config + +.. toctree:: + :maxdepth: 1 + :caption: API Reference: + + api/poly_lithic.src \ No newline at end of file diff --git a/docs/source/interfaces.rst b/docs/source/interfaces.rst new file mode 100644 index 0000000..0bd3b1f --- /dev/null +++ b/docs/source/interfaces.rst @@ -0,0 +1,17 @@ +Interfaces Guide +================ + +This page explains how to use interfaces in your project. + +Overview +-------- +Interfaces are used to connect to different data sources... + +Available Interfaces +-------------------- +- :class:`~poly_lithic.src.interfaces.BaseInterface.BaseInterface` +- :class:`~poly_lithic.src.interfaces.p4p_interface.SimplePVAInterface` + +Examples +-------- +Here are some examples of how to use interfaces... \ No newline at end of file diff --git a/docs/source/transformers.rst b/docs/source/transformers.rst new file mode 100644 index 0000000..9e0bf81 --- /dev/null +++ b/docs/source/transformers.rst @@ -0,0 +1,19 @@ +Transformers Guide +================== + +This page explains how to use transformers in your project. + +Overview +-------- +Transformers are used to transform data from one format to another... + +Available Transformers +---------------------- +- :class:`~poly_lithic.src.transformers.BaseTransformers.SimpleTransformer` +- :class:`~poly_lithic.src.transformers.BaseTransformers.CAImageTransfomer` +- :class:`~poly_lithic.src.transformers.BaseTransformers.PassThroughTransformer` +- :class:`~poly_lithic.src.transformers.CompoundTransformer.CompoundTransformer` + +Examples +-------- +Here are some examples of how to use transformers... \ No newline at end of file diff --git a/readme.md b/readme.md index a81b2ca..bd4c97f 100644 --- a/readme.md +++ b/readme.md @@ -1,12 +1,35 @@ -NEW DOCS in progress -use old docs in the meantime: [old docs](./readme_old.md) - -# Lume-Deployment - -Lume-Deployment - soon to be renamed, is a package that allows you do deploy any model with an arbitrary number of inputs and outputs, related data transformations and system interfaces. + + +# Table of Contents + - [Installation](#installation) + - [Usage](#usage) + - [Configuration file](#configuration-file-formerly-pv_mappings-files) + - [Modules](#modules) + - [Interface](#interface) + - [Interface Configs](#interface-configs) + - [p4p sample configuration](#p4p-sample-configuration) + - [p4p_server sample configuration](#p4p_server-sample-configuration) + - [k2eg sample configuration](#k2eg-sample-configuration) + - [Transformer](#transformer) + - [Transformer Configs](#transformer-configs) + - [SimpleTransformer sample configuration](#simpletransformer-sample-configuration) + - [CAImageTransformer sample configuration](#caimagetransformer-sample-configuration) + - [CompoundTransformer sample configuration](#compoundtransformer-sample-configuration) + - [PassThroughTransformer sample configuration](#passthroughtransformer-sample-configuration) + - [Model](#model) + - [Model Configs](#model-configs) + - [Example Model - Local Model](#example-model---local-model) + - [Example Model - MLFLow Model](#example-model---mlflow-model) + + - [Roadmap](#roadmap) +# Poly-Lithic + +Poly-Lithic is a package that allows you do deploy any model with an arbitrary number of inputs and outputs, related data transformations and system interfaces. Each deployment is defined by a model, typically hosted and retrieved from [MLFlow](https://mlflow.org/) and YAML file that describes the DG (Directed Graph) of model, transformations and interfaces. There are no restrictions on the numbers and types of nodes in the graph, so it may be used for things other than ML models. + ## Installation Python `3.11.x` recommended. @@ -27,6 +50,10 @@ pip install -e . ```python model_manager --publish -c ./tests/pv_mapping_mlflow.yaml -e ./tests/env.json ``` +or +```python +pl --publish -c ./tests/pv_mapping_mlflow.yaml -e ./tests/env.json +``` The env file is a json file that contains the environment variables that are used in the deployment. In this example we are pulling the [torch](./examples/torch_and_generic.ipynb) model and wrapping it with simple transformers and a simple p4p server. Reqired variables are: @@ -105,7 +132,7 @@ Note that the data is a dictionary of dictionaries. ## Modules ### Interface -Interface modules are used to interact with external data, usually an accelerators control systems but can be anything. They follow the following structure (see [base interface class](./model_manager/src/interfaces/BaseInterface.py)): +Interface modules are used to interact with external data, usually an accelerators control systems but can be anything. They follow the following structure (see [base interface class](./poly-lithic/src/interfaces/BaseInterface.py)): ```python class BaseInterface(ABC): @@ -148,11 +175,11 @@ data = { } ``` #### Interface Configs -| Module | Description | YAML configuration | Compatible with: | -| ------ | ----------- | ------------------ | --------------- | -| `p4p` | EPICS data source, must have an external EPICS server running. Note that SoftIOCPVA will not work with this module. | [config](#p4p-sample-configuration) | `SimpleTransformer`, `CompoundTransformer` | -| `p4p_server` | EPICS data source, host EPICS p4p server for specifed PVs | same [config](#p4p-sample-configuration) as `p4p`| `SimpleTransformer`, `CompoundTransformer` | -| `k2eg` | Kafka to EPICS gateway, get data from Kafka and write it to EPICS | [config](#k2eg-sample-configuration) | `SimpleTransformer`, `CompoundTransformer` , `CAImageTransformer`* | +| Module | Description | YAML configuration | +| ------ | ----------- | ------------------ | +| `p4p` | EPICS data source, must have an external EPICS server running. Note that SoftIOCPVA will not work with this module. | [config](#p4p-sample-configuration) | +| `p4p_server` | EPICS data source, host EPICS p4p server for specifed PVs | same [config](#p4p-sample-configuration) as `p4p`| +| `k2eg` | Kafka to EPICS gateway, get data from Kafka and write it to EPICS | [config](#k2eg-sample-configuration) | ##### `p4p` sample configuration ```yaml @@ -198,12 +225,276 @@ modules: ``` Yes, it is identical to p4p, the only difference is that the p4p server will host the PVs for the specified variables. -##### `k2eg` sample configuration - -TODO - See old [docs](./readme_old.md) for now. +#### `k2eg` Sample configuration + +This module is built on top of SLAC's [k2eg](https://github.com/slaclab/k2eg), it's great because it allows you get data from `pva` and `ca` protocols over Kafka. currently its the only interface that supports `ca` protocol. + +> [!CAUTION] +> need some more testing as it was last tested in Q1 2024. + +```yaml +input_data: + get_method: "k2eg" + config: + variables: + MY_VAR:TEST_A: + proto: ca # supports ca or pva + name: MY_VAR:TEST_A + MY_VAR:TEST_B: + proto: pva + name: MY_VAR:TEST_B +``` ### Transformer -### Model +Transformers are used to transform data from one format to another, they can be used to perform some data processing, aggregation or any other transformation action. They follow the structure (see [base transformer class](./poly-lithic/src/transformers/BaseTransformer.py)): + +```python +class BaseTransformer: + @abstractmethod + def __init__(self, config: dict): + """ + config: dict passed from the pv_mappings.yaml files. + """ + pass + + @abstractmethod + def transform(self): + """ + Call transform function to transform the input data, see SimpleTransformer in model_manager/src/transformers/BaseTransformers.py for an example. + """ + pass + + @abstractmethod + def handler(self, pv_name: str, value: dict | float | int): + """ + Handler function to handle the input data, in most cases it initiates the transform function when all the input data is available. + Handler is the only function exposed to the main loop of the program aside from initial configuration. + """ + pass +``` +#### Transformer Configs +| Module | Description | YAML configuration | +| ------ | ----------- | ------------------ | +| `SimpleTransformer` | Simple transformer that can be used to transform scalar values (ca or pv values that have a `value` field) | [config](#simpletransformer-sample-configuration) | +| `CAImageTransformer` | Transformer that can be used to transform a triplet of an array, x and y ca values into a np array | [config](#caimagetransformer-sample-configuration) | +| `CompoundTransformer` | Compound transformer that can be used to have multiple transformers in parallel | [config](#compoundtransformer-sample-configuration) | +| `PassThroughTransformer` | Transformer that can be used to pass data through without any transformation other than the tag | [config](#passthroughtransformer-sample-configuration) | + +##### `SimpleTransformer` Sample configuration +```yaml +modules: + input_transformer: + name: "input_transformer" + type: "transformer.SimpleTransformer" + pub: "model_input" + sub: + - "system_input" + module_args: None + config: + symbols: + - "LUME:MLFLOW:TEST_B" + - "LUME:MLFLOW:TEST_A" + variables: + x2: + formula: "LUME:MLFLOW:TEST_B" + x1: + formula: "LUME:MLFLOW:TEST_A" +``` + +##### `CAImageTransformer` Sample configuration +```yaml +modules: + image_transformer: + name: "image_transformer" + type: "transformer.CAImageTransformer" + pub: "model_input" + sub: + - "update" + module_args: None + config: + variables: + img_1: + img_ch: "MY_TEST_CA" + img_x_ch: "MY_TEST_CA_X" + img_y_ch: "MY_TEST_CA_Y" + img_2: + img_ch: "MY_TEST_C2" + img_x_ch: "MY_TEST_CA_X2" + img_y_ch: "MY_TEST_CA_Y2" +``` + +##### `PassThroughTransformer` Sample configuration +```yaml +modules: + output_transformer: + name: "output_transformer" + type: "transformer.PassThroughTransformer" + pub: "system_output" + sub: + - "model_output" + module_args: None + config: + variables: + LUME:MLFLOW:TEST_IMAGE: "y_img" +``` +##### `CompoundTransformer` Sample configuration +> [!CAUTION] +> This module will be deprecated in the future, pub-sub model means that compound transformers are no longer needed. + +```yaml +modules: + compound_transformer: + name: "compound_transformer" + type: "transformer.CompoundTransformer" + pub: "model_input" + sub: + - "update" + module_args: None + config: + transformers: + transformer_1: + type: "SimpleTransformer" + config: + symbols: + - "MY_TEST_A" + - "MY_TEST_B" + variables: + x2: + formula: "MY_TEST_A*2" + x1: + formula: "MY_TEST_B+MY_TEST_A" + transformer_2: + type: "CAImageTransformer" + config: + variables: + img_1: + img_ch: "MY_TEST_CA" + img_x_ch: "MY_TEST_CA_X" + img_y_ch: "MY_TEST_CA_Y" + img_2: + img_ch: "MY_TEST_C2" + img_x_ch: "MY_TEST_CA_X2" + img_y_ch: "MY_TEST_CA_Y2" +``` + + +### Model + +Models are the core of the deployment, they can be retrieved locally or from MLFlow and accept data in form of dictionries. By deafault models pivot the dictionry or rather remove the additional keys from messages to simplify the data structure that the model has to process. + +All models have to implement the `evaluate` method that takes a dictionary of inputs and returns a dictionary of outputs. + +#### Model Config + +```yaml +model: # this is the name of the model module, it is used to identify the model in the graph + name: "model" # name of the model used to identify it in the graph, overrides the name in the module section + type: "model.SimpleModel" # type of module, used to identify the model class and subclass, in this case we are saying it a model + pub: "model" # where the model will publish its outputs, this is the topic that the model will publish to + sub: "in_transformer" # topic that the model will subscribe to, this is the topic that the model will listen for inputs + module_args: None # defines what arguments to pass to the model observer, if any this can inform unpacking etc + config: + type: "modelGetter" # defines the type of model getter, this is used to identify the model getter class + args: # arguments to pass to the model getter class, in this case we are passing the path to the model definition file + +``` +See the following examples for usage + + +###### Example Model - Local Model + +```python + +class SimpleModel(torch.nn.Module): + def __init__(self): + super(SimpleModel, self).__init__() + self.linear1 = torch.nn.Linear(2, 10) + self.linear2 = torch.nn.Linear(10, 1) + + def forward(self, x): # this is for our benefit, it is not used by poly-lithic + x = torch.relu(self.linear1(x)) + x = self.linear2(x) + return x + + # this method is necessary for the model to be evaluated by poly-lithic + def evaluate(self, x: dict) -> dict: + # x will be a dicrt of keys and values + # {"x": x, "y": y} + input_tensor = torch.tensor([x['x'], x['y']], dtype=torch.float32) + # you may want to do somethinf more complex here + output_tensor = self.forward(input_tensor) + # return a dictionary of keys and values + return {'output': output_tensor.item()} + ``` + + Lets say we want to retreive the model locally, we need to specify a factory class: + +```python + +class ModelFactory: + # can do more complex things here but we will just load the model from a locally saved file + def __init__(self): + # add this path to python environment + os.environ['PYTHONPATH'] = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..', '..') + ) + print('PYTHONPATH set to:', os.environ['PYTHONPATH']) + self.model = SimpleModel() + model_path = 'examples/base/local/model.pth' + if os.path.exists(model_path): + self.model.load_state_dict(torch.load(model_path)) + print('Model loaded successfully.') + else: + print( + f"Warning: Model file '{model_path}' not found. Using untrained model." + ) + print('ModelFactory initialized') + + # this method is necessary for the model to be retrieved by poly-lithic + def get_model(self): + return self.model +``` +The in the config file: + +```yaml +... +model: + name: "model" + type: "model.SimpleModel" + pub: "model" + sub: "in_transformer" + module_args: None + config: + type: "LocalModelGetter" + args: + model_path: "examples/base/local/model_definition.py" # path to the model definition + model_factory_class: "ModelFactory" # class that you use to create the model + variables: + max: + type: "scalar" +... +``` + +Then to run the model: +```bash +pl --publish -c examples/base/local/deployment_config.yaml +``` +See the [local example notebook](./examples/base/local/simple_model_local.ipynb) for more details. + +#### Example Model - MLFLow Model +See the [MLFlow example notebook](./examples/base/mlflow_model/simple_model_mlflow.ipynb) for more details. + +## Roadmap +| Feature / Task | Timeline | Priority | Status | +|-----------------------------------------------|--------------|----------|---------------| +| πŸ–ŒοΈ 🎨 **Make logo** | 1–3 Months | πŸ₯‡ | 🚧 In Progress | +| 🧠 πŸ”§ **Lume-Model Integration** | 1–3 Months | πŸ₯‡ | 🚧 In Progress | +| πŸ“¦ πŸ€– **MLflow 3.x Support** | 1–3 Months | πŸ₯‡ | ⏳ Planned | +| 🌐 πŸš€ **Move to `gh-pages`** | 1–3 Months | πŸ₯ˆ | ⏳ Planned | +| πŸ”— πŸ§ͺ **p4p4isis Interface** | 6–12 Months | πŸ₯‰ | ⏳ Planned | +| πŸ“Š 🧭 **Time Series Aggregation** | 3–6 Months | πŸ₯‰ | ⏳ Planned | +| πŸ“ˆ πŸ” **Model Evaluator Module** | 3–6 Months | πŸ₯‰ | ⏳ Planned | +| πŸ” πŸ”§ **Model Retrainer Module** | 6–12 Months | πŸ₯ˆ | ⏳ Planned | \ No newline at end of file