12 |
12 |
13 |
13 |
14 def initService(): |
14 def initService(): |
15 """ |
15 """ |
16 Initialize the service and return the entry point. |
16 Initialize the service and return the entry point. |
17 |
17 |
18 @return the entry point for the background client (function) |
18 @return the entry point for the background client (function) |
19 """ |
19 """ |
20 return cyclomaticComplexity |
20 return cyclomaticComplexity |
21 |
21 |
22 |
22 |
23 def initBatchService(): |
23 def initBatchService(): |
24 """ |
24 """ |
25 Initialize the batch service and return the entry point. |
25 Initialize the batch service and return the entry point. |
26 |
26 |
27 @return the entry point for the background client (function) |
27 @return the entry point for the background client (function) |
28 """ |
28 """ |
29 return batchCyclomaticComplexity |
29 return batchCyclomaticComplexity |
30 |
30 |
31 |
31 |
32 def cyclomaticComplexity(file, text=""): |
32 def cyclomaticComplexity(file, text=""): |
33 """ |
33 """ |
34 Private function to calculate the cyclomatic complexity of one file. |
34 Private function to calculate the cyclomatic complexity of one file. |
35 |
35 |
36 @param file source filename |
36 @param file source filename |
37 @type str |
37 @type str |
38 @param text source text |
38 @param text source text |
39 @type str |
39 @type str |
40 @return tuple containing the result dictionary |
40 @return tuple containing the result dictionary |
41 @rtype (tuple of dict) |
41 @rtype (tuple of dict) |
42 """ |
42 """ |
43 return __cyclomaticComplexity(file, text) |
43 return __cyclomaticComplexity(file, text) |
44 |
44 |
45 |
45 |
46 def batchCyclomaticComplexity(argumentsList, send, fx, cancelled, |
46 def batchCyclomaticComplexity(argumentsList, send, fx, cancelled, maxProcesses=0): |
47 maxProcesses=0): |
|
48 """ |
47 """ |
49 Module function to calculate the cyclomatic complexity for a batch of |
48 Module function to calculate the cyclomatic complexity for a batch of |
50 files. |
49 files. |
51 |
50 |
52 @param argumentsList list of arguments tuples as given for |
51 @param argumentsList list of arguments tuples as given for |
53 cyclomaticComplexity |
52 cyclomaticComplexity |
54 @type list |
53 @type list |
55 @param send reference to send function |
54 @param send reference to send function |
56 @type function |
55 @type function |
81 for task in argumentsList[:initialTasks]: |
80 for task in argumentsList[:initialTasks]: |
82 taskQueue.put(task) |
81 taskQueue.put(task) |
83 |
82 |
84 # Start worker processes |
83 # Start worker processes |
85 workers = [ |
84 workers = [ |
86 multiprocessing.Process( |
85 multiprocessing.Process(target=workerTask, args=(taskQueue, doneQueue)) |
87 target=workerTask, args=(taskQueue, doneQueue) |
86 for _ in range(NumberOfProcesses) |
88 ) for _ in range(NumberOfProcesses) |
|
89 ] |
87 ] |
90 for worker in workers: |
88 for worker in workers: |
91 worker.start() |
89 worker.start() |
92 |
90 |
93 # Get and send results |
91 # Get and send results |
94 endIndex = len(argumentsList) - initialTasks |
92 endIndex = len(argumentsList) - initialTasks |
95 for i in range(len(argumentsList)): |
93 for i in range(len(argumentsList)): |
96 resultSent = False |
94 resultSent = False |
97 wasCancelled = False |
95 wasCancelled = False |
98 |
96 |
99 while not resultSent: |
97 while not resultSent: |
100 try: |
98 try: |
101 # get result (waiting max. 3 seconds and send it to frontend |
99 # get result (waiting max. 3 seconds and send it to frontend |
102 filename, result = doneQueue.get() |
100 filename, result = doneQueue.get() |
103 send(fx, filename, result) |
101 send(fx, filename, result) |
105 except queue.Empty: |
103 except queue.Empty: |
106 # ignore empty queue, just carry on |
104 # ignore empty queue, just carry on |
107 if cancelled(): |
105 if cancelled(): |
108 wasCancelled = True |
106 wasCancelled = True |
109 break |
107 break |
110 |
108 |
111 if wasCancelled or cancelled(): |
109 if wasCancelled or cancelled(): |
112 # just exit the loop ignoring the results of queued tasks |
110 # just exit the loop ignoring the results of queued tasks |
113 break |
111 break |
114 |
112 |
115 if i < endIndex: |
113 if i < endIndex: |
116 taskQueue.put(argumentsList[i + initialTasks]) |
114 taskQueue.put(argumentsList[i + initialTasks]) |
117 |
115 |
118 # Tell child processes to stop |
116 # Tell child processes to stop |
119 for _ in range(NumberOfProcesses): |
117 for _ in range(NumberOfProcesses): |
120 taskQueue.put('STOP') |
118 taskQueue.put("STOP") |
121 |
119 |
122 for worker in workers: |
120 for worker in workers: |
123 worker.join() |
121 worker.join() |
124 worker.close() |
122 worker.close() |
125 |
123 |
126 |
124 |
127 def workerTask(inputQueue, outputQueue): |
125 def workerTask(inputQueue, outputQueue): |
128 """ |
126 """ |
129 Module function acting as the parallel worker for the cyclomatic |
127 Module function acting as the parallel worker for the cyclomatic |
130 complexity calculation. |
128 complexity calculation. |
131 |
129 |
132 @param inputQueue input queue |
130 @param inputQueue input queue |
133 @type multiprocessing.Queue |
131 @type multiprocessing.Queue |
134 @param outputQueue output queue |
132 @param outputQueue output queue |
135 @type multiprocessing.Queue |
133 @type multiprocessing.Queue |
136 """ |
134 """ |
137 for filename, source in iter(inputQueue.get, 'STOP'): |
135 for filename, source in iter(inputQueue.get, "STOP"): |
138 result = __cyclomaticComplexity(filename, source) |
136 result = __cyclomaticComplexity(filename, source) |
139 outputQueue.put((filename, result)) |
137 outputQueue.put((filename, result)) |
140 |
138 |
141 |
139 |
142 def __cyclomaticComplexity(file, text=""): |
140 def __cyclomaticComplexity(file, text=""): |
143 """ |
141 """ |
144 Private function to calculate the cyclomatic complexity for one Python |
142 Private function to calculate the cyclomatic complexity for one Python |
145 file. |
143 file. |
146 |
144 |
147 @param file source filename |
145 @param file source filename |
148 @type str |
146 @type str |
149 @param text source text |
147 @param text source text |
150 @type str |
148 @type str |
151 @return tuple containing the result dictionary |
149 @return tuple containing the result dictionary |
152 @rtype (tuple of dict) |
150 @rtype (tuple of dict) |
153 """ |
151 """ |
154 from radon.complexity import cc_visit, cc_rank |
152 from radon.complexity import cc_visit, cc_rank |
155 |
153 |
156 try: |
154 try: |
157 cc = cc_visit(text) |
155 cc = cc_visit(text) |
158 res = {"result": [v for v in map(__cc2Dict, cc) |
156 res = {"result": [v for v in map(__cc2Dict, cc) if v["type"] != "method"]} |
159 if v["type"] != "method"]} |
|
160 totalCC = 0 |
157 totalCC = 0 |
161 rankSummary = { |
158 rankSummary = { |
162 "A": 0, |
159 "A": 0, |
163 "B": 0, |
160 "B": 0, |
164 "C": 0, |
161 "C": 0, |
172 res["total_cc"] = totalCC |
169 res["total_cc"] = totalCC |
173 res["count"] = len(cc) |
170 res["count"] = len(cc) |
174 res["summary"] = rankSummary |
171 res["summary"] = rankSummary |
175 except Exception as err: |
172 except Exception as err: |
176 res = {"error": str(err)} |
173 res = {"error": str(err)} |
177 return (res, ) |
174 return (res,) |
178 |
175 |
179 |
176 |
180 def __cc2Dict(obj): |
177 def __cc2Dict(obj): |
181 """ |
178 """ |
182 Private function to convert an object holding cyclomatic complexity results |
179 Private function to convert an object holding cyclomatic complexity results |
183 into a dictionary. |
180 into a dictionary. |
184 |
181 |
185 @param obj object as returned from cc_visit() |
182 @param obj object as returned from cc_visit() |
186 @type radon.visitors.Function |
183 @type radon.visitors.Function |
187 @return conversion result |
184 @return conversion result |
188 @rtype dict |
185 @rtype dict |
189 """ |
186 """ |
190 from radon.complexity import cc_rank |
187 from radon.complexity import cc_rank |
191 from radon.visitors import Function |
188 from radon.visitors import Function |
192 |
189 |
193 result = { |
190 result = { |
194 'type': __getType(obj), |
191 "type": __getType(obj), |
195 'rank': cc_rank(obj.complexity), |
192 "rank": cc_rank(obj.complexity), |
196 } |
193 } |
197 attrs = set(Function._fields) - {'is_method', 'closures'} |
194 attrs = set(Function._fields) - {"is_method", "closures"} |
198 attrs.add("fullname") |
195 attrs.add("fullname") |
199 for attr in attrs: |
196 for attr in attrs: |
200 v = getattr(obj, attr, None) |
197 v = getattr(obj, attr, None) |
201 if v is not None: |
198 if v is not None: |
202 result[attr] = v |
199 result[attr] = v |
203 for key in ('methods', 'closures'): |
200 for key in ("methods", "closures"): |
204 if hasattr(obj, key): |
201 if hasattr(obj, key): |
205 result[key] = list(map(__cc2Dict, getattr(obj, key))) |
202 result[key] = list(map(__cc2Dict, getattr(obj, key))) |
206 return result |
203 return result |
207 |
204 |
208 |
205 |
209 def __getType(obj): |
206 def __getType(obj): |
210 """ |
207 """ |
211 Private function to get the type of an object as a string. |
208 Private function to get the type of an object as a string. |
212 |
209 |
213 @param obj object to be analyzed |
210 @param obj object to be analyzed |
214 @type radon.visitors.Function or radon.visitors.Class |
211 @type radon.visitors.Function or radon.visitors.Class |
215 @return type string for the object |
212 @return type string for the object |
216 @rtype str, one of ["method", "function", "class"] |
213 @rtype str, one of ["method", "function", "class"] |
217 """ |
214 """ |
218 from radon.visitors import Function |
215 from radon.visitors import Function |
219 |
216 |
220 if isinstance(obj, Function): |
217 if isinstance(obj, Function): |
221 if obj.is_method: |
218 if obj.is_method: |
222 return 'method' |
219 return "method" |
223 else: |
220 else: |
224 return 'function' |
221 return "function" |
225 else: |
222 else: |
226 return 'class' |
223 return "class" |