gateway/modules/services/serviceGeneration/renderers/registry.py
2025-10-03 01:41:50 +02:00

157 lines
6.2 KiB
Python

"""
Renderer registry for automatic discovery and registration of renderers.
"""
import logging
import importlib
import pkgutil
from typing import Dict, Type, List, Optional
from .base_renderer 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', 'base_renderer.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)
logger.info(f"Discovered renderer: {attr.__name__} from {module_name}")
except Exception as e:
logger.warning(f"Could not load renderer from {module_name}: {str(e)}")
continue
self._discovered = True
logger.info(f"Renderer discovery completed. Found {len(self._renderers)} renderers.")
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) -> 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()
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) -> Optional[BaseRenderer]:
"""Get a renderer instance for the specified format."""
return _registry.get_renderer(output_format)
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()