-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathmypy.py
110 lines (89 loc) · 3.95 KB
/
mypy.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
from typing import Callable, Optional, Tuple, Union
from mypy import nodes
from mypy.plugin import ClassDefContext, Plugin
from pydantic.mypy import PydanticModelTransformer, PydanticPlugin
MODEL_METACLASS_FULLNAME = 'pydantic_xml.model.XmlModelMeta'
ATTR_FULLNAME = 'pydantic_xml.model.attr'
ELEMENT_FULLNAME = 'pydantic_xml.model.element'
WRAPPED_FULLNAME = 'pydantic_xml.model.wrapped'
ENTITIES_FULLNAME = (ATTR_FULLNAME, ELEMENT_FULLNAME, WRAPPED_FULLNAME)
def plugin(version: str) -> type[Plugin]:
return PydanticXmlPlugin
class PydanticXmlPlugin(PydanticPlugin):
def get_metaclass_hook(self, fullname: str) -> Optional[Callable[[ClassDefContext], None]]:
if fullname == MODEL_METACLASS_FULLNAME:
return self._pydantic_model_metaclass_marker_callback
return super().get_metaclass_hook(fullname)
def _pydantic_model_class_maker_callback(self, ctx: ClassDefContext) -> bool:
transformer = PydanticXmlModelTransformer(ctx.cls, ctx.reason, ctx.api, self.plugin_config)
return transformer.transform()
class PydanticXmlModelTransformer(PydanticModelTransformer):
@staticmethod
def get_has_default(stmt: nodes.AssignmentStmt) -> bool:
expr = stmt.rvalue
if isinstance(expr, nodes.TempNode):
return False
if (
isinstance(expr, nodes.CallExpr) and
isinstance(expr.callee, nodes.RefExpr) and
expr.callee.fullname in ENTITIES_FULLNAME
):
for arg, name in zip(expr.args, expr.arg_names):
if name == 'default':
return arg.__class__ is not nodes.EllipsisExpr
if name == 'default_factory':
return not (isinstance(arg, nodes.NameExpr) and arg.fullname == 'builtins.None')
return False
return PydanticModelTransformer.get_has_default(stmt)
@staticmethod
def get_alias_info(stmt: nodes.AssignmentStmt) -> Tuple[Union[str, None], bool]:
expr = stmt.rvalue
if isinstance(expr, nodes.TempNode):
return None, False
if (
isinstance(expr, nodes.CallExpr) and
isinstance(expr.callee, nodes.RefExpr) and
expr.callee.fullname in ENTITIES_FULLNAME
):
for arg, arg_name in zip(expr.args, expr.arg_names):
if arg_name != 'alias':
continue
if isinstance(arg, nodes.StrExpr):
return arg.value, False
else:
return None, True
return PydanticModelTransformer.get_alias_info(stmt)
@staticmethod
def get_strict(stmt: nodes.AssignmentStmt) -> Optional[bool]:
expr = stmt.rvalue
if (
isinstance(expr, nodes.CallExpr) and
isinstance(expr.callee, nodes.RefExpr) and
expr.callee.fullname in ENTITIES_FULLNAME
):
for arg, name in zip(expr.args, expr.arg_names):
if name != 'strict':
continue
if isinstance(arg, nodes.NameExpr):
if arg.fullname == 'builtins.True':
return True
elif arg.fullname == 'builtins.False':
return False
return None
return PydanticModelTransformer.get_strict(stmt)
@staticmethod
def is_field_frozen(stmt: nodes.AssignmentStmt) -> bool:
expr = stmt.rvalue
if isinstance(expr, nodes.TempNode):
return False
if not (
isinstance(expr, nodes.CallExpr) and
isinstance(expr.callee, nodes.RefExpr) and
expr.callee.fullname in ENTITIES_FULLNAME
):
return False
for i, arg_name in enumerate(expr.arg_names):
if arg_name == 'frozen':
arg = expr.args[i]
return isinstance(arg, nodes.NameExpr) and arg.fullname == 'builtins.True'
return PydanticModelTransformer.is_field_frozen(stmt)