arpakitlib 1.8.200__py3-none-any.whl → 1.8.201__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arpakitlib/clone_pydantic_model_fields.py +43 -0
- {arpakitlib-1.8.200.dist-info → arpakitlib-1.8.201.dist-info}/METADATA +1 -1
- {arpakitlib-1.8.200.dist-info → arpakitlib-1.8.201.dist-info}/RECORD +6 -6
- arpakitlib/prune_model.py +0 -79
- {arpakitlib-1.8.200.dist-info → arpakitlib-1.8.201.dist-info}/LICENSE +0 -0
- {arpakitlib-1.8.200.dist-info → arpakitlib-1.8.201.dist-info}/WHEEL +0 -0
- {arpakitlib-1.8.200.dist-info → arpakitlib-1.8.201.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
from typing import Any, Type
|
2
|
+
|
3
|
+
from pydantic import BaseModel, create_model
|
4
|
+
from pydantic_core import PydanticUndefined
|
5
|
+
|
6
|
+
|
7
|
+
def clone_pydantic_model_fields(
|
8
|
+
*,
|
9
|
+
model_cls: Type[BaseModel],
|
10
|
+
fields_to_remove: set[str],
|
11
|
+
new_class_name: str | None = None,
|
12
|
+
) -> Type[BaseModel]:
|
13
|
+
if new_class_name is None:
|
14
|
+
new_class_name = f"{model_cls.__name__}Cloned"
|
15
|
+
|
16
|
+
field_defs: dict[str, tuple[type[Any], Any]] = {}
|
17
|
+
|
18
|
+
for field_name, field_ in model_cls.model_fields.items():
|
19
|
+
if field_name in fields_to_remove:
|
20
|
+
continue
|
21
|
+
|
22
|
+
if field_.default_factory is not None and field_.default is PydanticUndefined:
|
23
|
+
default = field_
|
24
|
+
elif field_.default is not PydanticUndefined:
|
25
|
+
default = field_.default
|
26
|
+
else:
|
27
|
+
default = field_
|
28
|
+
|
29
|
+
field_defs[field_name] = ((field_.annotation or Any), default)
|
30
|
+
|
31
|
+
return create_model(
|
32
|
+
new_class_name,
|
33
|
+
__base__=BaseModel,
|
34
|
+
**field_defs,
|
35
|
+
)
|
36
|
+
|
37
|
+
|
38
|
+
def __example():
|
39
|
+
pass
|
40
|
+
|
41
|
+
|
42
|
+
if __name__ == '__main__':
|
43
|
+
__example()
|
@@ -413,9 +413,9 @@ arpakitlib/ar_sqlalchemy_util.py,sha256=hiDh1GrFHmnqa6lJPMq4fb9m3_fs-eDKuRQzbFxI
|
|
413
413
|
arpakitlib/ar_str_util.py,sha256=2lGpnXDf2h1cBZpVf5i1tX_HCv5iBd6IGnrCw4QWWlY,4350
|
414
414
|
arpakitlib/ar_type_util.py,sha256=Cs_tef-Fc5xeyAF54KgISCsP11NHyzIsglm4S3Xx7iM,4049
|
415
415
|
arpakitlib/ar_yookassa_api_client_util.py,sha256=VozuZeCJjmLd1zj2BdC9WfiAQ3XYOrIMsdpNK-AUlm0,5347
|
416
|
-
arpakitlib/
|
417
|
-
arpakitlib-1.8.
|
418
|
-
arpakitlib-1.8.
|
419
|
-
arpakitlib-1.8.
|
420
|
-
arpakitlib-1.8.
|
421
|
-
arpakitlib-1.8.
|
416
|
+
arpakitlib/clone_pydantic_model_fields.py,sha256=breicLEvj9tMJxyZDM93P_Mi1WpSqkuHVBSNLB1PwVM,1072
|
417
|
+
arpakitlib-1.8.201.dist-info/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
|
418
|
+
arpakitlib-1.8.201.dist-info/METADATA,sha256=4-wwzeEiw4Wc3oOAzfUKmecDRVpu-gzTahMI-P5j4r4,3741
|
419
|
+
arpakitlib-1.8.201.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
420
|
+
arpakitlib-1.8.201.dist-info/entry_points.txt,sha256=36xqR3PJFT2kuwjkM_EqoIy0qFUDPKSm_mJaI7emewE,87
|
421
|
+
arpakitlib-1.8.201.dist-info/RECORD,,
|
arpakitlib/prune_model.py
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
import copy
|
2
|
-
from typing import Type
|
3
|
-
|
4
|
-
from pydantic import BaseModel
|
5
|
-
from pydantic.fields import FieldInfo, Field
|
6
|
-
|
7
|
-
|
8
|
-
def prune_model(
|
9
|
-
*,
|
10
|
-
model_cls: Type[BaseModel],
|
11
|
-
fields_to_remove: set[str],
|
12
|
-
new_class_name: str,
|
13
|
-
) -> Type[BaseModel]:
|
14
|
-
"""
|
15
|
-
Создаёт новый класс-модель (Pydantic v2), который:
|
16
|
-
- наследуется от тех же баз, что и model_cls (в том же порядке);
|
17
|
-
- содержит только поля, объявленные В САМОМ model_cls (а не у его баз),
|
18
|
-
за вычетом fields_to_remove;
|
19
|
-
- копирует каждое поле целиком через deepcopy(FieldInfo), сохраняя ВСЕ атрибуты
|
20
|
-
(alias, constraints, repr, json_schema_extra и т.д.).
|
21
|
-
- НЕ копирует валидаторы, конфиг, docstring и прочее — только поля.
|
22
|
-
|
23
|
-
Наследованные от баз поля сохранятся за счёт наследования. Удалить наследуемое поле
|
24
|
-
таким способом нельзя — нужно менять базовые классы / MRO.
|
25
|
-
"""
|
26
|
-
if not (isinstance(model_cls, type) and issubclass(model_cls, BaseModel)):
|
27
|
-
raise TypeError("model_cls должен быть подклассом pydantic.BaseModel (v2).")
|
28
|
-
|
29
|
-
namespace: dict = {
|
30
|
-
"__module__": getattr(model_cls, "__module__", "__main__"),
|
31
|
-
"__annotations__": {},
|
32
|
-
}
|
33
|
-
|
34
|
-
for name, annotation in dict(getattr(model_cls, "__annotations__", {})).items():
|
35
|
-
if name in fields_to_remove:
|
36
|
-
continue
|
37
|
-
|
38
|
-
field_info: FieldInfo | None = getattr(model_cls, "model_fields", {}).get(name)
|
39
|
-
|
40
|
-
if isinstance(field_info, FieldInfo):
|
41
|
-
namespace["__annotations__"][name] = annotation
|
42
|
-
namespace[name] = copy.deepcopy(field_info)
|
43
|
-
|
44
|
-
new_model = type(new_class_name, model_cls.__bases__, namespace)
|
45
|
-
|
46
|
-
if not issubclass(new_model, BaseModel):
|
47
|
-
raise RuntimeError("not issubclass(new_model, BaseModel)")
|
48
|
-
|
49
|
-
return new_model
|
50
|
-
|
51
|
-
|
52
|
-
def __example():
|
53
|
-
from typing import Optional
|
54
|
-
from pydantic import BaseModel
|
55
|
-
|
56
|
-
class Timestamped(BaseModel):
|
57
|
-
created_at: int = 1
|
58
|
-
updated_at: int = 1
|
59
|
-
|
60
|
-
class Names:
|
61
|
-
arsen = "arse"
|
62
|
-
|
63
|
-
class User(Timestamped):
|
64
|
-
id: int = Field(default=1)
|
65
|
-
email: str = "asasf"
|
66
|
-
password_hash: str = "asasf"
|
67
|
-
nickname: Optional[str] = None
|
68
|
-
|
69
|
-
PublicUser = prune_model(
|
70
|
-
model_cls=User,
|
71
|
-
fields_to_remove={"id"},
|
72
|
-
new_class_name="PublicUser",
|
73
|
-
)
|
74
|
-
|
75
|
-
print(PublicUser())
|
76
|
-
|
77
|
-
|
78
|
-
if __name__ == '__main__':
|
79
|
-
__example()
|
File without changes
|
File without changes
|
File without changes
|