|
19 | 19 |
|
20 | 20 | __all__ = ( |
21 | 21 | 'attr', |
| 22 | + 'create_model', |
22 | 23 | 'element', |
23 | 24 | 'wrapped', |
24 | 25 | 'computed_attr', |
@@ -250,6 +251,70 @@ def wrapped( |
250 | 251 | ) |
251 | 252 |
|
252 | 253 |
|
| 254 | +Model = TypeVar('Model', bound='BaseXmlModel') |
| 255 | + |
| 256 | + |
| 257 | +def create_model( |
| 258 | + __model_name: str, |
| 259 | + *, |
| 260 | + __tag__: Optional[str] = None, |
| 261 | + __ns__: Optional[str] = None, |
| 262 | + __nsmap__: Optional[NsMap] = None, |
| 263 | + __ns_attrs__: Optional[bool] = None, |
| 264 | + __skip_empty__: Optional[bool] = None, |
| 265 | + __search_mode__: Optional[SearchMode] = None, |
| 266 | + __base__: Union[Type[Model], Tuple[Type[Model], ...], None] = None, |
| 267 | + __module__: Optional[str] = None, |
| 268 | + **kwargs: Any, |
| 269 | +) -> Type[Model]: |
| 270 | + """ |
| 271 | + Dynamically creates a new pydantic-xml model. |
| 272 | +
|
| 273 | + :param __model_name: model name |
| 274 | + :param __tag__: element tag |
| 275 | + :param __ns__: element namespace |
| 276 | + :param __nsmap__: element namespace map |
| 277 | + :param __ns_attrs__: use namespaced attributes |
| 278 | + :param __skip_empty__: skip empty elements (elements without sub-elements, attributes and text) |
| 279 | + :param __search_mode__: element search mode |
| 280 | + :param __base__: model base class |
| 281 | + :param __module__: module name that the model belongs to |
| 282 | + :param kwargs: pydantic model creation arguments. |
| 283 | + See https://docs.pydantic.dev/latest/api/base_model/#pydantic.create_model. |
| 284 | +
|
| 285 | + :return: created model |
| 286 | + """ |
| 287 | + |
| 288 | + cls_kwargs = kwargs.setdefault('__cls_kwargs__', {}) |
| 289 | + cls_kwargs['metaclass'] = XmlModelMeta |
| 290 | + |
| 291 | + cls_kwargs['tag'] = __tag__ |
| 292 | + cls_kwargs['ns'] = __ns__ |
| 293 | + cls_kwargs['nsmap'] = __nsmap__ |
| 294 | + cls_kwargs['ns_attrs'] = __ns_attrs__ |
| 295 | + cls_kwargs['skip_empty'] = __skip_empty__ |
| 296 | + cls_kwargs['search_mode'] = __search_mode__ |
| 297 | + |
| 298 | + model_base: Union[Type[BaseModel], Tuple[Type[BaseModel], ...]] = __base__ or BaseXmlModel |
| 299 | + |
| 300 | + if model_config := kwargs.pop('__config__', None): |
| 301 | + # since pydantic create_model function forbids __base__ and __config__ arguments together, |
| 302 | + # we create base pydantic class with __config__ and inherit from it |
| 303 | + BaseWithConfig = pd.create_model( |
| 304 | + f'{__model_name}Base', |
| 305 | + __module__=__module__, # type: ignore[arg-type] |
| 306 | + __config__=model_config, |
| 307 | + ) |
| 308 | + if not isinstance(model_base, tuple): |
| 309 | + model_base = (model_base, BaseWithConfig) |
| 310 | + else: |
| 311 | + model_base = (*model_base, BaseWithConfig) |
| 312 | + |
| 313 | + model = pd.create_model(__model_name, __base__=model_base, **kwargs) |
| 314 | + |
| 315 | + return typing.cast(Type[Model], model) |
| 316 | + |
| 317 | + |
253 | 318 | @te.dataclass_transform(kw_only_default=True, field_specifiers=(attr, element, wrapped, pd.Field)) |
254 | 319 | class XmlModelMeta(ModelMetaclass): |
255 | 320 | """ |
@@ -305,6 +370,7 @@ def __init_subclass__( |
305 | 370 | :param ns: element namespace |
306 | 371 | :param nsmap: element namespace map |
307 | 372 | :param ns_attrs: use namespaced attributes |
| 373 | + :param skip_empty: skip empty elements (elements without sub-elements, attributes and text) |
308 | 374 | :param search_mode: element search mode |
309 | 375 | """ |
310 | 376 |
|
|
0 commit comments