NCAkit / core /module_registry.py
ismdrobiul489's picture
Add step-by-step numbered logging for HF debugging
5134951
"""
Module Registry for NCAkit
Handles automatic discovery and registration of feature modules.
"""
import importlib
import pkgutil
import logging
from pathlib import Path
from typing import List, Dict, Any, Callable, Optional
from fastapi import FastAPI
logger = logging.getLogger(__name__)
class ModuleInfo:
"""Information about a registered module"""
def __init__(
self,
name: str,
prefix: str,
description: str = "",
register_fn: Callable = None
):
self.name = name
self.prefix = prefix
self.description = description
self.register_fn = register_fn
class ModuleRegistry:
"""
Centralized registry for all NCAkit modules.
Each module must have an __init__.py with:
- MODULE_NAME: str
- MODULE_PREFIX: str
- MODULE_DESCRIPTION: str (optional)
- register(app, config): function
"""
def __init__(self):
self._modules: Dict[str, ModuleInfo] = {}
self._initialized: bool = False
def discover_modules(self, modules_package: str = "modules") -> List[str]:
"""
Discover all available modules in the modules package.
Returns list of module names.
"""
discovered = []
try:
logger.info(f"Discovering modules in package: {modules_package}")
package = importlib.import_module(modules_package)
package_path = Path(package.__file__).parent
logger.info(f"Module package path: {package_path}")
for finder, name, is_pkg in pkgutil.iter_modules([str(package_path)]):
logger.info(f"Found: {name} (is_package={is_pkg})")
# Skip private/template modules
if name.startswith('_'):
logger.info(f"Skipping private module: {name}")
continue
if is_pkg:
discovered.append(name)
logger.info(f"Discovered module: {name}")
except Exception as e:
logger.error(f"Error discovering modules: {e}")
import traceback
logger.error(traceback.format_exc())
logger.info(f"Total discovered: {discovered}")
return discovered
def load_module(self, module_name: str, modules_package: str = "modules") -> Optional[ModuleInfo]:
"""Load a single module and return its info"""
logger.info(f"[STEP 4] Loading module: {module_name}")
try:
full_module_name = f"{modules_package}.{module_name}"
logger.info(f"[STEP 4.1] Importing: {full_module_name}")
module = importlib.import_module(full_module_name)
logger.info(f"[STEP 4.2] Import successful: {full_module_name}")
# Check required attributes
if not hasattr(module, 'register'):
logger.warning(f"[STEP 4.3] Module {module_name} has no register function, skipping")
return None
logger.info(f"[STEP 4.3] register() function found")
# Get module metadata
name = getattr(module, 'MODULE_NAME', module_name)
prefix = getattr(module, 'MODULE_PREFIX', f"/api/{module_name}")
description = getattr(module, 'MODULE_DESCRIPTION', "")
logger.info(f"[STEP 4.4] Module metadata: name={name}, prefix={prefix}")
info = ModuleInfo(
name=name,
prefix=prefix,
description=description,
register_fn=module.register
)
self._modules[name] = info
logger.info(f"[STEP 4.5] Loaded module: {name} (prefix: {prefix})")
return info
except Exception as e:
logger.error(f"[STEP 4.ERROR] Failed to load module {module_name}: {e}")
import traceback
logger.error(f"[STEP 4.TRACEBACK]\n{traceback.format_exc()}")
return None
def register_all(self, app: FastAPI, config: Any) -> int:
"""
Register all discovered modules with the FastAPI app.
Returns number of successfully registered modules.
"""
if self._initialized:
logger.warning("Modules already initialized")
return len(self._modules)
# Discover modules
module_names = self.discover_modules()
registered = 0
for name in module_names:
info = self.load_module(name)
if info and info.register_fn:
try:
info.register_fn(app, config)
registered += 1
logger.info(f"Registered module: {info.name}")
except Exception as e:
logger.error(f"Failed to register module {name}: {e}")
self._initialized = True
logger.info(f"Registered {registered}/{len(module_names)} modules")
return registered
def get_module(self, name: str) -> Optional[ModuleInfo]:
"""Get info about a specific module"""
return self._modules.get(name)
def list_modules(self) -> List[Dict[str, str]]:
"""List all registered modules"""
return [
{
"name": info.name,
"prefix": info.prefix,
"description": info.description
}
for info in self._modules.values()
]
# Global registry instance
registry = ModuleRegistry()