Coverage for presidio_analyzer / input_validation / schemas.py: 100%

63 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-03-29 09:03 +0000

1from pathlib import Path 

2from typing import Any, Dict, List, Union 

3 

4from pydantic import ValidationError 

5 

6from . import validate_language_codes 

7from .yaml_recognizer_models import RecognizerRegistryConfig 

8 

9 

10class ConfigurationValidator: 

11 """Class for validating configurations using Pydantic-enabled classes.""" 

12 

13 @staticmethod 

14 def validate_language_codes(languages: List[str]) -> List[str]: 

15 """Validate language codes format. 

16 

17 :param languages: List of languages to validate. 

18 """ 

19 validate_language_codes(languages) 

20 return languages 

21 

22 @staticmethod 

23 def validate_file_path(file_path: Union[str, Path]) -> Path: 

24 """Validate file path exists and is readable. 

25 

26 :param file_path: Path to validate. 

27 """ 

28 path = Path(file_path) 

29 if not path.exists(): 

30 raise ValueError(f"Configuration file does not exist: {path}") 

31 if not path.is_file(): 

32 raise ValueError(f"Path is not a file: {path}") 

33 return path 

34 

35 @staticmethod 

36 def validate_score_threshold(threshold: float) -> float: 

37 """Validate score threshold is within valid range. 

38 

39 :param threshold: score threshold to validate. 

40 """ 

41 if not 0.0 <= threshold <= 1.0: 

42 raise ValueError( 

43 f"Score threshold must be between 0.0 and 1.0, got: {threshold}" 

44 ) 

45 return threshold 

46 

47 @staticmethod 

48 def validate_nlp_configuration(config: Dict[str, Any]) -> Dict[str, Any]: 

49 """Validate NLP configuration structure. 

50 

51 :param config: NLP Configuration to validate. 

52 """ 

53 if not isinstance(config, dict): 

54 raise ValueError("NLP configuration must be a dictionary") 

55 

56 required_fields = ["nlp_engine_name", "models"] 

57 missing_fields = [field for field in required_fields if field not in config] 

58 if missing_fields: 

59 raise ValueError( 

60 f"NLP configuration missing required fields: {missing_fields}" 

61 ) 

62 

63 # Validate models structure 

64 if not isinstance(config["models"], list) or not config["models"]: 

65 raise ValueError("Models must be a non-empty list") 

66 

67 for model in config["models"]: 

68 if not isinstance(model, dict): 

69 raise ValueError("Each model must be a dictionary") 

70 if "lang_code" not in model or "model_name" not in model: 

71 raise ValueError("Each model must have 'lang_code' and 'model_name'") 

72 

73 return config 

74 

75 @staticmethod 

76 def validate_recognizer_registry_configuration( 

77 config: Dict[str, Any], 

78 ) -> Dict[str, Any]: 

79 """Validate recognizer registry configuration using Pydantic models.""" 

80 try: 

81 # Use Pydantic model for validation 

82 validated_config = RecognizerRegistryConfig(**config) 

83 # Use model_dump() without exclude_unset to include default values 

84 return validated_config.model_dump(exclude_unset=False) 

85 except ValidationError as e: 

86 raise ValueError("Invalid recognizer registry configuration") from e 

87 

88 @staticmethod 

89 def validate_analyzer_configuration(config: Dict[str, Any]) -> Dict[str, Any]: 

90 """Validate analyzer engine configuration.""" 

91 if not isinstance(config, dict): 

92 raise ValueError("Analyzer configuration must be a dictionary") 

93 

94 # Define valid top-level keys for analyzer configuration 

95 valid_keys = { 

96 "supported_languages", 

97 "default_score_threshold", 

98 "nlp_configuration", 

99 "recognizer_registry", 

100 } 

101 

102 # Check for unknown keys 

103 unknown_keys = set(config.keys()) - valid_keys 

104 if unknown_keys: 

105 raise ValueError( 

106 f"Unknown configuration key(s) in " 

107 f"analyzer configuration: {sorted(unknown_keys)}. " 

108 f"Valid keys are: {sorted(valid_keys)}" 

109 ) 

110 

111 # Validate supported languages if present 

112 if "supported_languages" in config: 

113 validate_language_codes(config["supported_languages"]) 

114 

115 # Validate score threshold if present 

116 if "default_score_threshold" in config: 

117 ConfigurationValidator.validate_score_threshold( 

118 config["default_score_threshold"] 

119 ) 

120 

121 # Validate nested configurations 

122 if "nlp_configuration" in config: 

123 ConfigurationValidator.validate_nlp_configuration( 

124 config["nlp_configuration"] 

125 ) 

126 

127 if "recognizer_registry" in config: 

128 ConfigurationValidator.validate_recognizer_registry_configuration( 

129 config["recognizer_registry"] 

130 ) 

131 

132 return config