gateway/modules/services/serviceGeneration/renderers/registry.py
2025-10-29 23:40:39 +01:00

154 lines
6 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 discover_renderers(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
current_dir = Path(__file__).parent
renderers_dir = current_dir
# Get the package name dynamically
package_name = __name__.rsplit('.', 1)[0]
# Scan all Python files in the renderers directory
for file_path in renderers_dir.glob("*.py"):
if file_path.name in ['registry.py', 'rendererBaseTemplate.py', '__init__.py']:
continue
# Extract module name from filename
module_name = file_path.stem
try:
# Import the module dynamically
full_module_name = f"{package_name}.{module_name}"
module = importlib.import_module(full_module_name)
# Look for renderer classes in the module
for attr_name in dir(module):
attr = getattr(module, attr_name)
if (isinstance(attr, type) and
issubclass(attr, BaseRenderer) and
attr != BaseRenderer and
hasattr(attr, 'get_supported_formats')):
# Register the renderer
self._register_renderer_class(attr)
except Exception as e:
logger.warning(f"Could not load renderer from {module_name}: {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 _register_renderer_class(self, renderer_class: Type[BaseRenderer]) -> None:
"""Register a renderer class with its supported formats."""
try:
# Get supported formats from the renderer class
supported_formats = renderer_class.get_supported_formats()
for format_name in supported_formats:
# Register primary format
self._renderers[format_name.lower()] = renderer_class
# Register aliases if any
if hasattr(renderer_class, 'get_format_aliases'):
aliases = renderer_class.get_format_aliases()
for alias in aliases:
self._format_mappings[alias.lower()] = format_name.lower()
logger.debug(f"Registered {renderer_class.__name__} for formats: {supported_formats}")
except Exception as e:
logger.error(f"Error registering renderer {renderer_class.__name__}: {str(e)}")
def get_renderer(self, output_format: str, services=None) -> Optional[BaseRenderer]:
"""Get a renderer instance for the specified format."""
if not self._discovered:
self.discover_renderers()
# Normalize format name
format_name = output_format.lower().strip()
# Check for aliases first
if format_name in self._format_mappings:
format_name = self._format_mappings[format_name]
# Get renderer class
renderer_class = self._renderers.get(format_name)
if renderer_class:
try:
return renderer_class(services=services)
except Exception as e:
logger.error(f"Error creating renderer instance for {format_name}: {str(e)}")
return None
logger.warning(f"No renderer found for format: {output_format}")
return None
def get_supported_formats(self) -> List[str]:
"""Get list of all supported formats."""
if not self._discovered:
self.discover_renderers()
formats = list(self._renderers.keys())
formats.extend(self._format_mappings.keys())
return sorted(set(formats))
def get_renderer_info(self) -> Dict[str, Dict[str, str]]:
"""Get information about all registered renderers."""
if not self._discovered:
self.discover_renderers()
info = {}
for format_name, renderer_class in self._renderers.items():
info[format_name] = {
'class_name': renderer_class.__name__,
'module': renderer_class.__module__,
'description': getattr(renderer_class, '__doc__', 'No description').strip().split('\n')[0] if renderer_class.__doc__ else 'No description'
}
return info
# Global registry instance
_registry = RendererRegistry()
def get_renderer(output_format: str, services=None) -> Optional[BaseRenderer]:
"""Get a renderer instance for the specified format."""
return _registry.get_renderer(output_format, services)
def get_supported_formats() -> List[str]:
"""Get list of all supported formats."""
return _registry.get_supported_formats()
def get_renderer_info() -> Dict[str, Dict[str, str]]:
"""Get information about all registered renderers."""
return _registry.get_renderer_info()