1"""Static runtime registration for the vendored BlockDiag family.23The upstream distributions discover drawers, node renderers, and plugins via4``importlib.metadata`` entry points. The Python Worker bundles source files,5not distribution metadata, so relying on that discovery makes the renderer6work locally and fail in the Worker. This module is the small, explicit7replacement for that packaging convention.89Only the SVG drawer, the built-in node renderers, and the three built-in10plugins shipped by this family are installed here. No third-party plugin11code is loaded.12"""1314from __future__ import annotations1516from importlib import import_module17from types import ModuleType18from typing import Iterable1920from blockdiag import plugins212223# Keep this list explicit. It is also the audit list for the upstream24# blockdiag_noderenderer entry points; adding a renderer requires a deliberate25# source and compatibility review rather than importing arbitrary packages.26_NODE_RENDERER_MODULES = (27 "blockdiag.noderenderer.actor",28 "blockdiag.noderenderer.beginpoint",29 "blockdiag.noderenderer.box",30 "blockdiag.noderenderer.circle",31 "blockdiag.noderenderer.cloud",32 "blockdiag.noderenderer.diamond",33 "blockdiag.noderenderer.dots",34 "blockdiag.noderenderer.ellipse",35 "blockdiag.noderenderer.endpoint",36 "blockdiag.noderenderer.flowchart.database",37 "blockdiag.noderenderer.flowchart.input",38 "blockdiag.noderenderer.flowchart.loopin",39 "blockdiag.noderenderer.flowchart.loopout",40 "blockdiag.noderenderer.flowchart.terminator",41 "blockdiag.noderenderer.mail",42 "blockdiag.noderenderer.minidiamond",43 "blockdiag.noderenderer.none",44 "blockdiag.noderenderer.note",45 "blockdiag.noderenderer.roundedbox",46 "blockdiag.noderenderer.square",47 "blockdiag.noderenderer.textbox",48 "packetdiag.noderenderers",49)5051_PLUGIN_MODULES = {52 "attributes": "blockdiag.plugins.attributes",53 "autoclass": "blockdiag.plugins.autoclass",54 "autolane": "actdiag.plugins.autolane",55}5657def _load_static_plugins(names: Iterable[str], diagram, **kwargs) -> None:58 """Load only the allowlisted built-in plugins.5960 This has the same public contract as ``blockdiag.plugins.load`` while61 deliberately omitting entry-point discovery. The upstream plugin module62 is still responsible for installing its handlers into the shared plugin63 registry.64 """6566 for name in names:67 if name in plugins.loaded_plugins:68 # Match upstream behavior: a repeated plugin request is ignored.69 return7071 module_name = _PLUGIN_MODULES.get(name)72 if module_name is None:73 raise AttributeError("unknown plugin: %s" % name)7475 module = import_module(module_name)76 plugins.loaded_plugins.append(name)77 if hasattr(module, "setup"):78 module.setup(module, diagram, **kwargs)798081def _install_module_setup(module_name: str) -> ModuleType:82 module = import_module(module_name)83 setup = getattr(module, "setup", None)84 if setup is None:85 raise RuntimeError("vendored runtime module has no setup(): %s" % module_name)86 setup(module)87 return module888990def register() -> None:91 """Install the vendored SVG drawer, renderers, and plugin loader.9293 Registration is process-wide because the vendored upstream APIs expose94 process-wide registries. The operation is idempotent, making it safe for95 the Worker entrypoint and tests to call more than once.96 """9798 # ``SVGImageDraw.setup`` calls blockdiag.imagedraw.install_imagedrawer.99 _install_module_setup("blockdiag.imagedraw.svg")100101 for module_name in _NODE_RENDERER_MODULES:102 _install_module_setup(module_name)103104 # Replace the upstream metadata-backed loader before any diagram can105 # process a ``plugin`` statement.106 plugins.load = _load_static_plugins107108109# A descriptive alias for callers that prefer an imperative name.110install = register111112113__all__ = ["install", "register"]