18
18
import typeguard
19
19
import inspect
20
20
import shlex
21
+ import typing
21
22
from typing import (
22
23
Callable ,
23
24
List ,
33
34
TypeVar ,
34
35
Generic ,
35
36
Iterable ,
37
+ Sequence ,
38
+ Type ,
36
39
)
37
40
38
41
import nixops .util
@@ -132,7 +135,7 @@ def __init__(self, *args: ImmutableValidatedObject, **kwargs):
132
135
kw = {}
133
136
for arg in args :
134
137
if not isinstance (arg , ImmutableValidatedObject ):
135
- raise TypeError ("Arg not a Immutablevalidatedobject instance" )
138
+ raise TypeError ("Arg not a ImmutableValidatedObject instance" )
136
139
kw .update (dict (arg ))
137
140
kw .update (kwargs )
138
141
@@ -143,30 +146,37 @@ def __init__(self, *args: ImmutableValidatedObject, **kwargs):
143
146
continue
144
147
anno .update (x .__annotations__ )
145
148
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 :
149
150
# Untyped, pass through
150
- if not ann :
151
+ if not value_type :
151
152
return value
152
153
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 )
170
180
171
181
return value
172
182
@@ -181,7 +191,7 @@ def _transform_value(key: Any, value: Any) -> Any:
181
191
# is set this attribute is set on self before __init__ is called
182
192
default = getattr (self , key ) if hasattr (self , key ) else None
183
193
value = kw .get (key , default )
184
- setattr (self , key , _transform_value (key , value ))
194
+ setattr (self , key , _transform_value (value , anno . get ( key ) ))
185
195
186
196
self ._frozen = True
187
197
0 commit comments