Coverage for pydantic/config.py: 100.00%

116 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-15 13:26 +0000

1import json 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

2from enum import Enum 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

3from typing import TYPE_CHECKING, Any, Callable, Dict, ForwardRef, Optional, Tuple, Type, Union 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

4 

5from typing_extensions import Literal, Protocol 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

6 

7from pydantic.typing import AnyArgTCallable, AnyCallable 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

8from pydantic.utils import GetterDict 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

9from pydantic.version import compiled 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

10 

11if TYPE_CHECKING: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

12 from typing import overload 

13 

14 from pydantic.fields import ModelField 

15 from pydantic.main import BaseModel 

16 

17 ConfigType = Type['BaseConfig'] 

18 

19 class SchemaExtraCallable(Protocol): 

20 @overload 

21 def __call__(self, schema: Dict[str, Any]) -> None: 

22 pass 

23 

24 @overload 

25 def __call__(self, schema: Dict[str, Any], model_class: Type[BaseModel]) -> None: 

26 pass 

27 

28else: 

29 SchemaExtraCallable = Callable[..., None] 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

30 

31__all__ = 'BaseConfig', 'ConfigDict', 'get_config', 'Extra', 'inherit_config', 'prepare_config' 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

32 

33 

34class Extra(str, Enum): 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

35 allow = 'allow' 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

36 ignore = 'ignore' 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

37 forbid = 'forbid' 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

38 

39 

40# https://github.com/cython/cython/issues/4003 

41# Fixed in Cython 3 and Pydantic v1 won't support Cython 3. 

42# Pydantic v2 doesn't depend on Cython at all. 

43if not compiled: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

44 from typing_extensions import TypedDict 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

45 

46 class ConfigDict(TypedDict, total=False): 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

47 title: Optional[str] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

48 anystr_lower: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

49 anystr_strip_whitespace: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

50 min_anystr_length: int 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

51 max_anystr_length: Optional[int] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

52 validate_all: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

53 extra: Extra 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

54 allow_mutation: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

55 frozen: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

56 allow_population_by_field_name: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

57 use_enum_values: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

58 fields: Dict[str, Union[str, Dict[str, str]]] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

59 validate_assignment: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

60 error_msg_templates: Dict[str, str] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

61 arbitrary_types_allowed: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

62 orm_mode: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

63 getter_dict: Type[GetterDict] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

64 alias_generator: Optional[Callable[[str], str]] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

65 keep_untouched: Tuple[type, ...] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

66 schema_extra: Union[Dict[str, object], 'SchemaExtraCallable'] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

67 json_loads: Callable[[str], object] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

68 json_dumps: AnyArgTCallable[str] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

69 json_encoders: Dict[Type[object], AnyCallable] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

70 underscore_attrs_are_private: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

71 allow_inf_nan: bool 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

72 copy_on_model_validation: Literal['none', 'deep', 'shallow'] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

73 # whether dataclass `__post_init__` should be run after validation 

74 post_init_call: Literal['before_validation', 'after_validation'] 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

75 

76else: 

77 ConfigDict = dict # type: ignore 1QRSTU

78 

79 

80class BaseConfig: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

81 title: Optional[str] = None 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

82 anystr_lower: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

83 anystr_upper: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

84 anystr_strip_whitespace: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

85 min_anystr_length: int = 0 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

86 max_anystr_length: Optional[int] = None 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

87 validate_all: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

88 extra: Extra = Extra.ignore 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

89 allow_mutation: bool = True 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

90 frozen: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

91 allow_population_by_field_name: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

92 use_enum_values: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

93 fields: Dict[str, Union[str, Dict[str, str]]] = {} 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

94 validate_assignment: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

95 error_msg_templates: Dict[str, str] = {} 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

96 arbitrary_types_allowed: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

97 orm_mode: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

98 getter_dict: Type[GetterDict] = GetterDict 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

99 alias_generator: Optional[Callable[[str], str]] = None 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

100 keep_untouched: Tuple[type, ...] = () 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

101 schema_extra: Union[Dict[str, Any], 'SchemaExtraCallable'] = {} 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

102 json_loads: Callable[[str], Any] = json.loads 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

103 json_dumps: Callable[..., str] = json.dumps 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

104 json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable] = {} 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

105 underscore_attrs_are_private: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

106 allow_inf_nan: bool = True 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

107 

108 # whether inherited models as fields should be reconstructed as base model, 

109 # and whether such a copy should be shallow or deep 

110 copy_on_model_validation: Literal['none', 'deep', 'shallow'] = 'shallow' 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

111 

112 # whether `Union` should check all allowed types before even trying to coerce 

113 smart_union: bool = False 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

114 # whether dataclass `__post_init__` should be run before or after validation 

115 post_init_call: Literal['before_validation', 'after_validation'] = 'before_validation' 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

116 

117 @classmethod 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

118 def get_field_info(cls, name: str) -> Dict[str, Any]: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

119 """ 

120 Get properties of FieldInfo from the `fields` property of the config class. 

121 """ 

122 

123 fields_value = cls.fields.get(name) 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

124 

125 if isinstance(fields_value, str): 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

126 field_info: Dict[str, Any] = {'alias': fields_value} 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD

127 elif isinstance(fields_value, dict): 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

128 field_info = fields_value 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD

129 else: 

130 field_info = {} 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

131 

132 if 'alias' in field_info: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

133 field_info.setdefault('alias_priority', 2) 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD

134 

135 if field_info.get('alias_priority', 0) <= 1 and cls.alias_generator: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

136 alias = cls.alias_generator(name) 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD

137 if not isinstance(alias, str): 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD

138 raise TypeError(f'Config.alias_generator must return str, not {alias.__class__}') 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD

139 field_info.update(alias=alias, alias_priority=1) 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD

140 return field_info 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

141 

142 @classmethod 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

143 def prepare_field(cls, field: 'ModelField') -> None: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

144 """ 

145 Optional hook to check or modify fields during model creation. 

146 """ 

147 pass 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

148 

149 

150def get_config(config: Union[ConfigDict, Type[object], None]) -> Type[BaseConfig]: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

151 if config is None: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

152 return BaseConfig 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

153 

154 else: 

155 config_dict = ( 1abcdefghijklmnopqrstQRSTKLMNOPUuvwxyzABCD

156 config 

157 if isinstance(config, dict) 

158 else {k: getattr(config, k) for k in dir(config) if not k.startswith('__')} 

159 ) 

160 

161 class Config(BaseConfig): 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

162 ... 1EFabcdefghijGHklmnopqrstKLMNOPIJuvwxyzABCD

163 

164 for k, v in config_dict.items(): 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

165 setattr(Config, k, v) 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

166 return Config 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

167 

168 

169def inherit_config(self_config: 'ConfigType', parent_config: 'ConfigType', **namespace: Any) -> 'ConfigType': 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

170 if not self_config: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

171 base_classes: Tuple['ConfigType', ...] = (parent_config,) 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

172 elif self_config == parent_config: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

173 base_classes = (self_config,) 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

174 else: 

175 base_classes = self_config, parent_config 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

176 

177 namespace['json_encoders'] = { 1abcdefghijklmnopqrstQRSTKLMNOPUuvwxyzABCD

178 **getattr(parent_config, 'json_encoders', {}), 

179 **getattr(self_config, 'json_encoders', {}), 

180 **namespace.get('json_encoders', {}), 

181 } 

182 

183 return type('Config', base_classes, namespace) 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

184 

185 

186def prepare_config(config: Type[BaseConfig], cls_name: str) -> None: 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

187 if not isinstance(config.extra, Extra): 1EFabcdefghijGHklmnopqrstQRSTKLMNOPUIJuvwxyzABCD

188 try: 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD

189 config.extra = Extra(config.extra) 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD

190 except ValueError: 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD

191 raise ValueError(f'"{cls_name}": {config.extra} is not a valid value for "extra"') 1EFabcdefghijGHklmnopqrstQRSTUIJuvwxyzABCD