8
8
from package_parser .models .annotation_models import (
9
9
AnnotationStore ,
10
10
ConstantAnnotation ,
11
+ ParameterInfo ,
12
+ ParameterType ,
13
+ RequiredAnnotation ,
11
14
UnusedAnnotation ,
12
15
)
13
16
from package_parser .utils import parent_qname
@@ -22,10 +25,9 @@ def generate_annotations(
22
25
:param api_file: API file
23
26
:param usages_file: UsageStore file
24
27
:param output_file: Output file
25
- :return: None
26
28
"""
27
29
if api_file is None or usages_file is None or output_file is None :
28
- raise ValueError ("api_file , usages_file, and output_file must be specified." )
30
+ raise ValueError ("Api_file , usages_file, and output_file must be specified." )
29
31
30
32
with api_file :
31
33
api_json = json .load (api_file )
@@ -36,7 +38,11 @@ def generate_annotations(
36
38
usages = UsageStore .from_json (usages_json )
37
39
38
40
annotations = AnnotationStore ()
39
- annotation_functions = [__get_unused_annotations , __get_constant_annotations ]
41
+ annotation_functions = [
42
+ __get_unused_annotations ,
43
+ __get_constant_annotations ,
44
+ __get_required_annotations ,
45
+ ]
40
46
41
47
__generate_annotation_dict (api , usages , annotations , annotation_functions )
42
48
@@ -60,29 +66,21 @@ def __get_constant_annotations(
60
66
usages : UsageStore , api : API , annotations : AnnotationStore
61
67
) -> None :
62
68
"""
63
- Returns all parameters that are only ever assigned a single value.
69
+ Collect all parameters that are only ever assigned a single value.
64
70
:param usages: UsageStore object
65
71
:param api: API object for usages
66
- :return: {"constant": dict[str, dict[str, str]]}
72
+ :param annotations: AnnotationStore, that holds all annotations
67
73
"""
68
- for parameter_qname in list (usages .parameter_usages .keys ()):
69
- if len (usages .value_usages [parameter_qname ].values ()) == 0 :
70
- continue
71
-
72
- if len (usages .value_usages [parameter_qname ].keys ()) == 1 :
73
- if usages .most_common_value (parameter_qname ) is None :
74
- continue
75
-
76
- target_name = __qname_to_target_name (api , parameter_qname )
77
- default_type , default_value = __get_default_type_from_value (
78
- str (usages .most_common_value (parameter_qname ))
79
- )
74
+ for qname in list (usages .parameter_usages .keys ()):
75
+ parameter_info = __get_parameter_info (qname , usages )
80
76
77
+ if parameter_info .type == ParameterType .Constant :
78
+ formatted_name = __qname_to_target_name (api , qname )
81
79
annotations .constant .append (
82
80
ConstantAnnotation (
83
- target = target_name ,
84
- defaultType = default_type ,
85
- defaultValue = default_value ,
81
+ target = formatted_name ,
82
+ defaultValue = parameter_info . value ,
83
+ defaultType = parameter_info . value_type ,
86
84
)
87
85
)
88
86
@@ -91,10 +89,10 @@ def __get_unused_annotations(
91
89
usages : UsageStore , api : API , annotations : AnnotationStore
92
90
) -> None :
93
91
"""
94
- Returns all parameters that are never used.
92
+ Collect all parameters, functions and classes that are never used.
95
93
:param usages: UsageStore object
96
94
:param api: API object for usages
97
- :return: {"unused": dict[str, dict[str, str]]}
95
+ :param annotations: AnnotationStore, that holds all annotations
98
96
"""
99
97
for parameter_name in list (api .parameters ().keys ()):
100
98
if (
@@ -121,9 +119,31 @@ def __get_unused_annotations(
121
119
annotations .unused .append (UnusedAnnotation (formatted_name ))
122
120
123
121
122
+ def __get_required_annotations (
123
+ usages : UsageStore , api : API , annotations : AnnotationStore
124
+ ) -> None :
125
+ """
126
+ Collects all parameters that are currently optional but should be required to be assign a value
127
+ :param usages: Usage store
128
+ :param api: Description of the API
129
+ :param annotations: AnnotationStore, that holds all annotations
130
+ """
131
+ parameters = api .parameters ()
132
+ optional_parameter = [
133
+ (it , parameters [it ])
134
+ for it in parameters
135
+ if parameters [it ].default_value is not None
136
+ ]
137
+ for qname , _ in optional_parameter :
138
+
139
+ if __get_parameter_info (qname , usages ).type is ParameterType .Required :
140
+ formatted_name = __qname_to_target_name (api , qname )
141
+ annotations .requireds .append (RequiredAnnotation (formatted_name ))
142
+
143
+
124
144
def __qname_to_target_name (api : API , qname : str ) -> str :
125
145
"""
126
- Formats the given name to the wanted format. This method is to be removed as soon as the UsageStore is updated to
146
+ Formats the given name to the output format. This method is to be removed as soon as the UsageStore is updated to
127
147
use the new format.
128
148
:param api: API object
129
149
:param qname: Name pre-formatting
@@ -149,21 +169,17 @@ def __qname_to_target_name(api: API, qname: str) -> str:
149
169
return package_name + module_name + class_name + function_name + parameter_name
150
170
151
171
152
- def __get_default_type_from_value (default_value : str ) -> tuple [str , str ]:
153
- default_value = str (default_value )[1 :- 1 ]
154
-
172
+ def __get_default_type_from_value (default_value : str ) -> str :
155
173
if default_value == "null" :
156
174
default_type = "none"
157
175
elif default_value == "True" or default_value == "False" :
158
176
default_type = "boolean"
159
177
elif default_value .isnumeric ():
160
178
default_type = "number"
161
- default_value = default_value
162
179
else :
163
180
default_type = "string"
164
- default_value = default_value
165
181
166
- return default_type , default_value
182
+ return default_type
167
183
168
184
169
185
def _preprocess_usages (usages : UsageStore , api : API ) -> None :
@@ -255,3 +271,48 @@ def __add_implicit_usages_of_default_value(usages: UsageStore, api: API) -> None
255
271
256
272
for location in locations_of_implicit_usages_of_default_value :
257
273
usages .add_value_usage (parameter_qname , default_value , location )
274
+
275
+
276
+ def __get_parameter_info (qname : str , usages : UsageStore ) -> ParameterInfo :
277
+ """
278
+ Returns a ParameterInfo object, that contains the type of the parameter, the value that is associated with it, and the values type
279
+ :param qname: name of the parameter
280
+ :param usages: UsageStore
281
+ :return ParameterInfo
282
+ """
283
+ values = [(it [0 ], len (it [1 ])) for it in usages .value_usages [qname ].items ()]
284
+
285
+ if len (values ) == 0 :
286
+ return ParameterInfo (ParameterType .Unused )
287
+ elif len (values ) == 1 :
288
+ value = values [0 ][0 ]
289
+ if value [0 ] == "'" :
290
+ value = value [1 :- 1 ]
291
+ return ParameterInfo (
292
+ ParameterType .Constant , value , __get_default_type_from_value (value )
293
+ )
294
+
295
+ if __is_required (values ):
296
+ return ParameterInfo (ParameterType .Required )
297
+
298
+ value = max (values , key = lambda item : item [1 ])[0 ]
299
+ if value [0 ] == "'" :
300
+ value = value [1 :- 1 ]
301
+ return ParameterInfo (
302
+ ParameterType .Optional , value , __get_default_type_from_value (value )
303
+ )
304
+
305
+
306
+ def __is_required (values : list [tuple [str , int ]]) -> bool :
307
+ """
308
+ This replaceable function determines how to differentiate between an optional and a required parameter
309
+ :param values: List of all associated values and the amount they get used with
310
+ :return True means the parameter should be required, False means it should be optional
311
+ """
312
+ n = len (values )
313
+ m = sum ([count for value , count in values ])
314
+
315
+ seconds_most_used_value_tupel , most_used_value_tupel = sorted (
316
+ values , key = lambda tup : tup [1 ]
317
+ )[- 2 :]
318
+ return most_used_value_tupel [1 ] - seconds_most_used_value_tupel [1 ] <= m / n
0 commit comments