You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

127 line
4.1 KiB

  1. import json
  2. from enum import Enum
  3. from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Type, Union
  4. from .typing import AnyCallable
  5. from .utils import GetterDict
  6. if TYPE_CHECKING:
  7. from typing import overload
  8. import typing_extensions
  9. from .fields import ModelField
  10. from .main import BaseModel
  11. ConfigType = Type['BaseConfig']
  12. class SchemaExtraCallable(typing_extensions.Protocol):
  13. @overload
  14. def __call__(self, schema: Dict[str, Any]) -> None:
  15. pass
  16. @overload
  17. def __call__(self, schema: Dict[str, Any], model_class: Type[BaseModel]) -> None:
  18. pass
  19. else:
  20. SchemaExtraCallable = Callable[..., None]
  21. __all__ = 'BaseConfig', 'Extra', 'inherit_config', 'prepare_config'
  22. class Extra(str, Enum):
  23. allow = 'allow'
  24. ignore = 'ignore'
  25. forbid = 'forbid'
  26. class BaseConfig:
  27. title: Optional[str] = None
  28. anystr_lower: bool = False
  29. anystr_strip_whitespace: bool = False
  30. min_anystr_length: int = 0
  31. max_anystr_length: Optional[int] = None
  32. validate_all: bool = False
  33. extra: Extra = Extra.ignore
  34. allow_mutation: bool = True
  35. frozen: bool = False
  36. allow_population_by_field_name: bool = False
  37. use_enum_values: bool = False
  38. fields: Dict[str, Union[str, Dict[str, str]]] = {}
  39. validate_assignment: bool = False
  40. error_msg_templates: Dict[str, str] = {}
  41. arbitrary_types_allowed: bool = False
  42. orm_mode: bool = False
  43. getter_dict: Type[GetterDict] = GetterDict
  44. alias_generator: Optional[Callable[[str], str]] = None
  45. keep_untouched: Tuple[type, ...] = ()
  46. schema_extra: Union[Dict[str, Any], 'SchemaExtraCallable'] = {}
  47. json_loads: Callable[[str], Any] = json.loads
  48. json_dumps: Callable[..., str] = json.dumps
  49. # key type should include ForwardRef, but that breaks with python3.6
  50. json_encoders: Dict[Union[Type[Any], str], AnyCallable] = {}
  51. underscore_attrs_are_private: bool = False
  52. # whether inherited models as fields should be reconstructed as base model
  53. copy_on_model_validation: bool = True
  54. # whether `Union` should check all allowed types before even trying to coerce
  55. smart_union: bool = False
  56. @classmethod
  57. def get_field_info(cls, name: str) -> Dict[str, Any]:
  58. """
  59. Get properties of FieldInfo from the `fields` property of the config class.
  60. """
  61. fields_value = cls.fields.get(name)
  62. if isinstance(fields_value, str):
  63. field_info: Dict[str, Any] = {'alias': fields_value}
  64. elif isinstance(fields_value, dict):
  65. field_info = fields_value
  66. else:
  67. field_info = {}
  68. if 'alias' in field_info:
  69. field_info.setdefault('alias_priority', 2)
  70. if field_info.get('alias_priority', 0) <= 1 and cls.alias_generator:
  71. alias = cls.alias_generator(name)
  72. if not isinstance(alias, str):
  73. raise TypeError(f'Config.alias_generator must return str, not {alias.__class__}')
  74. field_info.update(alias=alias, alias_priority=1)
  75. return field_info
  76. @classmethod
  77. def prepare_field(cls, field: 'ModelField') -> None:
  78. """
  79. Optional hook to check or modify fields during model creation.
  80. """
  81. pass
  82. def inherit_config(self_config: 'ConfigType', parent_config: 'ConfigType', **namespace: Any) -> 'ConfigType':
  83. if not self_config:
  84. base_classes: Tuple['ConfigType', ...] = (parent_config,)
  85. elif self_config == parent_config:
  86. base_classes = (self_config,)
  87. else:
  88. base_classes = self_config, parent_config
  89. namespace['json_encoders'] = {
  90. **getattr(parent_config, 'json_encoders', {}),
  91. **getattr(self_config, 'json_encoders', {}),
  92. **namespace.get('json_encoders', {}),
  93. }
  94. return type('Config', base_classes, namespace)
  95. def prepare_config(config: Type[BaseConfig], cls_name: str) -> None:
  96. if not isinstance(config.extra, Extra):
  97. try:
  98. config.extra = Extra(config.extra)
  99. except ValueError:
  100. raise ValueError(f'"{cls_name}": {config.extra} is not a valid value for "extra"')