# Path: sacred/config/config_dict.py
# class ConfigDict:
#     def __init__(self, d):
#         self._conf = normalize_or_die(d)
# 
#     def __call__(self, fixed=None, preset=None, fallback=None):
#         result = dogmatize(fixed or {})
#         recursive_fill_in(result, self._conf)
#         recursive_fill_in(result, preset or {})
#         added = result.revelation()
#         config_summary = ConfigSummary(added, result.modified, result.typechanges)
#         config_summary.update(undogmatize(result))
#         return config_summary
# 
# Path: sacred/config/config_scope.py
# class ConfigScope:
#     def __init__(self, func):
#         self.args, vararg_name, kw_wildcard, _, kwargs = get_argspec(func)
#         assert vararg_name is None, "*args not allowed for ConfigScope functions"
#         assert kw_wildcard is None, "**kwargs not allowed for ConfigScope functions"
#         assert not kwargs, "default values are not allowed for ConfigScope functions"
# 
#         self._func = func
#         self._body_code = get_function_body_code(func)
#         self._var_docs = get_config_comments(func)
#         self.__doc__ = self._func.__doc__
# 
#     def __call__(self, fixed=None, preset=None, fallback=None):
#         """
#         Evaluate this ConfigScope.
# 
#         This will evaluate the function body and fill the relevant local
#         variables into entries into keys in this dictionary.
# 
#         :param fixed: Dictionary of entries that should stay fixed during the
#                       evaluation. All of them will be part of the final config.
#         :type fixed: dict
#         :param preset: Dictionary of preset values that will be available
#                        during the evaluation (if they are declared in the
#                        function argument list). All of them will be part of the
#                        final config.
#         :type preset: dict
#         :param fallback: Dictionary of fallback values that will be available
#                          during the evaluation (if they are declared in the
#                          function argument list). They will NOT be part of the
#                          final config.
#         :type fallback: dict
#         :return: self
#         :rtype: ConfigScope
#         """
#         cfg_locals = dogmatize(fixed or {})
#         fallback = fallback or {}
#         preset = preset or {}
#         fallback_view = {}
# 
#         available_entries = set(preset.keys()) | set(fallback.keys())
# 
#         for arg in self.args:
#             if arg not in available_entries:
#                 raise KeyError(
#                     "'{}' not in preset for ConfigScope. "
#                     "Available options are: {}".format(arg, available_entries)
#                 )
#             if arg in preset:
#                 cfg_locals[arg] = preset[arg]
#             else:  # arg in fallback
#                 fallback_view[arg] = fallback[arg]
# 
#         cfg_locals.fallback = fallback_view
# 
#         with ConfigError.track(cfg_locals):
#             eval(self._body_code, copy(self._func.__globals__), cfg_locals)
# 
#         added = cfg_locals.revelation()
#         config_summary = ConfigSummary(
#             added,
#             cfg_locals.modified,
#             cfg_locals.typechanges,
#             cfg_locals.fallback_writes,
#             docs=self._var_docs,
#         )
#         # fill in the unused presets
#         recursive_fill_in(cfg_locals, preset)
# 
#         for key, value in cfg_locals.items():
#             try:
#                 config_summary[key] = normalize_or_die(value)
#             except ValueError:
#                 pass
#         return config_summary
# 
# Path: sacred/config/utils.py
# def chain_evaluate_config_scopes(config_scopes, fixed=None, preset=None, fallback=None):
#     fixed = fixed or {}
#     fallback = fallback or {}
#     final_config = dict(preset or {})
#     config_summaries = []
#     for config in config_scopes:
#         cfg = config(fixed=fixed, preset=final_config, fallback=fallback)
#         config_summaries.append(cfg)
#         final_config.update(cfg)
# 
#     if not config_scopes:
#         final_config.update(fixed)
# 
#     return undogmatize(final_config), config_summaries


import pytest
from sacred.config import ConfigScope, ConfigDict, chain_evaluate_config_scopes
    @ConfigScope
    def cfg1():
        a = 10
        b = 20

    @ConfigScope
    def cfg2(a):
        a *= 2
        b = 22

    final_cfg, summary = chain_evaluate_config_scopes([cfg1, cfg2])
    assert set(final_cfg.keys()) == {"a", "b"}
    assert final_cfg["a"] == 20
    assert final_cfg["b"] == 22


def test_chained_config_scopes_raise_for_undeclared_previous_keys():
    @ConfigScope
    def cfg1():
        a = 10

    @ConfigScope
    def cfg2():
        b = a * 2

    with pytest.raises(NameError):
        chain_evaluate_config_scopes([cfg1, cfg2])


def test_chained_config_scopes_cannot_modify_fixed():

# Path: sacred/config/config_dict.py
# class ConfigDict:
#     def __init__(self, d):
#         self._conf = normalize_or_die(d)
# 
#     def __call__(self, fixed=None, preset=None, fallback=None):
#         result = dogmatize(fixed or {})
#         recursive_fill_in(result, self._conf)
#         recursive_fill_in(result, preset or {})
#         added = result.revelation()
#         config_summary = ConfigSummary(added, result.modified, result.typechanges)
#         config_summary.update(undogmatize(result))
#         return config_summary
# 
# Path: sacred/config/config_scope.py
# class ConfigScope:
#     def __init__(self, func):
#         self.args, vararg_name, kw_wildcard, _, kwargs = get_argspec(func)
#         assert vararg_name is None, "*args not allowed for ConfigScope functions"
#         assert kw_wildcard is None, "**kwargs not allowed for ConfigScope functions"
#         assert not kwargs, "default values are not allowed for ConfigScope functions"
# 
#         self._func = func
#         self._body_code = get_function_body_code(func)
#         self._var_docs = get_config_comments(func)
#         self.__doc__ = self._func.__doc__
# 
#     def __call__(self, fixed=None, preset=None, fallback=None):
#         """
#         Evaluate this ConfigScope.
# 
#         This will evaluate the function body and fill the relevant local
#         variables into entries into keys in this dictionary.
# 
#         :param fixed: Dictionary of entries that should stay fixed during the
#                       evaluation. All of them will be part of the final config.
#         :type fixed: dict
#         :param preset: Dictionary of preset values that will be available
#                        during the evaluation (if they are declared in the
#                        function argument list). All of them will be part of the
#                        final config.
#         :type preset: dict
#         :param fallback: Dictionary of fallback values that will be available
#                          during the evaluation (if they are declared in the
#                          function argument list). They will NOT be part of the
#                          final config.
#         :type fallback: dict
#         :return: self
#         :rtype: ConfigScope
#         """
#         cfg_locals = dogmatize(fixed or {})
#         fallback = fallback or {}
#         preset = preset or {}
#         fallback_view = {}
# 
#         available_entries = set(preset.keys()) | set(fallback.keys())
# 
#         for arg in self.args:
#             if arg not in available_entries:
#                 raise KeyError(
#                     "'{}' not in preset for ConfigScope. "
#                     "Available options are: {}".format(arg, available_entries)
#                 )
#             if arg in preset:
#                 cfg_locals[arg] = preset[arg]
#             else:  # arg in fallback
#                 fallback_view[arg] = fallback[arg]
# 
#         cfg_locals.fallback = fallback_view
# 
#         with ConfigError.track(cfg_locals):
#             eval(self._body_code, copy(self._func.__globals__), cfg_locals)
# 
#         added = cfg_locals.revelation()
#         config_summary = ConfigSummary(
#             added,
#             cfg_locals.modified,
#             cfg_locals.typechanges,
#             cfg_locals.fallback_writes,
#             docs=self._var_docs,
#         )
#         # fill in the unused presets
#         recursive_fill_in(cfg_locals, preset)
# 
#         for key, value in cfg_locals.items():
#             try:
#                 config_summary[key] = normalize_or_die(value)
#             except ValueError:
#                 pass
#         return config_summary
# 
# Path: sacred/config/utils.py
# def chain_evaluate_config_scopes(config_scopes, fixed=None, preset=None, fallback=None):
#     fixed = fixed or {}
#     fallback = fallback or {}
#     final_config = dict(preset or {})
#     config_summaries = []
#     for config in config_scopes:
#         cfg = config(fixed=fixed, preset=final_config, fallback=fallback)
#         config_summaries.append(cfg)
#         final_config.update(cfg)
# 
#     if not config_scopes:
#         final_config.update(fixed)
# 
#     return undogmatize(final_config), config_summaries


# Path: tests/test_config/test_config_scope_chain.py
import pytest
from sacred.config import ConfigScope, ConfigDict, chain_evaluate_config_scopes

    @ConfigScope
    def cfg1():
        a = 10
        b = 20

    @ConfigScope
    def cfg2(a):
        a *= 2
        b = 22

    final_cfg, summary = chain_evaluate_config_scopes([cfg1, cfg2])
    assert set(final_cfg.keys()) == {"a", "b"}
    assert final_cfg["a"] == 20
    assert final_cfg["b"] == 22


def test_chained_config_scopes_raise_for_undeclared_previous_keys():
    @ConfigScope
    def cfg1():
        a = 10

    @ConfigScope
    def cfg2():
        b = a * 2

    with pytest.raises(NameError):
        chain_evaluate_config_scopes([cfg1, cfg2])


def test_chained_config_scopes_cannot_modify_fixed():

    @ConfigScope