1

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.

2
  • 1
    What exactly is it that you are trying to avoid? Commented Jun 26 at 15:30
  • i try to avoid that users have to install all dependencies of the subpackages, as they are quite different. But i still want to allow for typecheckers to succeed even if some subpackage does not have its dependencies installed. In addition I want to be able to run scripts using a subpackage, without caring about exceptions that arise from missing dependencies in another subpackage. Commented Jul 1 at 15:31

2 Answers 2

1

If you want a library or module to be imported only for type hinting reasons, use typing.TYPE_CHECKING as a condition before import and add the type hints as strings.

import typing

if typing.TYPE_CHECKING:
    from submodule import whatever

def example_use(param: 'whatever.type') -> 'whatever.other_type':
    return param.process()

your IDE will provide intellisense, but since the import does not happen (TYPE_CHECKING is a constant False), there won't be any import errors.

This won't work of course if you need the import to happen and you want to use the dependency.

If you want to use dynamic imports, you can use the importlib module for that. Also you can do crazy on-demand installs by the pip package, like this: https://stackoverflow.com/a/55188705/8375783 I'm not saying that it is a good idea, but it is possible to do things like that.

Sign up to request clarification or add additional context in comments.

3 Comments

Why did you end up with the conclusion that is for type checking reason?
My reasoning is that example code in documentation doesn't need to be runnable in place. Its main purpose is to be a reference for the user. Using TYPE_CHECKING achieves this cleanly: it provides deterministic behavior by guaranteeing the import is skipped at runtime. This avoids any ImportError by design, making a try/except block unnecessary, while still enabling full IDE/tooling support.
Yes the crazy installing method I dont like but Ill check your suggestion, and come back to this.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.