# Path: microcosm_pubsub/chain/decorators.py
# BINDS = "_binds"
# 
# EXTRACTS = "_extracts"
# 
# Path: microcosm_pubsub/chain/exceptions.py
# class ContextKeyNotFound(Exception):
#     def __init__(self, key_error: KeyError, func):
#         context_key, *_ = key_error.args
#         super().__init__(f"Failed to find context key `{context_key}` during evaluation of `{func}`")


from functools import WRAPPER_ASSIGNMENTS, wraps
from inspect import Parameter, Signature, signature
from microcosm_pubsub.chain.decorators import BINDS, EXTRACTS
from microcosm_pubsub.chain.exceptions import ContextKeyNotFound

def get_from_context(context, func, assigned=DEFAULT_ASSIGNED):
    """
    Decorate a function - pass to the function the relevant arguments
    from a context (dictionary) - based on the function arg names

    """
    positional_args = get_positional_args(func)

    @wraps(func, assigned=assigned + WRAPPER_ASSIGNMENTS)
    def decorate(*args, **kwargs):
        try:
            context_kwargs = {
                arg_name: (context[arg_name] if default is Signature.empty else context.get(arg_name, default))
                for arg_name, default in positional_args[len(args):]
                if arg_name not in kwargs
            }
        except KeyError as error:
            raise ContextKeyNotFound(error, func)

        return func(*args, **kwargs, **context_kwargs)
    return decorate


def save_to_context(context, func, assigned=DEFAULT_ASSIGNED):
    """
    Decorate a function - save to a context (dictionary) the function return value
    if the function is marked by @extracts decorator

    """

# Path: microcosm_pubsub/chain/decorators.py
# BINDS = "_binds"
# 
# EXTRACTS = "_extracts"
# 
# Path: microcosm_pubsub/chain/exceptions.py
# class ContextKeyNotFound(Exception):
#     def __init__(self, key_error: KeyError, func):
#         context_key, *_ = key_error.args
#         super().__init__(f"Failed to find context key `{context_key}` during evaluation of `{func}`")


# Path: microcosm_pubsub/chain/context_decorators.py
from functools import WRAPPER_ASSIGNMENTS, wraps
from inspect import Parameter, Signature, signature
from microcosm_pubsub.chain.decorators import BINDS, EXTRACTS
from microcosm_pubsub.chain.exceptions import ContextKeyNotFound


def get_from_context(context, func, assigned=DEFAULT_ASSIGNED):
    """
    Decorate a function - pass to the function the relevant arguments
    from a context (dictionary) - based on the function arg names

    """
    positional_args = get_positional_args(func)

    @wraps(func, assigned=assigned + WRAPPER_ASSIGNMENTS)
    def decorate(*args, **kwargs):
        try:
            context_kwargs = {
                arg_name: (context[arg_name] if default is Signature.empty else context.get(arg_name, default))
                for arg_name, default in positional_args[len(args):]
                if arg_name not in kwargs
            }
        except KeyError as error:
            raise ContextKeyNotFound(error, func)

        return func(*args, **kwargs, **context_kwargs)
    return decorate


def save_to_context(context, func, assigned=DEFAULT_ASSIGNED):
    """
    Decorate a function - save to a context (dictionary) the function return value
    if the function is marked by @extracts decorator

    """

    extracts = getattr(func, EXTRACTS, None)