From e43c82fb7ed2c210199e95753b6f7c9ad8b6faea Mon Sep 17 00:00:00 2001 From: talyz Date: Wed, 15 Nov 2023 18:07:23 +0100 Subject: [PATCH] ImmutableValidatedObject: Support nested Mapping types Also, improve the handling of sequences and fix the sequence test. --- nixops/util.py | 41 +++++++++++++++++++++++++++-------------- tests/unit/test_util.py | 14 ++++++++++++-- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/nixops/util.py b/nixops/util.py index e5f6ef966..e3c49fdbd 100644 --- a/nixops/util.py +++ b/nixops/util.py @@ -17,8 +17,9 @@ import re import typing import typeguard # type: ignore -import inspect import shlex +import collections.abc +from inspect import isclass from typing import ( Callable, List, @@ -149,21 +150,33 @@ def _transform_value(key: Any, value: Any) -> Any: if not ann: return value - if inspect.isclass(ann) and issubclass(ann, ImmutableValidatedObject): + if isclass(ann) and issubclass(ann, ImmutableValidatedObject): value = ann(**value) - # Support Sequence[ImmutableValidatedObject] - if isinstance(value, tuple) and not isinstance(ann, str): - new_value = [] - for v in value: - for subann in ann.__args__: # type: ignore - if inspect.isclass(subann) and issubclass( - subann, ImmutableValidatedObject - ): - new_value.append(subann(**v)) - else: - new_value.append(v) - value = tuple(new_value) + # Support containers of ImmutableValidatedObjects + match typing.get_origin(ann): + + case collections.abc.Sequence: + sequence: List = [] + for v in value: + for subann in typing.get_args(ann): + if isclass(subann) and issubclass( + subann, ImmutableValidatedObject + ): + sequence.append(subann(**v)) + else: + sequence.append(v) + value = tuple(sequence) + + case collections.abc.Mapping: + _, value_ann = typing.get_args(ann) + if isclass(value_ann) and issubclass( + value_ann, ImmutableValidatedObject + ): + mapping: Dict = {} + for k, v in value.items(): + mapping[k] = value_ann(**v) + value = mapping typeguard.check_type(value, ann) diff --git a/tests/unit/test_util.py b/tests/unit/test_util.py index 361679d7e..a828b8071 100644 --- a/tests/unit/test_util.py +++ b/tests/unit/test_util.py @@ -1,4 +1,4 @@ -from typing import Any, Sequence +from typing import Any, Sequence, Mapping import json from nixops.logger import Logger from io import StringIO @@ -121,4 +121,14 @@ class B(A): class WithSequence(util.ImmutableValidatedObject): subs: Sequence[SubResource] - WithSequence(subs=[SubResource(x=1), SubResource(x=2)]) + seq = WithSequence(subs=[{"x": 1}, {"x": 2}]) + for i in seq.subs: + self.assertIsInstance(i, SubResource) + + # Test Mapping[str, ImmutableValidatedObject] + class WithMapping(util.ImmutableValidatedObject): + mapping: Mapping[str, SubResource] + + mapped = WithMapping(mapping={"aaa": {"x": 1}, "bbb": {"x": 2}}) + for _, v in mapped.mapping.items(): + self.assertIsInstance(v, SubResource)