33    """Simple dynamic config backend.""" 
   38            initial_values: typing.Dict[str, typing.Any],
 
   39            config_cache_components: typing.Iterable[str],
 
   42        self._values = copy.deepcopy(initial_values)
 
   43        self._cache_invalidation_state = cache_invalidation_state
 
   44        self._config_cache_components = config_cache_components
 
   46    def set_values(self, values):
 
   47        self._values.update(values)
 
   48        self._sync_with_service()
 
   51        return self._values.copy()
 
   53    def remove_values(self, keys):
 
   54        extra_keys = set(keys).difference(self._values.keys())
 
   57                f
'Attempting to remove nonexistent configs: {extra_keys}',
 
   61        self._sync_with_service()
 
   63    def set(self, **values):
 
   64        self.set_values(values)
 
   66    def get(self, key, default=None):
 
   67        if key 
not in self._values:
 
   68            if default 
is not None:
 
   71        return self._values[key]
 
   73    def remove(self, key):
 
   74        return self.remove_values([key])
 
   76    def _sync_with_service(self):
 
   77        self._cache_invalidation_state.invalidate(
 
   78            self._config_cache_components,
 
 
   88        config_service_defaults,
 
   89        cache_invalidation_state,
 
   92    Fixture that allows to control dynamic config values used by the service. 
   94    After change to the config, be sure to call: 
   96    await service_client.update_server_state() 
   99    HTTP client requests call it automatically before each request. 
  101    @ingroup userver_testsuite_fixtures 
  103    all_values = config_service_defaults.copy()
 
  104    for path 
in reversed(list(search_path(
'config.json'))):
 
  105        values = load_json(path)
 
  106        all_values.update(values)
 
  107    for marker 
in request.node.iter_markers(
'config'):
 
  108        marker_json = object_substitute(marker.kwargs)
 
  109        all_values.update(marker_json)
 
  111        initial_values=all_values,
 
  112        config_cache_components=_CONFIG_CACHES,
 
  113        cache_invalidation_state=cache_invalidation_state,
 
 
  144        config_fallback_path, dynamic_config_fallback_patch,
 
  145) -> typing.Dict[str, typing.Any]:
 
  147    Fixture that returns default values for dynamic config. You may override 
  148    it in your local conftest.py or fixture: 
  151    @pytest.fixture(scope='session') 
  152    def config_service_defaults(): 
  153        with open('defaults.json') as fp: 
  157    @ingroup userver_testsuite_fixtures 
  159    if config_fallback_path 
and pathlib.Path(config_fallback_path).exists():
 
  160        with open(config_fallback_path, 
'r', encoding=
'utf-8') 
as file:
 
  161            fallback = json.load(file)
 
  162        fallback.update(dynamic_config_fallback_patch)
 
  166        'Either provide the path to dynamic config defaults file using ' 
  167        '--config-fallback pytest option, or override ' 
  168        f
'{config_service_defaults.__name__} fixture to provide custom ' 
  169        'dynamic config loading behavior.',
 
  173@pytest.fixture(scope='session') 
 
  200        pytestconfig, config_service_defaults, service_tmpdir,
 
  203    Returns a function that adjusts the static configuration file for 
  205    Sets the `fallback-path` of the `dynamic-config-client-updater` and 
  206    `dynamic-config-fallbacks` according to `config_service_defaults`. 
  208    @ingroup userver_testsuite_fixtures 
  211    def _patch_config(config_yaml, _config_vars):
 
  212        components = config_yaml[
'components_manager'][
'components']
 
  213        if not (components.keys() & _COMPONENTS_WITH_FALLBACK):
 
  217            service_tmpdir / 
'configs' / 
'dynamic_config_fallback.json' 
  219        fallback_path.parent.mkdir(exist_ok=
True)
 
  220        with open(fallback_path, 
'w', encoding=
'utf-8') 
as file:
 
  221            json.dump(config_service_defaults, file)
 
  223        for component_name 
in _COMPONENTS_WITH_FALLBACK:
 
  224            if component_name 
not in components:
 
  226            component = components[component_name]
 
  227            component[
'fallback-path'] = str(fallback_path)
 
  232@pytest.fixture(scope='session')