1
1
import json
2
2
from io import TextIOWrapper
3
- from pathlib import Path
4
- from typing import Any
5
-
6
- from package_parser .commands .find_usages import (
7
- ClassUsage ,
8
- FunctionUsage ,
9
- UsageStore ,
10
- ValueUsage ,
11
- )
3
+
4
+ from package_parser .commands .find_usages import UsageStore
12
5
from package_parser .commands .get_api import API
13
6
from package_parser .utils import parent_qname
14
7
15
8
16
- def generate_annotations (
17
- api_file : TextIOWrapper , usages_file : TextIOWrapper , out_dir : Path
18
- ):
9
+ def generate_annotations (api_file : TextIOWrapper , usages_file : TextIOWrapper , output_file : TextIOWrapper ) -> None :
10
+ # TODO: Make this a command line function?
11
+ """
12
+ Generates an annotation file from the given API and UsageStore files, and writes it to the given output file.
13
+ Annotations that are generated are: constant, unused,
14
+ :param api_file: API file
15
+ :param usages_file: UsageStore file
16
+ :param output_file: Output file
17
+ :return: None
18
+ """
19
+ if api_file is None or usages_file is None or output_file is None :
20
+ raise ValueError ("api_file, usages_file, and output_file must be specified." )
21
+
19
22
with api_file :
20
23
api_json = json .load (api_file )
21
24
api = API .from_json (api_json )
@@ -24,12 +27,114 @@ def generate_annotations(
24
27
usages_json = json .load (usages_file )
25
28
usages = UsageStore .from_json (usages_json )
26
29
27
- # out_dir.mkdir(parents=True, exist_ok=True)
28
- # base_file_name = api_file.name.replace("__api.json", "")
30
+ annotation_functions = [__get_unused_annotations , __get_constant_annotations ]
31
+
32
+ annotations_dict = __generate_annotation_dict (api , usages , annotation_functions )
29
33
34
+ json .dump (annotations_dict , output_file , indent = 2 )
35
+
36
+
37
+ def __generate_annotation_dict (api : API , usages : UsageStore , functions : list [callable ]):
30
38
__preprocess_usages (usages , api )
31
- constant_parameters = __find_constant_parameters (usages , api )
32
- return constant_parameters
39
+
40
+ annotations_dict = {function (usages , api ) for function in functions }
41
+
42
+ return annotations_dict
43
+
44
+
45
+ def __get_constant_annotations (usages : UsageStore , api : API ) -> dict [str , dict [str , dict [str , str ]]]:
46
+ """
47
+ Returns all parameters that are only ever assigned a single value.
48
+ :param usages: UsageStore object
49
+ :param api: API object for usages
50
+ :return: {"constant": dict[str, dict[str, str]]}
51
+ """
52
+ result : dict [str , dict [str , str ]] = {}
53
+
54
+ for parameter_qname in list (usages .parameter_usages .keys ()):
55
+
56
+ if len (usages .value_usages [parameter_qname ].values ()) == 0 :
57
+ continue
58
+
59
+ if len (usages .value_usages [parameter_qname ].keys ()) == 1 :
60
+ if usages .most_common_value (parameter_qname ) is None :
61
+ continue
62
+
63
+ target_name = __qname_to_target_name (api , parameter_qname )
64
+
65
+ default_type , default_value = __get_default_type_from_value (
66
+ str (usages .most_common_value (parameter_qname ))
67
+ )
68
+
69
+ result [target_name ] = {
70
+ "target" : target_name ,
71
+ "defaultType" : default_type ,
72
+ "defaultValue" : default_value ,
73
+ }
74
+
75
+ return {"constant" : result }
76
+
77
+
78
+ def __get_unused_annotations (usages : UsageStore , api : API ) -> dict [str , dict [str , dict [str , str ]]]:
79
+ """
80
+ Returns all parameters that are never used.
81
+ :param usages: UsageStore object
82
+ :param api: API object for usages
83
+ :return: {"unused": dict[str, dict[str, str]]}
84
+ """
85
+ unuseds : dict [str , dict [str , str ]] = {}
86
+
87
+ for parameter_name in list (usages .parameter_usages .keys ()):
88
+ if usages .parameter_usages .get (parameter_name ) == 0 :
89
+ formatted_name = __qname_to_target_name (api , parameter_name )
90
+ unuseds [formatted_name ] = {"target" : formatted_name }
91
+
92
+ return {"unused" : unuseds }
93
+
94
+
95
+ def __qname_to_target_name (api : API , qname : str ) -> str :
96
+ """
97
+ Formats the given name to the wanted format. This method is to be removed as soon as the UsageStore is updated to
98
+ use the new format.
99
+ :param api: API object
100
+ :param qname: Name pre-formatting
101
+ :return: Formatted name
102
+ """
103
+ if qname is None or api is None :
104
+ raise ValueError ("qname and api must be specified." )
105
+
106
+ target_elements = qname .split ("." )
107
+
108
+ package_name = api .package
109
+ module_name = class_name = function_name = parameter_name = ""
110
+
111
+ if "." .join (target_elements ) in api .parameters ().keys ():
112
+ parameter_name = "/" + target_elements .pop ()
113
+ if "." .join (target_elements ) in api .functions .keys ():
114
+ function_name = f"/{ target_elements .pop ()} "
115
+ if "." .join (target_elements ) in api .classes .keys ():
116
+ class_name = f"/{ target_elements .pop ()} "
117
+ if "." .join (target_elements ) in api .modules .keys ():
118
+ module_name = "/" + "." .join (target_elements )
119
+
120
+ return package_name + module_name + class_name + function_name + parameter_name
121
+
122
+
123
+ def __get_default_type_from_value (default_value : str ) -> tuple [str , str ]:
124
+ default_value = str (default_value )[1 :- 1 ]
125
+
126
+ if default_value == "null" :
127
+ default_type = "none"
128
+ elif default_value == "True" or default_value == "False" :
129
+ default_type = "boolean"
130
+ elif default_value .isnumeric ():
131
+ default_type = "number"
132
+ default_value = default_value
133
+ else :
134
+ default_type = "string"
135
+ default_value = default_value
136
+
137
+ return default_type , default_value
33
138
34
139
35
140
def __preprocess_usages (usages : UsageStore , api : API ) -> None :
@@ -121,70 +226,3 @@ def __add_implicit_usages_of_default_value(usages: UsageStore, api: API) -> None
121
226
122
227
for location in locations_of_implicit_usages_of_default_value :
123
228
usages .add_value_usage (parameter_qname , default_value , location )
124
-
125
-
126
- def __find_constant_parameters (
127
- usages : UsageStore , api : API
128
- ) -> dict [str , dict [str , str ]]:
129
- """
130
- Returns all parameters that are only ever assigned a single value.
131
-
132
- :param usages: Usage store
133
- """
134
-
135
- result = {}
136
-
137
- for parameter_qname in list (usages .parameter_usages .keys ()):
138
-
139
- if len (usages .value_usages [parameter_qname ].values ()) == 0 :
140
- continue
141
-
142
- if len (usages .value_usages [parameter_qname ].keys ()) == 1 :
143
- target_name = __qname_to_target_name (api , parameter_qname )
144
- default_type , default_value = __get_default_type_from_value (
145
- str (usages .most_common_value (parameter_qname ))
146
- )
147
- print (target_name )
148
- result [target_name ] = {
149
- "target" : target_name ,
150
- "defaultType" : default_type ,
151
- "defaultValue" : default_value ,
152
- }
153
-
154
- print (json .dumps (result ))
155
- return result
156
-
157
-
158
- def __qname_to_target_name (api : API , qname : str ) -> str :
159
- target_elements = qname .split ("." )
160
-
161
- package_name = api .package
162
- module_name = class_name = function_name = parameter_name = ""
163
-
164
- if "." .join (target_elements ) in api .parameters ().keys ():
165
- parameter_name = "/" + target_elements .pop ()
166
- if "." .join (target_elements ) in api .functions .keys ():
167
- function_name = f"/{ target_elements .pop ()} "
168
- if "." .join (target_elements ) in api .classes .keys ():
169
- class_name = f"/{ target_elements .pop ()} "
170
- if "." .join (target_elements ) in api .modules .keys ():
171
- module_name = "/" + "." .join (target_elements )
172
-
173
- return package_name + module_name + class_name + function_name + parameter_name
174
-
175
-
176
- def __get_default_type_from_value (default_value : str ) -> tuple [str , str ]:
177
- default_value = str (default_value )[1 :- 1 ]
178
-
179
- if default_value == "null" :
180
- default_type = "none"
181
- elif default_value == "True" or default_value == "False" :
182
- default_type = "boolean"
183
- elif default_value .isnumeric ():
184
- default_type = "number"
185
- default_value = default_value
186
- else :
187
- default_type = "string"
188
- default_value = default_value
189
-
190
- return default_type , default_value
0 commit comments