from contextlib import suppress
import importlib
from functools import reduce


def load_dotted_object(dotted_name):
    """Returns the Python object found at the `dotted_name`.

    `dotted_name` should include both the Python module as well as the object in
    the module to return. For example, `some.module.MyClass` will return the
    class object `MyClass` from the Python module `some.module`. If the object
    cannot be loaded, `ImportError` is raised.
    """

    def doit(mod_name, attributes=None):
        attributes = [] if attributes is None else attributes

        if not mod_name:
            raise ImportError(f"cannot import '{dotted_name}'")

        mod = None
        with suppress(ModuleNotFoundError):
            mod = importlib.import_module(mod_name)

        if not mod:
            mod_name, _, attr = mod_name.rpartition(".")
            attributes.append(attr)
            return doit(mod_name, attributes)

        attributes.reverse()
        obj = reduce(lambda a, p: getattr(a, p, {}), attributes, mod)
        if not obj:
            raise ImportError(
                f"module '{mod_name}' does not contain '{'.'.join(attributes)}'"
            )

        return obj

    return doit(dotted_name)