1+ """
2+ src/data_models/production_routine.py
3+
4+ Production routine data models.
5+ """
6+
17import re
28import time
39import uuid
@@ -84,6 +90,7 @@ class BuiltinParameter(BaseModel):
8490 description = "Function to generate the builtin parameter value"
8591 )
8692
93+
8794BUILTIN_PARAMETERS = [
8895 BuiltinParameter (
8996 name = "uuid" ,
@@ -101,7 +108,6 @@ class BuiltinParameter(BaseModel):
101108class Parameter (BaseModel ):
102109 """
103110 Parameter model with comprehensive validation and type information.
104-
105111 Fields:
106112 name (str): Parameter name (must be valid Python identifier)
107113 type (ParameterType): Parameter data type
@@ -117,13 +123,12 @@ class Parameter(BaseModel):
117123 enum_values (list[str] | None): Allowed values for enum type
118124 format (str | None): Format specification (e.g., 'YYYY-MM-DD')
119125 """
120-
126+
121127 # reserved prefixes: names that cannot be used at the beginning of a parameter name
122128 RESERVED_PREFIXES : ClassVar [list [str ]] = [
123129 "sessionStorage" , "localStorage" , "cookie" , "meta" , "uuid" , "epoch_milliseconds"
124130 ]
125-
126-
131+
127132 name : str = Field (..., description = "Parameter name (must be valid Python identifier)" )
128133 type : ParameterType = Field (
129134 default = ParameterType .STRING ,
@@ -179,24 +184,23 @@ def validate_name(cls, v):
179184 """Ensure parameter name is a valid Python identifier and not reserved."""
180185 if not re .match (r'^[a-zA-Z_][a-zA-Z0-9_]*$' , v ):
181186 raise ValueError (f"Parameter name '{ v } ' is not a valid Python identifier" )
182-
187+
183188 # Check for reserved prefixes
184189 for prefix in cls .RESERVED_PREFIXES :
185190 if v .startswith (prefix ):
186191 raise ValueError (
187192 f"Parameter name '{ v } ' cannot start with '{ prefix } '. "
188193 f"Reserved prefixes: { cls .RESERVED_PREFIXES } "
189194 )
190-
195+
191196 return v
192197
193- @field_validator ('type' )
194- @classmethod
195- def validate_type_consistency (cls , v , info ):
198+ @model_validator (mode = 'after' )
199+ def validate_type_consistency (self ) -> 'Parameter' :
196200 """Validate type-specific constraints are consistent."""
197- if v == ParameterType .ENUM and not info . data . get ( ' enum_values' ) :
201+ if self . type == ParameterType .ENUM and not self . enum_values :
198202 raise ValueError ("enum_values must be provided for enum type" )
199- return v
203+ return self
200204
201205 @field_validator ('default' )
202206 @classmethod
@@ -226,7 +230,6 @@ def validate_default_type(cls, v, info):
226230 else :
227231 raise ValueError (f"Default value { v } is not a valid boolean value" )
228232 raise ValueError (f"Default value { v } cannot be converted to boolean" )
229-
230233 return v
231234
232235 @field_validator ('examples' )
@@ -267,7 +270,6 @@ def validate_examples_type(cls, v, info):
267270 return validated_examples
268271
269272
270-
271273class HTTPMethod (StrEnum ):
272274 """
273275 Supported HTTP methods for API endpoints.
@@ -319,7 +321,6 @@ class RoutineOperationTypes(StrEnum):
319321 RETURN = "return"
320322
321323
322-
323324class RoutineOperation (BaseModel ):
324325 """
325326 Base class for routine operations.
@@ -441,31 +442,47 @@ def validate_parameter_usage(self) -> 'Routine':
441442 and no undefined parameters are used.
442443 Raises ValueError if unused parameters are found or undefined parameters are used.
443444 """
445+ # Check 0: Ensure name and description fields don't contain parameter placeholders
446+ # These are metadata fields and should not have interpolation patterns
447+ param_pattern = r'\{\{([^}]*)\}\}'
448+ # check in Routine.name
449+ name_matches = re .findall (param_pattern , self .name )
450+ if name_matches :
451+ raise ValueError (
452+ f"Parameter placeholders found in routine name '{ self .name } ': { name_matches } . "
453+ "The 'name' field is a metadata field and should not contain parameter placeholders like {{param}}."
454+ )
455+ # check in Routine.description
456+ description_matches = re .findall (param_pattern , self .description )
457+ if description_matches :
458+ raise ValueError (
459+ f"Parameter placeholders found in routine description: { description_matches } . "
460+ "The 'description' field is a metadata field and should not contain parameter placeholders like {{param}}."
461+ )
462+
444463 # list of builtin parameter names
445464 builtin_parameter_names = [builtin_parameter .name for builtin_parameter in BUILTIN_PARAMETERS ]
446-
465+
447466 # Convert the entire routine to JSON string for searching
448467 routine_json = self .model_dump_json ()
449468
450469 # Extract all parameter names
451470 defined_parameters = {param .name for param in self .parameters }
452471
453- # Find all parameter usages in the JSON: *"{{*}}"*
454- # Match quoted placeholders: "{{param}}" or \"{{param}}\" (escaped quotes in JSON strings)
455- # \"{{param}}\" in JSON string means "{{param}}" in actual value
456- # Pattern REQUIRES quotes (either " or \") immediately before {{ and after }}
457- param_pattern = r'(?:"|\\")\{\{([^}"]*)\}\}(?:"|\\")'
472+ # Find all parameter usages in the JSON: {{*}}
473+ # Match placeholders anywhere: {{param}}
474+ # This matches parameters whether they're standalone quoted values or embedded in strings
475+ param_pattern = r'\{\{([^}]*)\}\}'
458476 matches = re .findall (param_pattern , routine_json )
459-
477+
460478 # track used parameters
461479 used_parameters = set ()
462-
480+
463481 # iterate over all parameter usages
464482 for match in matches :
465-
466483 # clean the match (already extracted the content between braces)
467484 match = match .strip ()
468-
485+
469486 # if the parameter name contains a colon, it is a storage parameter
470487 if ":" in match :
471488 kind , path = [p .strip () for p in match .split (":" , 1 )]
@@ -484,15 +501,15 @@ def validate_parameter_usage(self) -> 'Routine':
484501 if unused_parameters :
485502 raise ValueError (
486503 f"Unused parameters found in routine '{ self .name } ': { list (unused_parameters )} . "
487- f "All defined parameters must be used somewhere in the routine operations."
504+ "All defined parameters must be used somewhere in the routine operations."
488505 )
489506
490507 # Check 2: No undefined parameters should be used
491508 undefined_parameters = used_parameters - defined_parameters
492509 if undefined_parameters :
493510 raise ValueError (
494511 f"Undefined parameters found in routine '{ self .name } ': { list (undefined_parameters )} . "
495- f "All parameters used in the routine must be defined in parameters."
512+ "All parameters used in the routine must be defined in parameters."
496513 )
497514
498515 return self
0 commit comments