gateway/modules/services/serviceGeneration/renderers/registry.py

154 lines
5.9 KiB
Python

"""
Renderer registry for automatic discovery and registration of renderers.
"""
import logging
import importlib
from typing import Dict, Type, List, Optional
from .rendererBaseTemplate import BaseRenderer
logger = logging.getLogger(__name__)
class RendererRegistry:
"""Registry for automatic renderer discovery and management."""
def __init__(self):
self._renderers: Dict[str, Type[BaseRenderer]] = {}
self._format_mappings: Dict[str, str] = {}
self._discovered = False
def discoverRenderers(self) -> None:
"""Automatically discover and register all renderers by scanning files."""
if self._discovered:
return
try:
import os
import sys
from pathlib import Path
# Get the directory containing this registry file
currentDir = Path(__file__).parent
renderersDir = currentDir
# Get the package name dynamically
packageName = __name__.rsplit('.', 1)[0]
# Scan all Python files in the renderers directory
for filePath in renderersDir.glob("*.py"):
if filePath.name in ['registry.py', 'rendererBaseTemplate.py', '__init__.py']:
continue
# Extract module name from filename
moduleName = filePath.stem
try:
# Import the module dynamically
fullModuleName = f"{packageName}.{moduleName}"
module = importlib.import_module(fullModuleName)
# Look for renderer classes in the module
for attrName in dir(module):
attr = getattr(module, attrName)
if (isinstance(attr, type) and
issubclass(attr, BaseRenderer) and
attr != BaseRenderer and
hasattr(attr, 'getSupportedFormats')):
# Register the renderer
self._registerRendererClass(attr)
except Exception as e:
logger.warning(f"Could not load renderer from {moduleName}: {str(e)}")
continue
self._discovered = True
except Exception as e:
logger.error(f"Error during renderer discovery: {str(e)}")
self._discovered = True # Mark as discovered to avoid repeated attempts
def _registerRendererClass(self, rendererClass: Type[BaseRenderer]) -> None:
"""Register a renderer class with its supported formats."""
try:
# Get supported formats from the renderer class
supportedFormats = rendererClass.getSupportedFormats()
for formatName in supportedFormats:
# Register primary format
self._renderers[formatName.lower()] = rendererClass
# Register aliases if any
if hasattr(rendererClass, 'getFormatAliases'):
aliases = rendererClass.getFormatAliases()
for alias in aliases:
self._format_mappings[alias.lower()] = formatName.lower()
logger.debug(f"Registered {rendererClass.__name__} for formats: {supportedFormats}")
except Exception as e:
logger.error(f"Error registering renderer {rendererClass.__name__}: {str(e)}")
def getRenderer(self, outputFormat: str, services=None) -> Optional[BaseRenderer]:
"""Get a renderer instance for the specified format."""
if not self._discovered:
self.discoverRenderers()
# Normalize format name
formatName = outputFormat.lower().strip()
# Check for aliases first
if formatName in self._format_mappings:
formatName = self._format_mappings[formatName]
# Get renderer class
rendererClass = self._renderers.get(formatName)
if rendererClass:
try:
return rendererClass(services=services)
except Exception as e:
logger.error(f"Error creating renderer instance for {formatName}: {str(e)}")
return None
logger.warning(f"No renderer found for format: {outputFormat}")
return None
def getSupportedFormats(self) -> List[str]:
"""Get list of all supported formats."""
if not self._discovered:
self.discoverRenderers()
formats = list(self._renderers.keys())
formats.extend(self._format_mappings.keys())
return sorted(set(formats))
def getRendererInfo(self) -> Dict[str, Dict[str, str]]:
"""Get information about all registered renderers."""
if not self._discovered:
self.discoverRenderers()
info = {}
for formatName, rendererClass in self._renderers.items():
info[formatName] = {
'class_name': rendererClass.__name__,
'module': rendererClass.__module__,
'description': getattr(rendererClass, '__doc__', 'No description').strip().split('\n')[0] if rendererClass.__doc__ else 'No description'
}
return info
# Global registry instance
_registry = RendererRegistry()
def getRenderer(outputFormat: str, services=None) -> Optional[BaseRenderer]:
"""Get a renderer instance for the specified format."""
return _registry.getRenderer(outputFormat, services)
def getSupportedFormats() -> List[str]:
"""Get list of all supported formats."""
return _registry.getSupportedFormats()
def getRendererInfo() -> Dict[str, Dict[str, str]]:
"""Get information about all registered renderers."""
return _registry.getRendererInfo()