# -*- coding: utf-8 -*-
"""
    volt.utils
    ~~~~~~~~~~

    General utility functions.

"""
# (c) 2012-2017 Wibowo Arindrarto <bow@bow.web.id>
import sys
import importlib.util as iutil
from collections import namedtuple, Mapping
from os import path


# Helper tuple for containing success or failure results.
class Result(namedtuple("Result", ["result", "errors"])):

    @classmethod
    def as_success(cls, success_value):
        """Returns the success value, with the error value set to an empty
        list."""
        return cls(success_value, [])

    @classmethod
    def as_failure(cls, failure_message):
        """Returns the error value in a list, with the success value set to
        None."""
        msg = [failure_message] if isinstance(failure_message, str) else \
            failure_message
        return cls(None, msg)


def import_mod_attr(target):
    """Imports the attribute of a module given its string path."""
    try:
        mod_name, cls_name = target.replace(":", ".").rsplit(".", 1)
    except ValueError:
        return Result.as_failure("invalid module attribute import target:"
                                 f" {target!r}")

    if path.isfile(mod_name):
        return Result.as_failure("import from file is not yet supported")

    sys.path = [""] + sys.path if not sys.path[0] == "" else sys.path

    try:
        spec = iutil.find_spec(mod_name)
    except (ModuleNotFoundError, ValueError):
        spec = None
    if spec is None:
        return Result.as_failure(f"failed to find module {mod_name!r} for"
                                 " import")
    mod = iutil.module_from_spec(spec)
    spec.loader.exec_module(mod)

    try:
        return Result.as_success(getattr(mod, cls_name))
    except AttributeError:
        return Result.as_failure(f"module {mod_name!r} does not contain"
                                 f" attribute {cls_name!r}")


def lazyproperty(func):
    """Decorator for lazy property loading.

    This decorator adds a dictionary called ``_cached`` to the instance
    that owns the class it decorates.

    """
    attr_name = func.__name__

    @property
    def cached(self):
        if not hasattr(self, '_cached'):
            setattr(self, '_cached', {})
        try:
            return self._cached[attr_name]
        except KeyError:
            result = self._cached[attr_name] = func(self)
            return result

    return cached


def nested_update(one, other):
    """Update function that respects nested values.

    This is similar to Python's dict.update, except when the value to
    be updated is an instance of :class:`collections.Mapping`, the
    function will recurse.

    """
    for key, value in other.items():
        if isinstance(value, Mapping):
            nv = nested_update(one.get(key, {}), value)
            one[key] = nv
        else:
            one[key] = other[key]

    return one

@@ -215,25 +215,16 @@
 medtuple
-, Mapping
 %0Afrom os
@@ -2411,485 +2411,4 @@
 hed%0A
-%0A%0Adef nested_update(one, other):%0A    %22%22%22Update function that respects nested values.%0A%0A    This is similar to Python's dict.update, except when the value to%0A    be updated is an instance of :class:%60collections.Mapping%60, the%0A    function will recurse.%0A%0A    %22%22%22%0A    for key, value in other.items():%0A        if isinstance(value, Mapping):%0A            nv = nested_update(one.get(key, %7B%7D), value)%0A            one%5Bkey%5D = nv%0A        else:%0A            one%5Bkey%5D = other%5Bkey%5D%0A%0A    return one%0A

Remove unused nested_update