Source code for nv_ingest_api.util.imports.dynamic_resolvers

# SPDX-FileCopyrightText: Copyright (c) 2024-25, NVIDIA CORPORATION & AFFILIATES.
# All rights reserved.
# SPDX-License-Identifier: Apache-2.0

import importlib
import inspect
from typing import Callable, Union, List, Optional

from nv_ingest.framework.orchestration.ray.stages.meta.ray_actor_stage_base import RayActorStage


[docs] def resolve_obj_from_path(path: str, allowed_base_paths: Optional[List[str]] = None) -> object: """ Import and return an object from a string path of the form 'module.sub:attr'. To enhance security, this function can restrict imports to a list of allowed base module paths. """ if ":" not in path: raise ValueError(f"Invalid path '{path}': expected format 'module.sub:attr'") module_path, attr_name = path.split(":", 1) # Security check: only allow imports from specified base paths if provided. if allowed_base_paths: is_allowed = any(module_path == base or module_path.startswith(base + ".") for base in allowed_base_paths) if not is_allowed: raise ImportError( f"Module '{module_path}' is not in the list of allowed base paths. " f"Allowed paths: {allowed_base_paths}" ) try: mod = importlib.import_module(module_path) except ModuleNotFoundError as e: raise ImportError(f"Could not import module '{module_path}'") from e try: obj = getattr(mod, attr_name) except AttributeError as e: raise AttributeError(f"Module '{module_path}' has no attribute '{attr_name}'") from e return obj
[docs] def resolve_callable_from_path( callable_path: str, signature_schema: Union[List[str], Callable[[inspect.Signature], None], str], allowed_base_paths: Optional[List[str]] = None, ) -> Callable: """ Import and return a callable from a module path string like 'module.submodule:callable_name', and validate its signature using the required signature_schema (callable or path to callable). Parameters ---------- callable_path : str The module path and callable in the format 'module.sub:callable'. signature_schema : Union[List[str], Callable, str] Either: - A list of parameter names to require. - A callable that takes an inspect.Signature and raises on failure. - A string path to such a callable ('module.sub:schema_checker'). allowed_base_paths : Optional[List[str]] An optional list of base module paths from which imports are allowed. If provided, both the callable and any signature schema specified by path must reside within one of these paths. Returns ------- Callable The resolved and validated callable. Raises ------ ValueError If the path is not correctly formatted. ImportError If the module cannot be imported or is not in the allowed paths. AttributeError If the attribute does not exist in the module. TypeError If the resolved attribute is not callable or the signature does not match. """ obj = resolve_obj_from_path(callable_path, allowed_base_paths=allowed_base_paths) if not callable(obj): raise TypeError(f"Object '{callable_path}' is not callable") # Load/check signature_schema schema_checker = signature_schema if isinstance(signature_schema, str): # When loading the schema checker, apply the same security restrictions. schema_checker = resolve_obj_from_path(signature_schema, allowed_base_paths=allowed_base_paths) sig = inspect.signature(obj) if isinstance(schema_checker, list): actual_params = list(sig.parameters.keys()) missing = [p for p in schema_checker if p not in actual_params] if missing: raise TypeError( f"Callable at '{callable_path}' is missing required parameters: {missing}\n" f"Actual parameters: {actual_params}" ) elif callable(schema_checker): try: schema_checker(sig) except Exception as e: raise TypeError(f"Signature validation for '{callable_path}' failed: {e}") from e else: raise TypeError(f"Invalid signature_schema: expected list, callable, or str, got {type(signature_schema)}") return obj
[docs] def resolve_actor_class_from_path( path: str, expected_base_class: type, allowed_base_paths: Optional[List[str]] = None ) -> type: """ Resolves an actor class from a path and validates that it is a class that inherits from the expected base class. This function correctly handles decorated Ray actors by inspecting their original class. Parameters ---------- path : str The full import path to the actor class. expected_base_class : type The base class that the resolved class must inherit from. allowed_base_paths : Optional[List[str]] An optional list of base module paths from which imports are allowed. Returns ------- type The resolved actor class (or Ray actor factory). """ obj = resolve_obj_from_path(path, allowed_base_paths=allowed_base_paths) # Determine the class to validate. If it's a Ray actor factory, we need to # inspect its MRO to find the original user-defined class. cls_to_validate = None if inspect.isclass(obj): cls_to_validate = obj else: # For actor factories, find the base class in the MRO that inherits from RayActorStage for base in obj.__class__.__mro__: if inspect.isclass(base) and issubclass(base, RayActorStage) and base is not RayActorStage: cls_to_validate = base break if cls_to_validate is None: raise TypeError( f"Could not resolve a valid actor class from path '{path}'. " f"The object is not a class and not a recognized actor factory." ) if not issubclass(cls_to_validate, expected_base_class): raise TypeError( f"Actor class '{cls_to_validate.__name__}' at '{path}' must inherit from '{expected_base_class.__name__}'." ) return obj