Source code for VeraGridEngine.Templates.BasicBlockCatalog.catalog

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0

from __future__ import annotations

from functools import lru_cache
from pathlib import Path
from typing import Sequence

from VeraGridEngine.Devices.Dynamic.emt_template import EmtModelTemplate
from VeraGridEngine.Devices.Dynamic.var_factory import VarFactory
from VeraGridEngine.Templates.BasicBlockCatalog.Functions import BasicBlockCatalogTemplateBuilder
from VeraGridEngine.Templates.BasicBlockCatalog.Functions import get_basic_block_catalog_template_builder_by_module_name
from VeraGridEngine.Templates.BasicBlockCatalog.catalog_static_registry import BasicBlockCatalogStaticRecord
from VeraGridEngine.Templates.BasicBlockCatalog.catalog_static_registry import build_basic_block_catalog_branch_skeleton as build_static_basic_block_catalog_branch_skeleton
from VeraGridEngine.Templates.BasicBlockCatalog.catalog_static_registry import build_basic_block_catalog_pending_template_reason
from VeraGridEngine.Templates.BasicBlockCatalog.catalog_static_registry import get_basic_block_catalog_static_records


[docs] class BasicBlockTemplateDescriptor: """ Explicit, typed description of one shipped catalog template. """ __slots__ = ( "_template_key", "_typ_id", "_blkdef_name", "_sample_display_name", "_display_label", "_category_path", "_inputs", "_outputs", "_states", "_params", "_unsupported_lines", "_module_name", "_module_filename", ) def __init__( self, template_key: str, typ_id: str, blkdef_name: str, sample_display_name: str, display_label: str, category_path: Sequence[str], inputs: Sequence[str], outputs: Sequence[str], states: Sequence[str], params: Sequence[str], unsupported_lines: Sequence[str], module_name: str, module_filename: str, ) -> None: """ Store one immutable catalog descriptor. :param template_key: Stable semantic lookup key. :param typ_id: Imported numeric type identifier. :param blkdef_name: Raw source block name. :param sample_display_name: Clean human-facing source name. :param display_label: Unique label exposed in the editor. :param category_path: Full editor category path. :param inputs: Input names. :param outputs: Output names. :param states: State names. :param params: Runtime parameter names. :param unsupported_lines: Pending markers for non editor-ready templates. :param module_name: Standalone module stem inside the shipped catalog. :param module_filename: Standalone module filename inside the shipped catalog. :returns: None. """ self._template_key = template_key self._typ_id = typ_id self._blkdef_name = blkdef_name self._sample_display_name = sample_display_name self._display_label = display_label self._category_path = tuple(category_path) self._inputs = tuple(inputs) self._outputs = tuple(outputs) self._states = tuple(states) self._params = tuple(params) self._unsupported_lines = tuple(unsupported_lines) self._module_name = module_name self._module_filename = module_filename @property def template_key(self) -> str: """ Return the stable semantic lookup key. :returns: Template lookup key. """ return self._template_key @property def typ_id(self) -> str: """ Return the imported numeric type identifier. :returns: Imported type identifier. """ return self._typ_id @property def blkdef_name(self) -> str: """ Return the raw imported block name. :returns: Raw block name. """ return self._blkdef_name @property def sample_display_name(self) -> str: """ Return the clean display name derived from the shipped module. :returns: Clean source name. """ return self._sample_display_name @property def display_label(self) -> str: """ Return the unique label exposed by the editor. :returns: Unique label. """ return self._display_label @property def category_path(self) -> Sequence[str]: """ Return the full library category path. :returns: Category path. """ return self._category_path @property def inputs(self) -> Sequence[str]: """ Return the input names. :returns: Input-name tuple. """ return self._inputs @property def outputs(self) -> Sequence[str]: """ Return the output names. :returns: Output-name tuple. """ return self._outputs @property def states(self) -> Sequence[str]: """ Return the state names. :returns: State-name tuple. """ return self._states @property def params(self) -> Sequence[str]: """ Return the runtime parameter names. :returns: Parameter-name tuple. """ return self._params @property def unsupported_lines(self) -> Sequence[str]: """ Return the pending markers for non editor-ready templates. :returns: Unsupported marker tuple. """ return self._unsupported_lines @property def module_name(self) -> str: """ Return the standalone Python module stem. :returns: Module stem. """ return self._module_name @property def module_filename(self) -> str: """ Return the standalone Python filename. :returns: Module filename. """ return self._module_filename @property def is_editor_ready(self) -> bool: """ Return whether the template can be exposed in the editor. :returns: ``True`` when the template has executable content. """ if len(self._unsupported_lines) == 0: return True else: return False @property def search_text(self) -> str: """ Build the normalized search text used by the editor filter. :returns: Search text. """ tokens: list[str] = list() category_name: str # The search index mixes the stable human-facing fields so one filter can # match label, source name, semantic key, imported type id, or category. tokens.append(self._display_label) tokens.append(self._blkdef_name) tokens.append(self._sample_display_name) tokens.append(self._template_key) tokens.append(self._typ_id) for category_name in self._category_path: tokens.append(category_name) return " ".join(tokens).strip()
[docs] def get_basic_block_catalog_root() -> Path: """ Return the directory containing the shipped basic block catalog. :returns: Catalog root directory. """ return Path(__file__).resolve().parent
[docs] def get_basic_block_catalog_templates_dir() -> Path: """ Return the directory with the shipped standalone template modules. :returns: Standalone-template directory. """ return get_basic_block_catalog_root() / "Functions"
[docs] def build_basic_block_catalog_branch_skeleton() -> dict[str, dict[str, list[object]]]: """ Build the static category tree used by the editor branch. :returns: Fresh category tree. """ return build_static_basic_block_catalog_branch_skeleton()
[docs] def get_basic_block_catalog_pending_template_reason() -> str: """ Return the canonical pending reason for non-executable catalog templates. :returns: Pending reason. """ return build_basic_block_catalog_pending_template_reason()
def _descriptor_sort_key(descriptor: BasicBlockTemplateDescriptor) -> tuple[int, str]: """ Build the deterministic sort key for one descriptor. :param descriptor: Catalog descriptor. :returns: Sort key ordered by type identifier and module name. """ return int(descriptor.typ_id), descriptor.module_name def _build_descriptor_from_static_record(record: BasicBlockCatalogStaticRecord) -> BasicBlockTemplateDescriptor: """ Convert one static record into the public descriptor object. :param record: Static snapshot record. :returns: Public catalog descriptor. """ return BasicBlockTemplateDescriptor( template_key=record.template_key, typ_id=record.typ_id, blkdef_name=record.blkdef_name, sample_display_name=record.sample_display_name, display_label=record.display_label, category_path=record.category_path, inputs=record.inputs, outputs=record.outputs, states=record.states, params=record.params, unsupported_lines=record.unsupported_lines, module_name=record.module_name, module_filename=record.module_filename, ) def _resolve_template_builder_from_descriptor(descriptor: BasicBlockTemplateDescriptor) -> BasicBlockCatalogTemplateBuilder: """ Resolve one shipped standalone template builder from the static package registry. :param descriptor: Catalog descriptor for the target module. :returns: Imported standalone template builder. :raises KeyError: Raised when the generated package registry does not expose the descriptor module. """ template_builder: BasicBlockCatalogTemplateBuilder | None = ( get_basic_block_catalog_template_builder_by_module_name(descriptor.module_name) ) # The descriptor snapshot owns the canonical module name, so the runtime can # resolve one explicit builder without probing the filesystem or imported module state. if template_builder is None: raise KeyError(f"Could not resolve standalone template builder for module '{descriptor.module_name}'") else: return template_builder
[docs] @lru_cache(maxsize=1) def get_basic_block_catalog_descriptors() -> Sequence[BasicBlockTemplateDescriptor]: """ Return the shipped basic block catalog descriptors. :returns: Full descriptor tuple. """ descriptors: list[BasicBlockTemplateDescriptor] = list() static_record: BasicBlockCatalogStaticRecord # The runtime now consumes one generated static snapshot instead of discovering # modules dynamically, so the catalog shape is stable across refactors. for static_record in get_basic_block_catalog_static_records(): descriptors.append(_build_descriptor_from_static_record(static_record)) descriptors.sort(key=_descriptor_sort_key) return tuple(descriptors)
[docs] @lru_cache(maxsize=1) def get_editor_ready_basic_block_catalog_descriptors() -> Sequence[BasicBlockTemplateDescriptor]: """ Return only the shipped templates that are ready for editor exposure. :returns: Editor-ready descriptor tuple. """ ready_descriptors: list[BasicBlockTemplateDescriptor] = list() descriptor: BasicBlockTemplateDescriptor for descriptor in get_basic_block_catalog_descriptors(): if descriptor.is_editor_ready: ready_descriptors.append(descriptor) else: pass return tuple(ready_descriptors)
[docs] @lru_cache(maxsize=1) def get_pending_basic_block_catalog_descriptors() -> Sequence[BasicBlockTemplateDescriptor]: """ Return only the shipped templates that are not ready for editor exposure. :returns: Pending descriptor tuple. """ pending_descriptors: list[BasicBlockTemplateDescriptor] = list() descriptor: BasicBlockTemplateDescriptor for descriptor in get_basic_block_catalog_descriptors(): if descriptor.is_editor_ready: pass else: pending_descriptors.append(descriptor) return tuple(pending_descriptors)
[docs] @lru_cache(maxsize=1) def get_basic_block_catalog_descriptor_by_key() -> dict[str, BasicBlockTemplateDescriptor]: """ Return the descriptor lookup indexed by semantic key. :returns: Descriptor lookup by semantic key. """ descriptor_lookup: dict[str, BasicBlockTemplateDescriptor] = dict() descriptor: BasicBlockTemplateDescriptor for descriptor in get_basic_block_catalog_descriptors(): descriptor_lookup[descriptor.template_key] = descriptor return descriptor_lookup
[docs] @lru_cache(maxsize=1) def get_basic_block_catalog_descriptor_by_typ_id() -> dict[str, BasicBlockTemplateDescriptor]: """ Return the descriptor lookup indexed by imported type identifier. :returns: Descriptor lookup by imported type identifier. """ descriptor_lookup: dict[str, BasicBlockTemplateDescriptor] = dict() descriptor: BasicBlockTemplateDescriptor for descriptor in get_basic_block_catalog_descriptors(): descriptor_lookup[descriptor.typ_id] = descriptor return descriptor_lookup
[docs] def load_basic_block_catalog_template( descriptor: BasicBlockTemplateDescriptor, var_factory: VarFactory, name: str | None = None, ) -> EmtModelTemplate: """ Materialize one shipped catalog-derived template on demand. :param descriptor: Template descriptor. :param var_factory: Variable factory for the new instance. :param name: Optional explicit instance name. :returns: Materialized EMT template. """ template_builder: BasicBlockCatalogTemplateBuilder = _resolve_template_builder_from_descriptor(descriptor) template: EmtModelTemplate = template_builder(var_factory, name) return template