pythonexceptionpython-import

Handling Missing Dependencies Gracefully in Python Modules


I'm currently working on a modular python framework. The current dilemma is, that one module has a set of submodules that have vastly different dependencies, and a user usually would only use one of those based on his choice. As I want to include examples for all, I also import those modules in some example files outside of the project structure.

The question I'm having is the following: Python will throw exceptions as for submodule_Y as some libraries are missing (and we do not want to install them). Is there a best way to avoid this issue?

my current approach in the __init__.py of the parent module is the following:

try:
    import submodule_Y
except:
    pass

This is not good practice, so I was wondering what would be the optimal way to ignore exceptions if a certain submodule is not wanted by the user.


Solution

  • In the end I resorted to a utility function that handles imports with missing dependencies as follows:

    import warnings
    
    
    def optional_import(module_path:str, symbol_name: str) -> object:
        """
        Handles optional imports if dependencies are not installed.
    
        :param module_path: The module path to use for import.
        :param symbol_name: The symbol name to import.
        :returns: The imported symbol or a dummy object if an ImportError is raised.
        """
        try:
            module = __import__(module_path, fromlist=[symbol_name])
        except ModuleNotFoundError as e:
            warnings.warn(f"Could not import {symbol_name} from {module_path} due to {e}. Will use dummy object instead.", ImportWarning)
    
            class Dummy:
                """A dummy class for imports that fail."""
                def __init__(self, *args, **kwargs) -> None:
                    """
                    Initialize the dummy class.
    
                    :param args: The args to pass to the class.
                    :param kwargs: The kwargs to pass to the class.
                    :raises ImportError: Always raised.
                    """
                    raise ImportError(f"Could not import {symbol_name}, it requires {e.name} to be installed.")
            return Dummy
        return getattr(module, symbol_name)
    

    This allows me to import submodules easily and get a warning if some dependencies are missing. If i then use the imported module and initialize Objects an ImportError is raised.