11
11
import types
12
12
import tempfile
13
13
import re
14
- from typing import Dict , Any , Optional , List
14
+ from typing import Dict , Any , Optional , List , ForwardRef
15
15
16
16
# Because I'm subprocessing myself, I need to do weird thing as import.
17
17
try :
@@ -41,6 +41,10 @@ def create_empty_report():
41
41
return {"client" : {}, "models" : {"enums" : {}, "exceptions" : {}, "models" : {}}, "operations" : {}}
42
42
43
43
44
+ def is_model (model_cls : object , is_new_model : bool ) -> bool :
45
+ return hasattr (model_cls , "_is_model" if is_new_model else "_attribute_map" )
46
+
47
+
44
48
def create_report (module_name : str ) -> Dict [str , Any ]:
45
49
module_to_generate = importlib .import_module (module_name )
46
50
client_name = getattr (module_to_generate , "__all__" )
@@ -54,14 +58,15 @@ def create_report(module_name: str) -> Dict[str, Any]:
54
58
55
59
# Look for models first
56
60
model_names = [model_name for model_name in dir (module_to_generate .models ) if model_name [0 ].isupper ()]
61
+ is_new_model = hasattr (module_to_generate , "_model_base" )
57
62
for model_name in model_names :
58
63
model_cls = getattr (module_to_generate .models , model_name )
59
- if hasattr (model_cls , "_attribute_map" ):
60
- report ["models" ]["models" ][model_name ] = create_model_report (model_cls )
64
+ if is_model (model_cls , is_new_model ):
65
+ report ["models" ]["models" ][model_name ] = create_model_report (model_cls , is_new_model )
61
66
elif issubclass (model_cls , Exception ): # If not, might be an exception
62
- report ["models" ]["exceptions" ][model_name ] = create_model_report (model_cls )
67
+ report ["models" ]["exceptions" ][model_name ] = create_model_report (model_cls , is_new_model )
63
68
else :
64
- report ["models" ]["enums" ][model_name ] = create_model_report (model_cls )
69
+ report ["models" ]["enums" ][model_name ] = create_model_report (model_cls , is_new_model )
65
70
# Look for operation groups
66
71
try :
67
72
operations_classes = [op_name for op_name in dir (module_to_generate .operations ) if op_name [0 ].isupper ()]
@@ -83,22 +88,69 @@ def create_report(module_name: str) -> Dict[str, Any]:
83
88
return report
84
89
85
90
86
- def create_model_report (model_cls ):
91
+ def get_attr_map (model_cls : object , is_new_model : bool ) -> Dict [str , Any ]:
92
+ if is_new_model :
93
+ return getattr (model_cls (), "_attr_to_rest_field" )
94
+ return getattr (model_cls , "_attribute_map" )
95
+
96
+
97
+ def get_type_annotation (model_cls : object , attribute : str ) -> List [str ]:
98
+ # make sure to get the annotations from the base class
99
+ mros = model_cls .__mro__ [:- 3 ][::- 1 ]
100
+ annotations = {
101
+ k : v
102
+ for mro_class in mros
103
+ if hasattr (mro_class , "__annotations__" ) # pylint: disable=no-member
104
+ for k , v in mro_class .__annotations__ .items () # pylint: disable=no-member
105
+ }
106
+ attr_type = annotations .get (attribute )
107
+ type_list = getattr (attr_type , "__args__" , [attr_type ])
108
+ return sorted (
109
+ [
110
+ item .__forward_arg__ .replace ("_models." , "" )
111
+ if isinstance (item , ForwardRef )
112
+ else getattr (item , "__name__" , str (item ))
113
+ for item in type_list
114
+ ]
115
+ )
116
+
117
+
118
+ def get_type (model_cls : object , attribute : str , conf : Dict [str , Any ], is_new_model : bool ) -> str :
119
+ if is_new_model :
120
+ return " or " .join (filter (lambda x : x != "NoneType" , get_type_annotation (model_cls , attribute )))
121
+ return conf ["type" ]
122
+
123
+
124
+ def _get_validation (model_cls : object , attribute : str ) -> Dict [str , Any ]:
125
+ return getattr (model_cls , "_validation" , {}).get (attribute , {})
126
+
127
+
128
+ def is_required (model_cls : object , attribute : str , is_new_model : bool ) -> bool :
129
+ if is_new_model :
130
+ return "NoneType" not in get_type_annotation (model_cls , attribute )
131
+ return _get_validation (model_cls , attribute ).get ("required" , False )
132
+
133
+
134
+ def is_readonly (model_cls : object , attribute : str , is_new_model : bool ) -> bool :
135
+ if is_new_model :
136
+ return getattr (getattr (model_cls , "_attr_to_rest_field" ).get (attribute ), "_visibility" ) == ["read" ]
137
+ return _get_validation (model_cls , attribute ).get ("readonly" , False )
138
+
139
+
140
+ def create_model_report (model_cls : object , is_new_model : bool ):
87
141
result = {
88
142
"name" : model_cls .__name__ ,
89
143
}
90
144
# If _attribute_map, it's a model
91
- if hasattr (model_cls , "_attribute_map" ):
145
+ if is_model (model_cls , is_new_model ):
92
146
result ["type" ] = "Model"
93
- for attribute , conf in model_cls ._attribute_map .items ():
94
- attribute_validation = getattr (model_cls , "_validation" , {}).get (attribute , {})
95
-
147
+ for attribute , conf in get_attr_map (model_cls , is_new_model ).items ():
96
148
result .setdefault ("parameters" , {})[attribute ] = {
97
149
"name" : attribute ,
98
150
"properties" : {
99
- "type" : conf [ "type" ] ,
100
- "required" : attribute_validation . get ( "required" , False ),
101
- "readonly" : attribute_validation . get ( "readonly" , False ),
151
+ "type" : get_type ( model_cls , attribute , conf , is_new_model ) ,
152
+ "required" : is_required ( model_cls , attribute , is_new_model ),
153
+ "readonly" : is_readonly ( model_cls , attribute , is_new_model ),
102
154
},
103
155
}
104
156
elif issubclass (model_cls , Exception ): # If not, might be an exception
@@ -190,7 +242,6 @@ def main(
190
242
metadata_path : Optional [str ] = None ,
191
243
last_pypi_stable : bool = False ,
192
244
):
193
-
194
245
output_msg = output if output else "default folder"
195
246
_LOGGER .info (
196
247
f"Building code report of { input_parameter } for version { version } in { output_msg } ({ no_venv } /{ pypi } /{ last_pypi } )"
0 commit comments