Check if a configuration class is properly defined. - It should inherit from Pydantic's BaseModel. - It should have a model_config attribute. - It should have a model_fields attribute. - The factory_fields, required_fields, and optional_fields should be properly defined. - I
(
cls: BaseModel,
factory_fields: list[str] | None = None,
required_fields: list[str] | None = None,
optional_fields: list[str] | None = None,
reserved_fields: list[str] | None = None,
)
| 99 | |
| 100 | |
| 101 | def check_config_base_class( |
| 102 | cls: BaseModel, |
| 103 | factory_fields: list[str] | None = None, |
| 104 | required_fields: list[str] | None = None, |
| 105 | optional_fields: list[str] | None = None, |
| 106 | reserved_fields: list[str] | None = None, |
| 107 | ) -> None: |
| 108 | """ |
| 109 | Check if a configuration class is properly defined. |
| 110 | - It should inherit from Pydantic's BaseModel. |
| 111 | - It should have a model_config attribute. |
| 112 | - It should have a model_fields attribute. |
| 113 | - The factory_fields, required_fields, and optional_fields should be properly defined. |
| 114 | - It should have a ConfigDict as model_config. |
| 115 | |
| 116 | Args: |
| 117 | cls: The config class to check |
| 118 | factory_fields: List of field names with default_factory. |
| 119 | required_fields: List of field names that should be required, despite factory fields. |
| 120 | optional_fields: List of field names that should be optional, despite factory fields. |
| 121 | reserved_fields: List of field names that should be ignored in the checks. |
| 122 | Like fields defined in `memos.configs.base.BaseConfig`. |
| 123 | """ |
| 124 | if reserved_fields is None: |
| 125 | reserved_fields = ["model_schema"] |
| 126 | |
| 127 | # Check if the class is a subclass of BaseModel |
| 128 | assert inspect.isclass(cls), f"{cls} is not a class" |
| 129 | assert issubclass(cls, BaseModel), f"{cls} is not a Pydantic BaseModel" |
| 130 | |
| 131 | # Check model_config |
| 132 | assert cls.model_config == {"extra": "forbid", "strict": True}, ( |
| 133 | f"{cls} does not have the correct model_config" |
| 134 | ) |
| 135 | |
| 136 | # Check model_fields |
| 137 | factory_fields = factory_fields or [] |
| 138 | required_fields = required_fields or [] |
| 139 | optional_fields = optional_fields or [] |
| 140 | actual_factory_fields = [] |
| 141 | actual_required_fields = [] |
| 142 | actual_optional_fields = [] |
| 143 | for field_name, field_info in cls.model_fields.items(): |
| 144 | if field_name in reserved_fields: |
| 145 | continue |
| 146 | elif field_info.default_factory is not None: |
| 147 | actual_factory_fields.append(field_name) |
| 148 | elif field_info.default == PydanticUndefined: |
| 149 | actual_required_fields.append(field_name) |
| 150 | else: |
| 151 | actual_optional_fields.append(field_name) |
| 152 | assert set(actual_factory_fields) == set(factory_fields), ( |
| 153 | f"{cls} has incorrect factory fields: expected {actual_factory_fields}, got {factory_fields}" |
| 154 | ) |
| 155 | assert set(actual_required_fields) == set(required_fields), ( |
| 156 | f"{cls} has incorrect required fields: expected {actual_required_fields}, got {required_fields}" |
| 157 | ) |
| 158 | assert set(actual_optional_fields) == set(optional_fields), ( |