# Path: microcosm_pubsub/chain/chain.py
# class Chain:
#     """
#     Structure that contains callable functions that shares the same context.
# 
#     """
#     def __init__(self, *args):
#         """
#         Build a chain that can be resolved multiple times
#         :param *args: callable functions, Chain statemnts or other Chains
# 
#         """
#         self.links = list(args)
# 
#     @property
#     def context_decorators(self):
#         """
#         Decorators to apply to the chain links
# 
#         """
#         return [
#             # Order matter - get_from_context should be first
#             get_from_context,
#             temporarily_replace_context_keys,
#             save_to_context,
#             save_to_context_by_func_name,
#         ]
# 
#     @property
#     def context_decorators_assigned(self):
#         """
#         Tuple naming attributes that self.context_decorators attributes should keep
#         It should contain all attributes that set by relevant decorators
#         (such as @extracts)
# 
#         """
#         return DEFAULT_ASSIGNED
# 
#     @property
#     def new_context_type(self):
#         """
#         Context type to create
# 
#         """
#         return SafeContext
# 
#     def __call__(self, context=None, **kwargs):
#         """
#         Resolve the chain and return the last chain function result
#         :param context: use existing context instead of creating a new one
#         :param **kwargs: initialize the context with some values
# 
#         """
#         context = context or self.new_context_type()
#         context.update(kwargs)
# 
#         res = None
# 
#         for link in self.links:
#             func = self.apply_decorators(context, link)
#             res = func()
# 
#         return res
# 
#     def __len__(self):
#         return len(self.links)
# 
#     def apply_decorators(self, context, link):
#         decorated_link = link
#         for decorator in self.context_decorators:
#             decorated_link = decorator(context, decorated_link, self.context_decorators_assigned)
#         return decorated_link
# 
#     @classmethod
#     def make(cls, *args, chain=None, links=None, **kwargs):
#         if chain is not None:
#             return chain
#         elif links is not None:
#             return cls(*links)
#         elif args:
#             return cls(*args)
#         else:
#             raise ValueError("Must define one of 'chain', 'links', or '*args'")
# 
# Path: microcosm_pubsub/chain/exceptions.py
# class AttributeNotFound(Exception):
#     def __init__(self, parent, attribute):
#         super().__init__(f"Failed to find attribute `{attribute}` on `{parent}`")
# 
# Path: microcosm_pubsub/chain/statements/assign.py
# def assign(this):
#     return AssignStatement(Reference(this))
# 
# def assign_constant(this):
#     return AssignStatement(Constant(this))
# 
# def assign_function(this):
#     return AssignStatement(Function(this))
# 
# def extract(name, key, key_property=None):
#     """
#     Extract an argument from a context to another context key
# 
#     :param name: new context key
#     :param key: old context key
#     :param key_property: propery of the context key
# 
#     """
#     if key_property:
#         key = ".".join([key, key_property])
# 
#     return AssignStatement(Reference(key), name)


from hamcrest import (
    assert_that,
    calling,
    equal_to,
    is_,
    raises,
)
from microcosm_pubsub.chain import Chain
from microcosm_pubsub.chain.exceptions import AttributeNotFound
from microcosm_pubsub.chain.statements import (
    assign,
    assign_constant,
    assign_function,
    extract,
)
        assign_function(func).to("param"),
    )
    assert_that(
        chain(),
        is_(equal_to("value")),
    )


def test_assign_function_with_noargs():
    chain = Chain(
        assign_function(lambda: dict()).to("param"),
    )
    assert_that(
        chain(),
        is_(equal_to(dict())),
    )


def test_assign_function_with_builtin():
    chain = Chain(
        assign_function(dict).to("param"),
    )
    assert_that(
        chain(),
        is_(equal_to(dict())),
    )


def test_assign_missing_property():
    chain = Chain(

# Path: microcosm_pubsub/chain/chain.py
# class Chain:
#     """
#     Structure that contains callable functions that shares the same context.
# 
#     """
#     def __init__(self, *args):
#         """
#         Build a chain that can be resolved multiple times
#         :param *args: callable functions, Chain statemnts or other Chains
# 
#         """
#         self.links = list(args)
# 
#     @property
#     def context_decorators(self):
#         """
#         Decorators to apply to the chain links
# 
#         """
#         return [
#             # Order matter - get_from_context should be first
#             get_from_context,
#             temporarily_replace_context_keys,
#             save_to_context,
#             save_to_context_by_func_name,
#         ]
# 
#     @property
#     def context_decorators_assigned(self):
#         """
#         Tuple naming attributes that self.context_decorators attributes should keep
#         It should contain all attributes that set by relevant decorators
#         (such as @extracts)
# 
#         """
#         return DEFAULT_ASSIGNED
# 
#     @property
#     def new_context_type(self):
#         """
#         Context type to create
# 
#         """
#         return SafeContext
# 
#     def __call__(self, context=None, **kwargs):
#         """
#         Resolve the chain and return the last chain function result
#         :param context: use existing context instead of creating a new one
#         :param **kwargs: initialize the context with some values
# 
#         """
#         context = context or self.new_context_type()
#         context.update(kwargs)
# 
#         res = None
# 
#         for link in self.links:
#             func = self.apply_decorators(context, link)
#             res = func()
# 
#         return res
# 
#     def __len__(self):
#         return len(self.links)
# 
#     def apply_decorators(self, context, link):
#         decorated_link = link
#         for decorator in self.context_decorators:
#             decorated_link = decorator(context, decorated_link, self.context_decorators_assigned)
#         return decorated_link
# 
#     @classmethod
#     def make(cls, *args, chain=None, links=None, **kwargs):
#         if chain is not None:
#             return chain
#         elif links is not None:
#             return cls(*links)
#         elif args:
#             return cls(*args)
#         else:
#             raise ValueError("Must define one of 'chain', 'links', or '*args'")
# 
# Path: microcosm_pubsub/chain/exceptions.py
# class AttributeNotFound(Exception):
#     def __init__(self, parent, attribute):
#         super().__init__(f"Failed to find attribute `{attribute}` on `{parent}`")
# 
# Path: microcosm_pubsub/chain/statements/assign.py
# def assign(this):
#     return AssignStatement(Reference(this))
# 
# def assign_constant(this):
#     return AssignStatement(Constant(this))
# 
# def assign_function(this):
#     return AssignStatement(Function(this))
# 
# def extract(name, key, key_property=None):
#     """
#     Extract an argument from a context to another context key
# 
#     :param name: new context key
#     :param key: old context key
#     :param key_property: propery of the context key
# 
#     """
#     if key_property:
#         key = ".".join([key, key_property])
# 
#     return AssignStatement(Reference(key), name)


# Path: microcosm_pubsub/tests/chain/statements/test_assign.py
from hamcrest import (
    assert_that,
    calling,
    equal_to,
    is_,
    raises,
)
from microcosm_pubsub.chain import Chain
from microcosm_pubsub.chain.exceptions import AttributeNotFound
from microcosm_pubsub.chain.statements import (
    assign,
    assign_constant,
    assign_function,
    extract,
)

        assign_function(func).to("param"),
    )
    assert_that(
        chain(),
        is_(equal_to("value")),
    )


def test_assign_function_with_noargs():
    chain = Chain(
        assign_function(lambda: dict()).to("param"),
    )
    assert_that(
        chain(),
        is_(equal_to(dict())),
    )


def test_assign_function_with_builtin():
    chain = Chain(
        assign_function(dict).to("param"),
    )
    assert_that(
        chain(),
        is_(equal_to(dict())),
    )


def test_assign_missing_property():
    chain = Chain(

        assign("arg.missing").to("param"),