1818import typeguard
1919import inspect
2020import shlex
21+ import typing
2122from typing import (
2223 Callable ,
2324 List ,
3334 TypeVar ,
3435 Generic ,
3536 Iterable ,
37+ Sequence ,
38+ Type ,
3639)
3740
3841import nixops .util
@@ -132,7 +135,7 @@ def __init__(self, *args: ImmutableValidatedObject, **kwargs):
132135 kw = {}
133136 for arg in args :
134137 if not isinstance (arg , ImmutableValidatedObject ):
135- raise TypeError ("Arg not a Immutablevalidatedobject instance" )
138+ raise TypeError ("Arg not a ImmutableValidatedObject instance" )
136139 kw .update (dict (arg ))
137140 kw .update (kwargs )
138141
@@ -143,30 +146,37 @@ def __init__(self, *args: ImmutableValidatedObject, **kwargs):
143146 continue
144147 anno .update (x .__annotations__ )
145148
146- def _transform_value (key : Any , value : Any ) -> Any :
147- ann = anno .get (key )
148-
149+ def _transform_value (value : Any , value_type : Optional [Type ]) -> Any :
149150 # Untyped, pass through
150- if not ann :
151+ if not value_type :
151152 return value
152153
153- if inspect .isclass (ann ) and issubclass (ann , ImmutableValidatedObject ):
154- value = ann (** value )
155-
156- # Support Sequence[ImmutableValidatedObject]
157- if isinstance (value , tuple ) and not isinstance (ann , str ):
158- new_value = []
159- for v in value :
160- for subann in ann .__args__ :
161- if inspect .isclass (subann ) and issubclass (
162- subann , ImmutableValidatedObject
163- ):
164- new_value .append (subann (** v ))
165- else :
166- new_value .append (v )
167- value = tuple (new_value )
168-
169- typeguard .check_type (key , value , ann )
154+ # Support ImmutableValidatedObject
155+ if (
156+ isinstance (value , Mapping )
157+ and inspect .isclass (value_type )
158+ and issubclass (value_type , ImmutableValidatedObject )
159+ ):
160+ value = value_type (** value )
161+
162+ type_origin = typing .get_origin (value_type ) # type: ignore[attr-defined]
163+ type_args = tuple (set (typing .get_args (value_type )) - {type (None )}) # type: ignore[attr-defined]
164+ if (
165+ type_origin is not None
166+ and len (type_args ) == 1
167+ and inspect .isclass (type_args [0 ])
168+ and issubclass (type_args [0 ], ImmutableValidatedObject )
169+ ):
170+ # Support Sequence[ImmutableValidatedObject]
171+ if isinstance (value , Sequence ) and issubclass (tuple , type_origin ):
172+ value = tuple (_transform_value (v , type_args [0 ]) for v in value )
173+
174+ # Support Optional[ImmutableValidatedObject]
175+ if type_origin is Union :
176+ if value is not None :
177+ value = _transform_value (value , type_args [0 ])
178+
179+ typeguard .check_type (key , value , value_type )
170180
171181 return value
172182
@@ -181,7 +191,7 @@ def _transform_value(key: Any, value: Any) -> Any:
181191 # is set this attribute is set on self before __init__ is called
182192 default = getattr (self , key ) if hasattr (self , key ) else None
183193 value = kw .get (key , default )
184- setattr (self , key , _transform_value (key , value ))
194+ setattr (self , key , _transform_value (value , anno . get ( key ) ))
185195
186196 self ._frozen = True
187197
0 commit comments