Source code for pyiron_snippets.retrieve

"""
Helper functions for managing the relationship between strings and imports.
"""

from __future__ import annotations

import importlib


[docs] class StringNotImportableError(ImportError): ...
[docs] def import_from_string(library_path: str) -> object: """ Import an object using a string of its python library location. Args: library_path (str): The full module path to the desired object. Returns: (object): The imported object. Example: >>> from pyiron_snippets import retrieve >>> ThreadPoolExecutor = retrieve.import_from_string( ... "concurrent.futures.ThreadPoolExecutor" ... ) >>> with ThreadPoolExecutor(max_workers=2) as executor: ... future = executor.submit(pow, 2, 3) ... print(future.result()) 8 """ if (not isinstance(library_path, str)) or len(library_path) == 0: raise ValueError( f"Expected a non-empty string, got '{library_path}' of type {type(library_path)} instead." ) split_path = library_path.split(".", 1) if len(split_path) == 1: module_name, path = split_path[0], "" else: module_name, path = split_path try: obj = importlib.import_module(module_name) except ModuleNotFoundError as e: raise ModuleNotFoundError( f"The topmost entry of {library_path} could not be found. The most likely " f"causes of this problem are a typo, or that the module is not yet in your " f"system's PYTHONPATH. The latter can be checked from inside python with " f"`import sys; print(sys.path)`." ) from e for k in path.split("."): if k == "": break try: obj = getattr(obj, k) except AttributeError: # Try importing as a submodule # This can be necessary of an __init__.py is empty and nothing else has # referenced the module yet current_path = f"{obj.__name__}.{k}" obj = importlib.import_module(current_path) return obj
[docs] def get_importable_string_from_string_reduction( string_reduction: str, reduced_object: object ) -> str: """ Per the pickle docs: > If a string is returned, the string should be interpreted as the name of a global variable. It should be the object’s local name relative to its module; the pickle module searches the module namespace to determine the object’s module. This behaviour is typically useful for singletons. To then import such an object from a non-local caller, we try scoping the string with the module of the object which returned it. """ try: import_from_string(string_reduction) importable = string_reduction except ModuleNotFoundError: importable = reduced_object.__module__ + "." + string_reduction try: import_from_string(importable) except (ModuleNotFoundError, AttributeError) as e: raise StringNotImportableError( f"Couldn't import {string_reduction} after scoping it as {importable}. " f"Please contact the developers so we can figure out how to handle " f"this edge case." ) from e return importable