| from importlib import import_module |
| from typing import TypeVar |
|
|
| T = TypeVar("T") |
|
|
|
|
| def resolve_variable[T]( |
| variable_path: str, |
| expected_type: type[T] | tuple[type, ...] | None = None, |
| ) -> T: |
| """Resolve a variable from a path. |
| |
| Args: |
| variable_path: The path to the variable (e.g. "parent_package_name.sub_package_name.module_name:variable_name"). |
| expected_type: Optional type or tuple of types to validate the resolved variable against. |
| If provided, uses isinstance() to check if the variable is an instance of the expected type(s). |
| |
| Returns: |
| The resolved variable. |
| |
| Raises: |
| ImportError: If the module path is invalid or the attribute doesn't exist. |
| ValueError: If the resolved variable doesn't pass the validation checks. |
| """ |
| try: |
| module_path, variable_name = variable_path.rsplit(":", 1) |
| except ValueError as err: |
| raise ImportError(f"{variable_path} doesn't look like a variable path. Example: parent_package_name.sub_package_name.module_name:variable_name") from err |
|
|
| try: |
| module = import_module(module_path) |
| except ImportError as err: |
| raise ImportError(f"Could not import module {module_path}") from err |
|
|
| try: |
| variable = getattr(module, variable_name) |
| except AttributeError as err: |
| raise ImportError(f"Module {module_path} does not define a {variable_name} attribute/class") from err |
|
|
| |
| if expected_type is not None: |
| if not isinstance(variable, expected_type): |
| type_name = expected_type.__name__ if isinstance(expected_type, type) else " or ".join(t.__name__ for t in expected_type) |
| raise ValueError(f"{variable_path} is not an instance of {type_name}, got {type(variable).__name__}") |
|
|
| return variable |
|
|
|
|
| def resolve_class[T](class_path: str, base_class: type[T] | None = None) -> type[T]: |
| """Resolve a class from a module path and class name. |
| |
| Args: |
| class_path: The path to the class (e.g. "langchain_openai:ChatOpenAI"). |
| base_class: The base class to check if the resolved class is a subclass of. |
| |
| Returns: |
| The resolved class. |
| |
| Raises: |
| ImportError: If the module path is invalid or the attribute doesn't exist. |
| ValueError: If the resolved object is not a class or not a subclass of base_class. |
| """ |
| model_class = resolve_variable(class_path, expected_type=type) |
|
|
| if not isinstance(model_class, type): |
| raise ValueError(f"{class_path} is not a valid class") |
|
|
| if base_class is not None and not issubclass(model_class, base_class): |
| raise ValueError(f"{class_path} is not a subclass of {base_class.__name__}") |
|
|
| return model_class |
|
|