import collections
from importlib import import_module


def import_string(dotted_path):
    """
    Copied from Django's django.utils.module_loading.import_string to be able
    to work framework independently.

    Import a dotted module path and return the attribute/class designated by the
    last name in the path. Raise ImportError if the import failed.
    """
    try:
        module_path, class_name = dotted_path.rsplit(".", 1)
    except ValueError as err:  # pragma: no cover
        raise ImportError("%s doesn't look like a module path" % dotted_path) from err

    module = import_module(module_path)

    try:
        return getattr(module, class_name)
    except AttributeError as err:  # pragma: no cover
        raise ImportError(
            'Module "%s" does not define a "%s" attribute/class'
            % (module_path, class_name)
        ) from err


def dict_merge(dct, merge_dct, add_keys=True):
    """
    Recursive dict merge. Inspired by :meth:``dict.update()``, instead of
    updating only top-level keys, dict_merge recurses down into dicts nested
    to an arbitrary depth, updating keys. The ``merge_dct`` is merged into
    ``dct``.

    This version will return a copy of the dictionary and leave the original
    arguments untouched.

    The optional argument ``add_keys``, determines whether keys which are
    present in ``merge_dict`` but not ``dct`` should be included in the
    new dict.

    :param dict dct: Dict onto which the merge is executed
    :param dict merge_dct: Dict which is merged into dct
    :param bool add_keys: whether to add new keys
    :returns: The updated dict
    :rtype: dict
    """
    dct = dct.copy()
    if not add_keys:  # pragma: no cover
        merge_dct = {k: merge_dct[k] for k in set(dct).intersection(set(merge_dct))}

    for k, v in merge_dct.items():
        if (
            k in dct
            and isinstance(dct[k], dict)
            and isinstance(merge_dct[k], collections.Mapping)
        ):
            dct[k] = dict_merge(dct[k], v, add_keys=add_keys)
        else:
            dct[k] = v

    return dct

import collections
from importlib import import_module


def import_string(dotted_path):
    """
    Copied from Django's django.utils.module_loading.import_string to be able
    to work framework independently.

    Import a dotted module path and return the attribute/class designated by the
    last name in the path. Raise ImportError if the import failed.
    """
    try:
        module_path, class_name = dotted_path.rsplit(".", 1)
    except ValueError as err:  # pragma: no cover
        raise ImportError("%s doesn't look like a module path" % dotted_path) from err

    module = import_module(module_path)

    try:
        return getattr(module, class_name)
    except AttributeError as err:  # pragma: no cover
        raise ImportError(
            'Module "%s" does not define a "%s" attribute/class'
            % (module_path, class_name)
        ) from err


def dict_merge(dct, merge_dct, add_keys=True):
    """
    Recursive dict merge. Inspired by :meth:``dict.update()``, instead of
    updating only top-level keys, dict_merge recurses down into dicts nested
    to an arbitrary depth, updating keys. The ``merge_dct`` is merged into
    ``dct``.

    This version will return a copy of the dictionary and leave the original
    arguments untouched.

    The optional argument ``add_keys``, determines whether keys which are
    present in ``merge_dict`` but not ``dct`` should be included in the
    new dict.

    :param dict dct: Dict onto which the merge is executed
    :param dict merge_dct: Dict which is merged into dct
    :param bool add_keys: whether to add new keys
    :returns: The updated dict
    :rtype: dict
    """
    dct = dct.copy()
    if not add_keys:  # pragma: no cover
        merge_dct = {k: merge_dct[k] for k in set(dct).intersection(set(merge_dct))}

    for k, v in merge_dct.items():
        if (
            k in dct
            and isinstance(dct[k], dict)
            and isinstance(merge_dct[k], collections.Mapping)
        ):
            dct[k] = dict_merge(dct[k], v, add_keys=add_keys)
        else:
            dct[k] = v

    return dct

Copied from Django's django.utils.module_loading.import_string to be able     to work framework independently.      Import a dotted module path and return the attribute/class designated by the     last name in the path. Raise ImportError if the import failed. # pragma: no cover # pragma: no cover Recursive dict merge. Inspired by :meth:``dict.update()``, instead of     updating only top-level keys, dict_merge recurses down into dicts nested     to an arbitrary depth, updating keys. The ``merge_dct`` is merged into     ``dct``.      This version will return a copy of the dictionary and leave the original     arguments untouched.      The optional argument ``add_keys``, determines whether keys which are     present in ``merge_dict`` but not ``dct`` should be included in the     new dict.      :param dict dct: Dict onto which the merge is executed     :param dict merge_dct: Dict which is merged into dct     :param bool add_keys: whether to add new keys     :returns: The updated dict     :rtype: dict # pragma: no cover