# Path: soap/context/base.py
# class _Context(dict):
#     """
#     _Context, a dictionary subclass with dot syntax and snapshot support.
#     """
#     def __init__(self, dictionary=None, **kwargs):
#         super().__init__()
#         if dictionary:
#             kwargs.update(dictionary)
#         kwargs = {k: self._cast_dict(v) for k, v in kwargs.items()}
#         with self.no_invalidate_cache():
#             for k, v in kwargs.items():
#                 self.__setattr__(k, v)
# 
#     @contextmanager
#     def no_invalidate_cache(self):
#         super().__setattr__('_invalidate_cache', False)
#         yield
#         super().__delattr__('_invalidate_cache')
# 
#     def __setattr__(self, key, value):
#         hook = getattr(self, key + '_hook', lambda v: v)
#         value = hook(value)
#         if key != '_snapshot':
#             value = self._cast_dict(value)
#         old_value = self.get(key)
#         self[key] = value
#         if getattr(self, '_invalidate_cache', True):
#             if old_value != value:
#                 invalidate_cache()
# 
#     def __getattr__(self, key):
#         try:
#             return self[key]
#         except KeyError as e:
#             raise AttributeError(str(e))
# 
#     def __delattr__(self, key):
#         try:
#             del self[key]
#         except KeyError as e:
#             raise AttributeError(str(e))
# 
#     def _cast_dict(self, dictionary):
#         if not isinstance(dictionary, dict):
#             return dictionary
#         dictionary = {k: self._cast_dict(v) for k, v in dictionary.items()}
#         return self.__class__(dictionary)
# 
#     def update(self, dictionary=None, **kwargs):
#         dictionary = dict(dictionary or {}, **kwargs)
#         for k, v in dictionary.items():
#             setattr(self, k, v)
# 
#     def take_snapshot(self):
#         with self.no_invalidate_cache():
#             self._snapshot = copy.deepcopy(dict(self))
# 
#     def restore_snapshot(self):
#         if '_snapshot' not in self:
#             raise RestoreSnapshotError(
#                 'Cannot restore snapshot: no snapshot exists.')
#         snapshot = self._snapshot
#         self.update(snapshot)
#         for k in list(self.keys()):
#             if k not in snapshot:
#                 del self[k]
# 
#     @contextmanager
#     def local(self, dictionary=None, **kwargs):
#         """Withable local context.  """
#         self.take_snapshot()
#         self.update(dictionary, **kwargs)
#         yield
#         self.restore_snapshot()


import copy
import unittest
from soap.context.base import _Context
        with context.local(a=2):
            self.assertEqual(context.a, 2)
            context.e = 3
        self.assertEqual(context.a, 1)
        self.assertNotIn('e', context)
        context.c = {}
        with context.c.local(d=4):
            self.assertEqual(context.c.d, 4)
        self.assertFalse(context.c)

    def test_recursive_local(self):
        context = copy.deepcopy(self.context)
        with context.local(a=3):
            context.outter = True
            self.assertEqual(context.a, 3)
            with context.local(b=4):
                context.inner = True
                self.assertEqual(context.b, 4)
            self.assertNotIn('inner', context)
            self.assertTrue(context.outter)
            self.assertEqual(context.b, self.context.b)
        self.assertEqual(context, self.context)

    def test_chain_local(self):
        context = copy.deepcopy(self.context)
        with context.b.local(c=3):
            self.assertEqual(context.b.c, 3)
        self.assertEqual(context, self.context)

    def test_hook(self):

# Path: soap/context/base.py
# class _Context(dict):
#     """
#     _Context, a dictionary subclass with dot syntax and snapshot support.
#     """
#     def __init__(self, dictionary=None, **kwargs):
#         super().__init__()
#         if dictionary:
#             kwargs.update(dictionary)
#         kwargs = {k: self._cast_dict(v) for k, v in kwargs.items()}
#         with self.no_invalidate_cache():
#             for k, v in kwargs.items():
#                 self.__setattr__(k, v)
# 
#     @contextmanager
#     def no_invalidate_cache(self):
#         super().__setattr__('_invalidate_cache', False)
#         yield
#         super().__delattr__('_invalidate_cache')
# 
#     def __setattr__(self, key, value):
#         hook = getattr(self, key + '_hook', lambda v: v)
#         value = hook(value)
#         if key != '_snapshot':
#             value = self._cast_dict(value)
#         old_value = self.get(key)
#         self[key] = value
#         if getattr(self, '_invalidate_cache', True):
#             if old_value != value:
#                 invalidate_cache()
# 
#     def __getattr__(self, key):
#         try:
#             return self[key]
#         except KeyError as e:
#             raise AttributeError(str(e))
# 
#     def __delattr__(self, key):
#         try:
#             del self[key]
#         except KeyError as e:
#             raise AttributeError(str(e))
# 
#     def _cast_dict(self, dictionary):
#         if not isinstance(dictionary, dict):
#             return dictionary
#         dictionary = {k: self._cast_dict(v) for k, v in dictionary.items()}
#         return self.__class__(dictionary)
# 
#     def update(self, dictionary=None, **kwargs):
#         dictionary = dict(dictionary or {}, **kwargs)
#         for k, v in dictionary.items():
#             setattr(self, k, v)
# 
#     def take_snapshot(self):
#         with self.no_invalidate_cache():
#             self._snapshot = copy.deepcopy(dict(self))
# 
#     def restore_snapshot(self):
#         if '_snapshot' not in self:
#             raise RestoreSnapshotError(
#                 'Cannot restore snapshot: no snapshot exists.')
#         snapshot = self._snapshot
#         self.update(snapshot)
#         for k in list(self.keys()):
#             if k not in snapshot:
#                 del self[k]
# 
#     @contextmanager
#     def local(self, dictionary=None, **kwargs):
#         """Withable local context.  """
#         self.take_snapshot()
#         self.update(dictionary, **kwargs)
#         yield
#         self.restore_snapshot()


# Path: tests/test_context.py
import copy
import unittest
from soap.context.base import _Context

        with context.local(a=2):
            self.assertEqual(context.a, 2)
            context.e = 3
        self.assertEqual(context.a, 1)
        self.assertNotIn('e', context)
        context.c = {}
        with context.c.local(d=4):
            self.assertEqual(context.c.d, 4)
        self.assertFalse(context.c)

    def test_recursive_local(self):
        context = copy.deepcopy(self.context)
        with context.local(a=3):
            context.outter = True
            self.assertEqual(context.a, 3)
            with context.local(b=4):
                context.inner = True
                self.assertEqual(context.b, 4)
            self.assertNotIn('inner', context)
            self.assertTrue(context.outter)
            self.assertEqual(context.b, self.context.b)
        self.assertEqual(context, self.context)

    def test_chain_local(self):
        context = copy.deepcopy(self.context)
        with context.b.local(c=3):
            self.assertEqual(context.b.c, 3)
        self.assertEqual(context, self.context)

    def test_hook(self):

        class HookContext(_Context):