@@ -95,6 +95,16 @@ validators = {
9595 Text: valid_str
9696}
9797
98+ cdef dict _create_error(str name, object value, object error, object val_type, object annotated_type, object exception = None ):
99+ return {
100+ " field" : name,
101+ " value" : value,
102+ " error" : error,
103+ " value_type" : val_type,
104+ " annotation" : annotated_type,
105+ " exception" : exception
106+ }
107+
98108
99109cdef inline bint is_enum_class(object annotated_type):
100110 cdef int res
@@ -294,6 +304,168 @@ cdef dict _validate_union_field(
294304 errors
295305 )
296306
307+ cdef dict _validate_constraints(
308+ object field,
309+ str name,
310+ object value,
311+ object annotated_type,
312+ object val_type
313+ ):
314+ """
315+ Validates primitive field constraints based on field metadata.
316+
317+ Handles the following validations:
318+ - For strings: length, min_length, max_length
319+ - For numbers: min, max, gt, lt, ge, le, eq
320+
321+ Args:
322+ field: The Field object
323+ name: Field name
324+ value: The value to validate
325+ annotated_type: The annotated type
326+ val_type: The actual value type
327+
328+ Returns:
329+ Empty dict if validation passes, error dict otherwise
330+ """
331+ # string comparisons
332+ cdef object length = None
333+ cdef object min_length = None
334+ cdef object max_length = None
335+ # integer/float comparisons
336+ cdef object min_val = None
337+ cdef object max_val = None
338+ cdef object ge_val = None
339+ cdef object le_val = None
340+ cdef object eq_val = None
341+ cdef object ne_val = None
342+ cdef object pattern = None
343+ # Skip validation if value is None
344+ if value is None :
345+ return {}
346+
347+ metadata = field.metadata
348+ error = {}
349+
350+ # String validations
351+ if annotated_type is str :
352+ length = metadata.get(' length' , None )
353+ min_length = metadata.get(' min_length' , None )
354+ max_length = metadata.get(' max_length' , None )
355+ pattern = metadata.get(' pattern' , getattr (field, ' _pattern' , None ))
356+ # Length validation
357+ if length is not None and len (value) != length:
358+ return _create_error(
359+ name,
360+ value,
361+ f" String length must be exactly {length} characters, got {len(value)}" ,
362+ val_type,
363+ annotated_type
364+ )
365+
366+ # Min length validation
367+ if min_length is not None and len (value) < min_length:
368+ return _create_error(
369+ name,
370+ value,
371+ f" String length must be at least {min_length} characters, got {len(value)}" ,
372+ val_type,
373+ annotated_type
374+ )
375+
376+ # Max length validation
377+ if max_length is not None and len (value) > max_length:
378+ return _create_error(
379+ name,
380+ value,
381+ f" String length must be at most {max_length} characters, got {len(value)}" ,
382+ val_type,
383+ annotated_type
384+ )
385+
386+ # Pattern validation
387+ if pattern is not None :
388+ import re
389+ if not re.match(pattern, value):
390+ return _create_error(
391+ name,
392+ value,
393+ f" String does not match pattern {pattern}" ,
394+ val_type,
395+ annotated_type
396+ )
397+
398+ # Numeric validations (int, float, Decimal)
399+ elif annotated_type in (int , float , Decimal):
400+ min_val = metadata.get(' min' , getattr (field, ' gt' , None ))
401+ max_val = metadata.get(' max' , getattr (field, ' lt' , None ))
402+ ge_val = getattr (field, ' ge' , None )
403+ le_val = getattr (field, ' le' , None )
404+ eq_val = getattr (field, ' eq' , None )
405+ ne_val = getattr (field, ' ne' , None )
406+ # Equal validation
407+ if eq_val is not None and value != eq_val:
408+ return _create_error(
409+ name,
410+ value,
411+ f" Value must be equal to {eq_val}" ,
412+ val_type,
413+ annotated_type
414+ )
415+
416+ # Not equal validation
417+ if ne_val is not None and value == ne_val:
418+ return _create_error(
419+ name,
420+ value,
421+ f" Value must not be equal to {ne_val}" ,
422+ val_type,
423+ annotated_type
424+ )
425+
426+ # Minimum value validation (greater than)
427+ if min_val is not None and value <= min_val:
428+ return _create_error(
429+ name,
430+ value,
431+ f" Value must be greater than {min_val}" ,
432+ val_type,
433+ annotated_type
434+ )
435+
436+ # Maximum value validation (less than)
437+ if max_val is not None and value >= max_val:
438+ return _create_error(
439+ name,
440+ value,
441+ f" Value must be less than {max_val}" ,
442+ val_type,
443+ annotated_type
444+ )
445+
446+ # Greater than or equal validation
447+ if ge_val is not None and value < ge_val:
448+ return _create_error(
449+ name,
450+ value,
451+ f" Value must be greater than or equal to {ge_val}" ,
452+ val_type,
453+ annotated_type
454+ )
455+
456+ # Less than or equal validation
457+ if le_val is not None and value > le_val:
458+ return _create_error(
459+ name,
460+ value,
461+ f" Value must be less than or equal to {le_val}" ,
462+ val_type,
463+ annotated_type
464+ )
465+
466+ # If we've made it here, all validations passed
467+ return {}
468+
297469cpdef dict _validation(
298470 object F,
299471 str name,
@@ -321,6 +493,11 @@ cpdef dict _validation(
321493 return _create_error(name, value, msg, val_type, annotated_type)
322494 except ValueError :
323495 raise
496+ # Check for primitive type constraints if the value is not None
497+ if F._type_category == ' primitive' :
498+ errors = _validate_constraints(F, name, value, annotated_type, val_type)
499+ if errors:
500+ return errors
324501 # check: data type hint
325502 # If field_type is known, short-circuit certain checks
326503 if F.type == Text:
@@ -532,16 +709,6 @@ cpdef dict _validation(
532709 )
533710 return error
534711
535- cdef dict _create_error(str name, object value, object error, object val_type, object annotated_type, object exception = None ):
536- return {
537- " field" : name,
538- " value" : value,
539- " error" : error,
540- " value_type" : val_type,
541- " annotation" : annotated_type,
542- " exception" : exception
543- }
544-
545712# Define a validator function for uint64
546713def validate_uint64 (value: int ) -> None:
547714 """Validate uint64 values.
0 commit comments