Skip to content

Commit

Permalink
ADCM-6292 & ADCM-6294 Change structure initialization when it's defau…
Browse files Browse the repository at this point in the history
…lt is None and (de)sync structure on child change (#88)

Co-authored-by: Araslanov Egor <[email protected]>
  • Loading branch information
2 people authored and a-alferov committed Jan 24, 2025
1 parent 7e12323 commit 37cfd82
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 31 deletions.
70 changes: 47 additions & 23 deletions adcm_aio_client/config/_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,50 +113,74 @@ def value(self: Self) -> T:
def set(self: Self, value: Any) -> Self: # noqa: ANN401
try:
self._data.set_value(parameter=self._name, value=value)
except (TypeError, KeyError) as err:
except (TypeError, KeyError):
if len(self._name) == 1:
# not in any sort of group, should continue with exception
raise

self._set_parent_groups_to_defaults(err=err)
self._ensure_parent_groups_are_dicts()
self._data.set_value(parameter=self._name, value=value)

return self

def _set_parent_groups_to_defaults(self: Self, err: Exception) -> None:
# find first `None` group
root_group_name, *rest = self._name[:-1]
group = (root_group_name,)

while rest:
value_ = self._data.get_value(group)
if value_ is None:
break
def _ensure_parent_groups_are_dicts(self: Self) -> None:
*groups, _ = self._name
path = ()
for group in groups:
group_name = (*path, group)

next_group_name, *rest = rest
group = (*group, next_group_name)
try:
value = self._data.get_value(group_name)
except KeyError:
value = None

value_ = self._data.get_value(group)
if value_ is not None:
# error was legit and not about None group
raise err
if value is None:
self._data.set_value(group_name, {})

# actually build defaults
defaults = self._schema.get_default(group)
self._data.set_value(group, defaults)
path = group_name


class _Desyncable(_ConfigWrapper):
_SYNC_ATTR = "isSynchronized"

def sync(self: Self) -> Self:
self._data.set_attribute(parameter=self._name, attribute=self._SYNC_ATTR, value=True)
return self
return self._set(value=True)

def desync(self: Self) -> Self:
self._data.set_attribute(parameter=self._name, attribute=self._SYNC_ATTR, value=False)
return self._set(value=False)

def _set(self: Self, *, value: bool) -> Self:
try:
self._data.set_attribute(parameter=self._name, attribute=self._SYNC_ATTR, value=value)
except KeyError:
if len(self._name) == 1:
# not a group, attribute have to exist
raise

# we assume that this element is a structure,
# so we have to (de)sync it
closest_attribute = self._find_closest_attribute(self._name)
if closest_attribute is None or not self._schema.is_group(closest_attribute):
# it's not a structure-based group
raise

self._data.set_attribute(parameter=closest_attribute, attribute=self._SYNC_ATTR, value=value)

return self

def _find_closest_attribute(self: Self, name: LevelNames) -> LevelNames | None:
if not name:
return None

prev_name = tuple(name[:-1])

try:
self._data.get_attribute(parameter=prev_name, attribute=self._SYNC_ATTR)
except KeyError:
return self._find_closest_attribute(prev_name)

return prev_name


class ParameterHG[T](_Desyncable, Parameter[T]):
def set(self: Self, value: Any) -> Self: # noqa: ANN401
Expand Down
15 changes: 7 additions & 8 deletions tests/integration/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,10 @@ async def test_host_group_config(cluster: Cluster) -> None:
main_config["more"]["strange"].set(strange_val_1) # type: ignore
main_config["Optional", ActivatableParameterGroup].activate()
main_config["complexity_level", Parameter].set(complexity_changed)
# todo fix working with structures here
# sag = main_config["A lot of text"]["sag"]
# sag["quantity"].set(4)
# sag["nested"]["attr"].set( "foo")
# sag["nested"]["op"].set( "bar")
sag = main_config["A lot of text"]["sag"] # type: ignore
sag["quantity"].set(4) # type: ignore
sag["nested"]["attr"].set("foo") # type: ignore
sag["nested"]["op"].set("bar") # type: ignore

config_1["Set me", ParameterHG].set(req_val_2)
config_1["from_doc"]["person"].set(person_val_2) # type: ignore
Expand All @@ -293,9 +292,9 @@ async def test_host_group_config(cluster: Cluster) -> None:
await config_1.refresh(strategy=apply_local_changes)
await config_2.refresh(strategy=apply_remote_changes)

# todo same fix with structure
# values = get_field_value("A lot of text", "sag", "quantity", configs=configs)
# assert set(values) == {4}
config_1["A lot of text"]["sag"]["nested"]["op"].sync().desync() # type: ignore
values = get_field_value("A lot of text", "sag", "quantity", configs=configs)
assert set(values) == {4}
values = get_field_value("Set me", configs=configs)
assert values == (req_val_1, req_val_2, req_val_1)
values = get_field_value("more", "strange", configs=configs)
Expand Down

0 comments on commit 37cfd82

Please sign in to comment.