22import re
33import tempfile
44from dataclasses import dataclass
5- from typing import Any , ClassVar , TypeAlias
5+ from typing import Annotated , TypeAlias
66
77import sh # type: ignore[import-untyped]
88from models_library .docker import DockerGenericTag
99from pydantic import (
1010 BaseModel ,
1111 ByteSize ,
12- ConstrainedStr ,
12+ ConfigDict ,
1313 Field ,
1414 NonNegativeFloat ,
1515 NonNegativeInt ,
16- validator ,
16+ StringConstraints ,
17+ field_validator ,
1718)
1819from types_aiobotocore_ec2 .literals import InstanceStateNameType , InstanceTypeType
1920
@@ -33,26 +34,26 @@ def __gt__(self, other: "Resources") -> bool:
3334 return self .cpus > other .cpus or self .ram > other .ram
3435
3536 def __add__ (self , other : "Resources" ) -> "Resources" :
36- return Resources .construct (
37+ return Resources .model_construct (
3738 ** {
3839 key : a + b
3940 for (key , a ), b in zip (
40- self .dict ().items (), other .dict ().values (), strict = True
41+ self .model_dump ().items (), other .model_dump ().values (), strict = True
4142 )
4243 }
4344 )
4445
4546 def __sub__ (self , other : "Resources" ) -> "Resources" :
46- return Resources .construct (
47+ return Resources .model_construct (
4748 ** {
4849 key : a - b
4950 for (key , a ), b in zip (
50- self .dict ().items (), other .dict ().values (), strict = True
51+ self .model_dump ().items (), other .model_dump ().values (), strict = True
5152 )
5253 }
5354 )
5455
55- @validator ("cpus" , pre = True )
56+ @field_validator ("cpus" , mode = "before" )
5657 @classmethod
5758 def _floor_cpus_to_0 (cls , v : float ) -> float :
5859 return max (v , 0 )
@@ -67,19 +68,26 @@ class EC2InstanceType:
6768InstancePrivateDNSName : TypeAlias = str
6869
6970
70- class AWSTagKey (ConstrainedStr ):
71- # see [https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions]
72- regex = re .compile (r"^(?!(_index|\.{1,2})$)[a-zA-Z0-9\+\-=\._:@]+$" )
73- min_length = 1
74- max_length = 128
75-
76-
77- class AWSTagValue (ConstrainedStr ):
78- # see [https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions]
79- # quotes []{} were added as it allows to json encode. it seems to be accepted as a value
80- regex = re .compile (r"^[a-zA-Z0-9\s\+\-=\.,_:/@\"\'\[\]\{\}]*$" )
81- min_length = 0
82- max_length = 256
71+ # see [https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions]
72+ AWSTagKey : TypeAlias = Annotated [
73+ str ,
74+ StringConstraints (
75+ min_length = 1 ,
76+ max_length = 128 ,
77+ pattern = re .compile (r"^(?!(_index|\.{1,2})$)[a-zA-Z0-9\+\-=\._:@]+$" ),
78+ ),
79+ ]
80+
81+ # see [https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions]
82+ # quotes []{} were added as it allows to json encode. it seems to be accepted as a value
83+ AWSTagValue : TypeAlias = Annotated [
84+ str ,
85+ StringConstraints (
86+ min_length = 0 ,
87+ max_length = 256 ,
88+ pattern = r"^[a-zA-Z0-9\s\+\-=\.,_:/@\"\'\[\]\{\}]*$" ,
89+ ),
90+ ]
8391
8492
8593EC2Tags : TypeAlias = dict [AWSTagKey , AWSTagValue ]
@@ -148,8 +156,23 @@ class EC2InstanceBootSpecific(BaseModel):
148156 default = 0 , description = "number of buffer EC2s to keep (defaults to 0)"
149157 )
150158
151- class Config :
152- schema_extra : ClassVar [dict [str , Any ]] = {
159+ @field_validator ("custom_boot_scripts" )
160+ @classmethod
161+ def validate_bash_calls (cls , v ):
162+ try :
163+ with tempfile .NamedTemporaryFile (mode = "wt" , delete = True ) as temp_file :
164+ temp_file .writelines (v )
165+ temp_file .flush ()
166+ # NOTE: this will not capture runtime errors, but at least some syntax errors such as invalid quotes
167+ sh .bash ("-n" , temp_file .name )
168+ except sh .ErrorReturnCode as exc :
169+ msg = f"Invalid bash call in custom_boot_scripts: { v } , Error: { exc .stderr } "
170+ raise ValueError (msg ) from exc
171+
172+ return v
173+
174+ model_config = ConfigDict (
175+ json_schema_extra = {
153176 "examples" : [
154177 {
155178 # just AMI
@@ -205,18 +228,4 @@ class Config:
205228 },
206229 ]
207230 }
208-
209- @validator ("custom_boot_scripts" )
210- @classmethod
211- def validate_bash_calls (cls , v ):
212- try :
213- with tempfile .NamedTemporaryFile (mode = "wt" , delete = True ) as temp_file :
214- temp_file .writelines (v )
215- temp_file .flush ()
216- # NOTE: this will not capture runtime errors, but at least some syntax errors such as invalid quotes
217- sh .bash ("-n" , temp_file .name )
218- except sh .ErrorReturnCode as exc :
219- msg = f"Invalid bash call in custom_boot_scripts: { v } , Error: { exc .stderr } "
220- raise ValueError (msg ) from exc
221-
222- return v
231+ )
0 commit comments