@@ -68,6 +68,15 @@ def _convert_nan_to_none(v):
6868 return v
6969
7070
71+ def _valid_petab_id (v : str ) -> str :
72+ """Field validator for PEtab IDs."""
73+ if not v :
74+ raise ValueError ("ID must not be empty." )
75+ if not is_valid_identifier (v ):
76+ raise ValueError (f"Invalid ID: { v } " )
77+ return v
78+
79+
7180class ObservableTransformation (str , Enum ):
7281 """Observable transformation types.
7382
@@ -141,7 +150,9 @@ class Observable(BaseModel):
141150 """Observable definition."""
142151
143152 #: Observable ID.
144- id : str = Field (alias = C .OBSERVABLE_ID )
153+ id : Annotated [str , AfterValidator (_valid_petab_id )] = Field (
154+ alias = C .OBSERVABLE_ID
155+ )
145156 #: Observable name.
146157 name : str | None = Field (alias = C .OBSERVABLE_NAME , default = None )
147158 #: Observable formula.
@@ -162,15 +173,6 @@ class Observable(BaseModel):
162173 arbitrary_types_allowed = True , populate_by_name = True
163174 )
164175
165- @field_validator ("id" )
166- @classmethod
167- def _validate_id (cls , v ):
168- if not v :
169- raise ValueError ("ID must not be empty." )
170- if not is_valid_identifier (v ):
171- raise ValueError (f"Invalid ID: { v } " )
172- return v
173-
174176 @field_validator (
175177 "name" ,
176178 "formula" ,
@@ -313,9 +315,11 @@ class Change(BaseModel):
313315 """
314316
315317 #: The ID of the target entity to change.
316- target_id : str | None = Field (alias = C .TARGET_ID , default = None )
318+ target_id : Annotated [str , AfterValidator (_valid_petab_id )] = Field (
319+ alias = C .TARGET_ID
320+ )
317321 #: The value to set the target entity to.
318- target_value : sp .Basic | None = Field (alias = C .TARGET_VALUE , default = None )
322+ target_value : sp .Basic = Field (alias = C .TARGET_VALUE )
319323
320324 #: :meta private:
321325 model_config = ConfigDict (
@@ -324,16 +328,6 @@ class Change(BaseModel):
324328 use_enum_values = True ,
325329 )
326330
327- @model_validator (mode = "before" )
328- @classmethod
329- def _validate_id (cls , data : dict ):
330- target_id = data .get ("target_id" , data .get (C .TARGET_ID ))
331-
332- if not is_valid_identifier (target_id ):
333- raise ValueError (f"Invalid ID: { target_id } " )
334-
335- return data
336-
337331 @field_validator ("target_value" , mode = "before" )
338332 @classmethod
339333 def _sympify (cls , v ):
@@ -366,22 +360,15 @@ class Condition(BaseModel):
366360 """
367361
368362 #: The condition ID.
369- id : str = Field (alias = C .CONDITION_ID )
363+ id : Annotated [str , AfterValidator (_valid_petab_id )] = Field (
364+ alias = C .CONDITION_ID
365+ )
370366 #: The changes associated with this condition.
371367 changes : list [Change ]
372368
373369 #: :meta private:
374370 model_config = ConfigDict (populate_by_name = True )
375371
376- @field_validator ("id" )
377- @classmethod
378- def _validate_id (cls , v ):
379- if not v :
380- raise ValueError ("ID must not be empty." )
381- if not is_valid_identifier (v ):
382- raise ValueError (f"Invalid ID: { v } " )
383- return v
384-
385372 def __add__ (self , other : Change ) -> Condition :
386373 """Add a change to the set."""
387374 if not isinstance (other , Change ):
@@ -488,8 +475,6 @@ class ExperimentPeriod(BaseModel):
488475 def _validate_id (cls , condition_id ):
489476 if pd .isna (condition_id ) or not condition_id :
490477 return None
491- # if not condition_id:
492- # raise ValueError("ID must not be empty.")
493478 if not is_valid_identifier (condition_id ):
494479 raise ValueError (f"Invalid ID: { condition_id } " )
495480 return condition_id
@@ -504,7 +489,9 @@ class Experiment(BaseModel):
504489 """
505490
506491 #: The experiment ID.
507- id : str = Field (alias = C .EXPERIMENT_ID )
492+ id : Annotated [str , AfterValidator (_valid_petab_id )] = Field (
493+ alias = C .EXPERIMENT_ID
494+ )
508495 #: The periods of the experiment.
509496 periods : list [ExperimentPeriod ] = []
510497
@@ -513,15 +500,6 @@ class Experiment(BaseModel):
513500 arbitrary_types_allowed = True , populate_by_name = True
514501 )
515502
516- @field_validator ("id" )
517- @classmethod
518- def _validate_id (cls , v ):
519- if not v :
520- raise ValueError ("ID must not be empty." )
521- if not is_valid_identifier (v ):
522- raise ValueError (f"Invalid ID: { v } " )
523- return v
524-
525503 def __add__ (self , other : ExperimentPeriod ) -> Experiment :
526504 """Add a period to the experiment."""
527505 if not isinstance (other , ExperimentPeriod ):
@@ -747,7 +725,9 @@ class Mapping(BaseModel):
747725 """Mapping PEtab entities to model entities."""
748726
749727 #: PEtab entity ID.
750- petab_id : str = Field (alias = C .PETAB_ENTITY_ID )
728+ petab_id : Annotated [str , AfterValidator (_valid_petab_id )] = Field (
729+ alias = C .PETAB_ENTITY_ID
730+ )
751731 #: Model entity ID.
752732 model_id : Annotated [str | None , BeforeValidator (_convert_nan_to_none )] = (
753733 Field (alias = C .MODEL_ENTITY_ID , default = None )
@@ -760,17 +740,6 @@ class Mapping(BaseModel):
760740 #: :meta private:
761741 model_config = ConfigDict (populate_by_name = True )
762742
763- @field_validator (
764- "petab_id" ,
765- )
766- @classmethod
767- def _validate_id (cls , v ):
768- if not v :
769- raise ValueError ("ID must not be empty." )
770- if not is_valid_identifier (v ):
771- raise ValueError (f"Invalid ID: { v } " )
772- return v
773-
774743
775744class MappingTable (BaseModel ):
776745 """PEtab mapping table."""
@@ -913,7 +882,7 @@ def _validate(self) -> Self:
913882 )
914883
915884 if self .lb is not None and self .ub is not None and self .lb >= self .ub :
916- raise ValueError ("Lower bound must be less than upper bound" )
885+ raise ValueError ("Lower bound must be less than upper bound. " )
917886
918887 # TODO parameterScale?
919888
0 commit comments