99 Field ,
1010 GetCoreSchemaHandler ,
1111 GetJsonSchemaHandler ,
12- SerializerFunctionWrapHandler ,
13- Tag ,
1412 field_validator ,
15- model_serializer ,
1613 model_validator ,
1714)
1815from pydantic .json_schema import JsonSchemaValue
@@ -227,11 +224,9 @@ def parse(cls, v: Any) -> Any:
227224 # Range and min/max dict - for backward compatibility
228225 if isinstance (v , Range ):
229226 return {"arch" : None , "count" : v }
230- # A subset rather than exactly {"min", "max"}: `ResourcesSpec` serializes `cpu` down to its
231- # count for old clients, and under `exclude_none=True` that leaves just `{"min": ...}`.
232- # Requiring both keys made the round trip land on the `Range[int]` arm of `ResourcesSpec.cpu`
233- # instead of coming back as a `CPUSpec`. `arch`/`count` are the only `CPUSpec` fields, so a
234- # mapping of min/max is unambiguously a range.
227+ # `arch` and `count` are the only `CPUSpec` fields, so a mapping of `min`/`max` is
228+ # unambiguously a count range. A subset rather than exactly `{"min", "max"}`, because a
229+ # half-open range may omit the other key.
235230 if isinstance (v , Mapping ) and v and v .keys () <= {"min" , "max" }:
236231 return {"arch" : None , "count" : v }
237232 return v
@@ -395,20 +390,7 @@ def _parse(cls, v: Any) -> Any:
395390
396391
397392class ResourcesSpec (CoreModel ):
398- # TODO: remove `Range[int]` in 0.20. It is kept only for backward compatibility.
399- cpu : Annotated [
400- Union [
401- # `Tag` only names the arm in validation errors. Without it the `loc` of a bad `cpu`
402- # spells out the whole wrapped schema —
403- # `cpu.function-before[parse(), function-before[parse(), ... CPUSpec]].count` — which
404- # is what `dstack apply` shows the user.
405- Annotated [CPUSpec , Tag ("CPUSpec" )],
406- Annotated [Range [int ], Tag ("Range[int]" )],
407- ],
408- # `CPUSpec` and `Range[int]` both accept a bare int/str, so the arm has to be picked by
409- # declaration order rather than by pydantic v2's "smart" union resolution.
410- Field (description = "The CPU requirements" , union_mode = "left_to_right" ),
411- ] = CPUSpec ()
393+ cpu : Annotated [CPUSpec , Field (description = "The CPU requirements" )] = CPUSpec ()
412394 memory : Annotated [Range [Memory ], Field (description = "The RAM size (e.g., `8GB`)" )] = (
413395 DEFAULT_MEMORY_SIZE
414396 )
@@ -435,8 +417,7 @@ def unconstrained(cls) -> "ResourcesSpec":
435417 )
436418
437419 def pretty_format (self ) -> str :
438- # TODO: Remove in 0.20. Use self.cpu directly
439- cpu = CPUSpec .model_validate (self .cpu )
420+ cpu = self .cpu
440421 resources : Dict [str , Any ] = dict (cpu_arch = cpu .arch , cpus = cpu .count , memory = self .memory )
441422 if self .gpu :
442423 gpu = self .gpu
@@ -452,18 +433,3 @@ def pretty_format(self) -> str:
452433 resources .update (disk_size = self .disk .size )
453434 res = pretty_resources (** resources )
454435 return res
455-
456- @model_serializer (mode = "wrap" )
457- def _serialize (self , handler : SerializerFunctionWrapHandler ) -> Dict [str , Any ]:
458- res = handler (self )
459- self ._update_serialized_cpu (res )
460- return res
461-
462- # TODO: Remove in 0.20. Added for backward compatibility.
463- def _update_serialized_cpu (self , values : Dict ):
464- cpu = values .get ("cpu" )
465- if cpu :
466- arch = cpu .get ("arch" )
467- count = cpu .get ("count" )
468- if count and arch in [None , gpuhunt .CPUArchitecture .X86 .value ]:
469- values ["cpu" ] = count
0 commit comments