66 @type bool |
69 @type bool |
67 @param args dictionary of arguments for the annotation checks |
70 @param args dictionary of arguments for the annotation checks |
68 @type dict |
71 @type dict |
69 """ |
72 """ |
70 self.__select = tuple(select) |
73 self.__select = tuple(select) |
71 self.__ignore = ('',) if select else tuple(ignore) |
74 self.__ignore = ("",) if select else tuple(ignore) |
72 self.__expected = expected[:] |
75 self.__expected = expected[:] |
73 self.__repeat = repeat |
76 self.__repeat = repeat |
74 self.__filename = filename |
77 self.__filename = filename |
75 self.__source = source[:] |
78 self.__source = source[:] |
76 self.__tree = copy.deepcopy(tree) |
79 self.__tree = copy.deepcopy(tree) |
77 self.__args = args |
80 self.__args = args |
78 |
81 |
79 # statistics counters |
82 # statistics counters |
80 self.counters = {} |
83 self.counters = {} |
81 |
84 |
82 # collection of detected errors |
85 # collection of detected errors |
83 self.errors = [] |
86 self.errors = [] |
84 |
87 |
85 checkersWithCodes = [ |
88 checkersWithCodes = [ |
86 ( |
89 ( |
87 self.__checkFunctionAnnotations, |
90 self.__checkFunctionAnnotations, |
88 ("A001", "A002", "A003", "A101", "A102", |
91 ( |
89 "A201", "A202", "A203", "A204", "A205", "A206", |
92 "A001", |
90 "A301", ) |
93 "A002", |
|
94 "A003", |
|
95 "A101", |
|
96 "A102", |
|
97 "A201", |
|
98 "A202", |
|
99 "A203", |
|
100 "A204", |
|
101 "A205", |
|
102 "A206", |
|
103 "A301", |
|
104 ), |
91 ), |
105 ), |
92 (self.__checkAnnotationsFuture, ("A871",)), |
106 (self.__checkAnnotationsFuture, ("A871",)), |
93 (self.__checkAnnotationsCoverage, ("A881",)), |
107 (self.__checkAnnotationsCoverage, ("A881",)), |
94 (self.__checkAnnotationComplexity, ("A891", "A892")), |
108 (self.__checkAnnotationComplexity, ("A891", "A892")), |
95 ] |
109 ] |
96 |
110 |
97 self.__checkers = [] |
111 self.__checkers = [] |
98 for checker, codes in checkersWithCodes: |
112 for checker, codes in checkersWithCodes: |
99 if any(not (code and self.__ignoreCode(code)) |
113 if any(not (code and self.__ignoreCode(code)) for code in codes): |
100 for code in codes): |
|
101 self.__checkers.append(checker) |
114 self.__checkers.append(checker) |
102 |
115 |
103 def __ignoreCode(self, code): |
116 def __ignoreCode(self, code): |
104 """ |
117 """ |
105 Private method to check if the message code should be ignored. |
118 Private method to check if the message code should be ignored. |
106 |
119 |
107 @param code message code to check for |
120 @param code message code to check for |
108 @type str |
121 @type str |
109 @return flag indicating to ignore the given code |
122 @return flag indicating to ignore the given code |
110 @rtype bool |
123 @rtype bool |
111 """ |
124 """ |
112 return (code.startswith(self.__ignore) and |
125 return code.startswith(self.__ignore) and not code.startswith(self.__select) |
113 not code.startswith(self.__select)) |
126 |
114 |
|
115 def __error(self, lineNumber, offset, code, *args): |
127 def __error(self, lineNumber, offset, code, *args): |
116 """ |
128 """ |
117 Private method to record an issue. |
129 Private method to record an issue. |
118 |
130 |
119 @param lineNumber line number of the issue |
131 @param lineNumber line number of the issue |
120 @type int |
132 @type int |
121 @param offset position within line of the issue |
133 @param offset position within line of the issue |
122 @type int |
134 @type int |
123 @param code message code |
135 @param code message code |
146 "offset": offset, |
158 "offset": offset, |
147 "code": code, |
159 "code": code, |
148 "args": args, |
160 "args": args, |
149 } |
161 } |
150 ) |
162 ) |
151 |
163 |
152 def run(self): |
164 def run(self): |
153 """ |
165 """ |
154 Public method to check the given source against annotation issues. |
166 Public method to check the given source against annotation issues. |
155 """ |
167 """ |
156 if not self.__filename: |
168 if not self.__filename: |
157 # don't do anything, if essential data is missing |
169 # don't do anything, if essential data is missing |
158 return |
170 return |
159 |
171 |
160 if not self.__checkers: |
172 if not self.__checkers: |
161 # don't do anything, if no codes were selected |
173 # don't do anything, if no codes were selected |
162 return |
174 return |
163 |
175 |
164 for check in self.__checkers: |
176 for check in self.__checkers: |
165 check() |
177 check() |
166 |
178 |
167 ####################################################################### |
179 ####################################################################### |
168 ## Annotations |
180 ## Annotations |
169 ## |
181 ## |
170 ## adapted from: flake8-annotations v2.7.0 |
182 ## adapted from: flake8-annotations v2.7.0 |
171 ####################################################################### |
183 ####################################################################### |
172 |
184 |
173 def __checkFunctionAnnotations(self): |
185 def __checkFunctionAnnotations(self): |
174 """ |
186 """ |
175 Private method to check for function annotation issues. |
187 Private method to check for function annotation issues. |
176 """ |
188 """ |
177 suppressNoneReturning = self.__args.get( |
189 suppressNoneReturning = self.__args.get( |
178 "SuppressNoneReturning", |
190 "SuppressNoneReturning", |
179 AnnotationsCheckerDefaultArgs["SuppressNoneReturning"]) |
191 AnnotationsCheckerDefaultArgs["SuppressNoneReturning"], |
|
192 ) |
180 suppressDummyArgs = self.__args.get( |
193 suppressDummyArgs = self.__args.get( |
181 "SuppressDummyArgs", |
194 "SuppressDummyArgs", AnnotationsCheckerDefaultArgs["SuppressDummyArgs"] |
182 AnnotationsCheckerDefaultArgs["SuppressDummyArgs"]) |
195 ) |
183 allowUntypedDefs = self.__args.get( |
196 allowUntypedDefs = self.__args.get( |
184 "AllowUntypedDefs", |
197 "AllowUntypedDefs", AnnotationsCheckerDefaultArgs["AllowUntypedDefs"] |
185 AnnotationsCheckerDefaultArgs["AllowUntypedDefs"]) |
198 ) |
186 allowUntypedNested = self.__args.get( |
199 allowUntypedNested = self.__args.get( |
187 "AllowUntypedNested", |
200 "AllowUntypedNested", AnnotationsCheckerDefaultArgs["AllowUntypedNested"] |
188 AnnotationsCheckerDefaultArgs["AllowUntypedNested"]) |
201 ) |
189 mypyInitReturn = self.__args.get( |
202 mypyInitReturn = self.__args.get( |
190 "MypyInitReturn", |
203 "MypyInitReturn", AnnotationsCheckerDefaultArgs["MypyInitReturn"] |
191 AnnotationsCheckerDefaultArgs["MypyInitReturn"]) |
204 ) |
192 |
205 |
193 # Store decorator lists as sets for easier lookup |
206 # Store decorator lists as sets for easier lookup |
194 dispatchDecorators = set(self.__args.get( |
207 dispatchDecorators = set( |
195 "DispatchDecorators", |
208 self.__args.get( |
196 AnnotationsCheckerDefaultArgs["DispatchDecorators"])) |
209 "DispatchDecorators", |
197 overloadDecorators = set(self.__args.get( |
210 AnnotationsCheckerDefaultArgs["DispatchDecorators"], |
198 "OverloadDecorators", |
211 ) |
199 AnnotationsCheckerDefaultArgs["OverloadDecorators"])) |
212 ) |
200 |
213 overloadDecorators = set( |
|
214 self.__args.get( |
|
215 "OverloadDecorators", |
|
216 AnnotationsCheckerDefaultArgs["OverloadDecorators"], |
|
217 ) |
|
218 ) |
|
219 |
201 from .AnnotationsFunctionVisitor import FunctionVisitor |
220 from .AnnotationsFunctionVisitor import FunctionVisitor |
|
221 |
202 visitor = FunctionVisitor(self.__source) |
222 visitor = FunctionVisitor(self.__source) |
203 visitor.visit(self.__tree) |
223 visitor.visit(self.__tree) |
204 |
224 |
205 # Keep track of the last encountered function decorated by |
225 # Keep track of the last encountered function decorated by |
206 # `typing.overload`, if any. Per the `typing` module documentation, |
226 # `typing.overload`, if any. Per the `typing` module documentation, |
207 # a series of overload-decorated definitions must be followed by |
227 # a series of overload-decorated definitions must be followed by |
208 # exactly one non-overload-decorated definition of the same function. |
228 # exactly one non-overload-decorated definition of the same function. |
209 lastOverloadDecoratedFunctionName = None |
229 lastOverloadDecoratedFunctionName = None |
210 |
230 |
211 # Iterate over the arguments with missing type hints, by function. |
231 # Iterate over the arguments with missing type hints, by function. |
212 for function in visitor.functionDefinitions: |
232 for function in visitor.functionDefinitions: |
213 if ( |
233 if function.isDynamicallyTyped() and ( |
214 function.isDynamicallyTyped() and |
234 allowUntypedDefs or (function.isNested and allowUntypedNested) |
215 (allowUntypedDefs or |
|
216 (function.isNested and allowUntypedNested)) |
|
217 ): |
235 ): |
218 # Skip recording errors from dynamically typed functions |
236 # Skip recording errors from dynamically typed functions |
219 # or nested functions |
237 # or nested functions |
220 continue |
238 continue |
221 |
239 |
222 # Skip recording errors for configured dispatch functions, such as |
240 # Skip recording errors for configured dispatch functions, such as |
223 # (by default) `functools.singledispatch` and |
241 # (by default) `functools.singledispatch` and |
224 # `functools.singledispatchmethod` |
242 # `functools.singledispatchmethod` |
225 if function.hasDecorator(dispatchDecorators): |
243 if function.hasDecorator(dispatchDecorators): |
226 continue |
244 continue |
227 |
245 |
228 # Create sentinels to check for mixed hint styles |
246 # Create sentinels to check for mixed hint styles |
229 hasTypeComment = function.hasTypeComment |
247 hasTypeComment = function.hasTypeComment |
230 |
248 |
231 has3107Annotation = False |
249 has3107Annotation = False |
232 # PEP 3107 annotations are captured by the return arg |
250 # PEP 3107 annotations are captured by the return arg |
233 |
251 |
234 # Iterate over annotated args to detect mixing of type annotations |
252 # Iterate over annotated args to detect mixing of type annotations |
235 # and type comments. Emit this only once per function definition |
253 # and type comments. Emit this only once per function definition |
236 for arg in function.getAnnotatedArguments(): |
254 for arg in function.getAnnotatedArguments(): |
237 if arg.hasTypeComment: |
255 if arg.hasTypeComment: |
238 hasTypeComment = True |
256 hasTypeComment = True |
239 |
257 |
240 if arg.has3107Annotation: |
258 if arg.has3107Annotation: |
241 has3107Annotation = True |
259 has3107Annotation = True |
242 |
260 |
243 if hasTypeComment and has3107Annotation: |
261 if hasTypeComment and has3107Annotation: |
244 # Short-circuit check for mixing of type comments & |
262 # Short-circuit check for mixing of type comments & |
245 # 3107-style annotations |
263 # 3107-style annotations |
246 self.__error(function.lineno - 1, function.col_offset, |
264 self.__error(function.lineno - 1, function.col_offset, "A301") |
247 "A301") |
|
248 break |
265 break |
249 |
266 |
250 # Before we iterate over the function's missing annotations, check |
267 # Before we iterate over the function's missing annotations, check |
251 # to see if it's the closing function def in a series of |
268 # to see if it's the closing function def in a series of |
252 # `typing.overload` decorated functions. |
269 # `typing.overload` decorated functions. |
253 if lastOverloadDecoratedFunctionName == function.name: |
270 if lastOverloadDecoratedFunctionName == function.name: |
254 continue |
271 continue |
255 |
272 |
256 # If it's not, and it is overload decorated, store it for the next |
273 # If it's not, and it is overload decorated, store it for the next |
257 # iteration |
274 # iteration |
258 if function.hasDecorator(overloadDecorators): |
275 if function.hasDecorator(overloadDecorators): |
259 lastOverloadDecoratedFunctionName = function.name |
276 lastOverloadDecoratedFunctionName = function.name |
260 |
277 |
261 # Record explicit errors for arguments that are missing annotations |
278 # Record explicit errors for arguments that are missing annotations |
262 for arg in function.getMissedAnnotations(): |
279 for arg in function.getMissedAnnotations(): |
263 if arg.argname == "return": |
280 if arg.argname == "return": |
264 # return annotations have multiple possible short-circuit |
281 # return annotations have multiple possible short-circuit |
265 # paths |
282 # paths |
266 if ( |
283 if ( |
267 suppressNoneReturning and |
284 suppressNoneReturning |
268 not arg.hasTypeAnnotation and |
285 and not arg.hasTypeAnnotation |
269 function.hasOnlyNoneReturns |
286 and function.hasOnlyNoneReturns |
270 ): |
287 ): |
271 # Skip recording return errors if the function has only |
288 # Skip recording return errors if the function has only |
272 # `None` returns. This includes the case of no returns. |
289 # `None` returns. This includes the case of no returns. |
273 continue |
290 continue |
274 |
291 |
275 if ( |
292 if ( |
276 mypyInitReturn and |
293 mypyInitReturn |
277 function.isClassMethod and |
294 and function.isClassMethod |
278 function.name == "__init__" and |
295 and function.name == "__init__" |
279 function.getAnnotatedArguments() |
296 and function.getAnnotatedArguments() |
280 ): |
297 ): |
281 # Skip recording return errors for `__init__` if at |
298 # Skip recording return errors for `__init__` if at |
282 # least one argument is annotated |
299 # least one argument is annotated |
283 continue |
300 continue |
284 |
301 |
285 # If the `suppressDummyArgs` flag is `True`, skip recording |
302 # If the `suppressDummyArgs` flag is `True`, skip recording |
286 # errors for any arguments named `_` |
303 # errors for any arguments named `_` |
287 if arg.argname == "_" and suppressDummyArgs: |
304 if arg.argname == "_" and suppressDummyArgs: |
288 continue |
305 continue |
289 |
306 |
290 self.__classifyError(function, arg) |
307 self.__classifyError(function, arg) |
291 |
308 |
292 def __classifyError(self, function, arg): |
309 def __classifyError(self, function, arg): |
293 """ |
310 """ |
294 Private method to classify the missing type annotation based on the |
311 Private method to classify the missing type annotation based on the |
295 Function & Argument metadata. |
312 Function & Argument metadata. |
296 |
313 |
297 For the currently defined rules & program flow, the assumption can be |
314 For the currently defined rules & program flow, the assumption can be |
298 made that an argument passed to this method will match a linting error, |
315 made that an argument passed to this method will match a linting error, |
299 and will only match a single linting error |
316 and will only match a single linting error |
300 |
317 |
301 This function provides an initial classificaton, then passes relevant |
318 This function provides an initial classificaton, then passes relevant |
302 attributes to cached helper function(s). |
319 attributes to cached helper function(s). |
303 |
320 |
304 @param function reference to the Function object |
321 @param function reference to the Function object |
305 @type Function |
322 @type Function |
306 @param arg reference to the Argument object |
323 @param arg reference to the Argument object |
307 @type Argument |
324 @type Argument |
308 """ |
325 """ |
309 # Check for return type |
326 # Check for return type |
310 # All return "arguments" have an explicitly defined name "return" |
327 # All return "arguments" have an explicitly defined name "return" |
311 if arg.argname == "return": |
328 if arg.argname == "return": |
312 errorCode = self.__returnErrorClassifier( |
329 errorCode = self.__returnErrorClassifier( |
313 function.isClassMethod, function.classDecoratorType, |
330 function.isClassMethod, |
314 function.functionType |
331 function.classDecoratorType, |
|
332 function.functionType, |
315 ) |
333 ) |
316 else: |
334 else: |
317 # Otherwise, classify function argument error |
335 # Otherwise, classify function argument error |
318 isFirstArg = arg == function.args[0] |
336 isFirstArg = arg == function.args[0] |
319 errorCode = self.__argumentErrorClassifier( |
337 errorCode = self.__argumentErrorClassifier( |
320 function.isClassMethod, isFirstArg, |
338 function.isClassMethod, |
321 function.classDecoratorType, arg.annotationType, |
339 isFirstArg, |
322 ) |
340 function.classDecoratorType, |
323 |
341 arg.annotationType, |
|
342 ) |
|
343 |
324 if errorCode in ("A001", "A002", "A003"): |
344 if errorCode in ("A001", "A002", "A003"): |
325 self.__error(arg.lineno - 1, arg.col_offset, errorCode, |
345 self.__error(arg.lineno - 1, arg.col_offset, errorCode, arg.argname) |
326 arg.argname) |
|
327 else: |
346 else: |
328 self.__error(arg.lineno - 1, arg.col_offset, errorCode) |
347 self.__error(arg.lineno - 1, arg.col_offset, errorCode) |
329 |
348 |
330 @lru_cache() |
349 @lru_cache() |
331 def __returnErrorClassifier(self, isClassMethod, classDecoratorType, |
350 def __returnErrorClassifier(self, isClassMethod, classDecoratorType, functionType): |
332 functionType): |
|
333 """ |
351 """ |
334 Private method to classify a return type annotation issue. |
352 Private method to classify a return type annotation issue. |
335 |
353 |
336 @param isClassMethod flag indicating a classmethod type function |
354 @param isClassMethod flag indicating a classmethod type function |
337 @type bool |
355 @type bool |
338 @param classDecoratorType type of class decorator |
356 @param classDecoratorType type of class decorator |
339 @type ClassDecoratorType |
357 @type ClassDecoratorType |
340 @param functionType type of function |
358 @param functionType type of function |
393 elif annotationType == AnnotationType.VARARG: |
412 elif annotationType == AnnotationType.VARARG: |
394 return "A002" |
413 return "A002" |
395 else: |
414 else: |
396 # Combine PosOnlyArgs, Args, and KwOnlyArgs |
415 # Combine PosOnlyArgs, Args, and KwOnlyArgs |
397 return "A001" |
416 return "A001" |
398 |
417 |
399 ####################################################################### |
418 ####################################################################### |
400 ## Annotations Coverage |
419 ## Annotations Coverage |
401 ## |
420 ## |
402 ## adapted from: flake8-annotations-coverage v0.0.5 |
421 ## adapted from: flake8-annotations-coverage v0.0.5 |
403 ####################################################################### |
422 ####################################################################### |
404 |
423 |
405 def __checkAnnotationsCoverage(self): |
424 def __checkAnnotationsCoverage(self): |
406 """ |
425 """ |
407 Private method to check for function annotation coverage. |
426 Private method to check for function annotation coverage. |
408 """ |
427 """ |
409 minAnnotationsCoverage = self.__args.get( |
428 minAnnotationsCoverage = self.__args.get( |
410 "MinimumCoverage", |
429 "MinimumCoverage", AnnotationsCheckerDefaultArgs["MinimumCoverage"] |
411 AnnotationsCheckerDefaultArgs["MinimumCoverage"]) |
430 ) |
412 if minAnnotationsCoverage == 0: |
431 if minAnnotationsCoverage == 0: |
413 # 0 means it is switched off |
432 # 0 means it is switched off |
414 return |
433 return |
415 |
434 |
416 functionDefs = [ |
435 functionDefs = [ |
417 f for f in ast.walk(self.__tree) |
436 f |
|
437 for f in ast.walk(self.__tree) |
418 if isinstance(f, (ast.AsyncFunctionDef, ast.FunctionDef)) |
438 if isinstance(f, (ast.AsyncFunctionDef, ast.FunctionDef)) |
419 ] |
439 ] |
420 if not functionDefs: |
440 if not functionDefs: |
421 # no functions/methods at all |
441 # no functions/methods at all |
422 return |
442 return |
423 |
443 |
424 functionDefAnnotationsInfo = [ |
444 functionDefAnnotationsInfo = [ |
425 self.__hasTypeAnnotations(f) for f in functionDefs |
445 self.__hasTypeAnnotations(f) for f in functionDefs |
426 ] |
446 ] |
427 annotationsCoverage = int( |
447 annotationsCoverage = int( |
428 len(list(filter(None, functionDefAnnotationsInfo))) / |
448 len(list(filter(None, functionDefAnnotationsInfo))) |
429 len(functionDefAnnotationsInfo) * 100 |
449 / len(functionDefAnnotationsInfo) |
|
450 * 100 |
430 ) |
451 ) |
431 if annotationsCoverage < minAnnotationsCoverage: |
452 if annotationsCoverage < minAnnotationsCoverage: |
432 self.__error(0, 0, "A881", annotationsCoverage) |
453 self.__error(0, 0, "A881", annotationsCoverage) |
433 |
454 |
434 def __hasTypeAnnotations(self, funcNode): |
455 def __hasTypeAnnotations(self, funcNode): |
435 """ |
456 """ |
436 Private method to check for type annotations. |
457 Private method to check for type annotations. |
437 |
458 |
438 @param funcNode reference to the function definition node to be checked |
459 @param funcNode reference to the function definition node to be checked |
439 @type ast.AsyncFunctionDef or ast.FunctionDef |
460 @type ast.AsyncFunctionDef or ast.FunctionDef |
440 @return flag indicating the presence of type annotations |
461 @return flag indicating the presence of type annotations |
441 @rtype bool |
462 @rtype bool |
442 """ |
463 """ |
443 hasReturnAnnotation = funcNode.returns is not None |
464 hasReturnAnnotation = funcNode.returns is not None |
444 hasArgsAnnotations = any(a for a in funcNode.args.args |
465 hasArgsAnnotations = any( |
445 if a.annotation is not None) |
466 a for a in funcNode.args.args if a.annotation is not None |
446 hasKwargsAnnotations = (funcNode.args and |
467 ) |
447 funcNode.args.kwarg and |
468 hasKwargsAnnotations = ( |
448 funcNode.args.kwarg.annotation is not None) |
469 funcNode.args |
449 hasKwonlyargsAnnotations = any(a for a in funcNode.args.kwonlyargs |
470 and funcNode.args.kwarg |
450 if a.annotation is not None) |
471 and funcNode.args.kwarg.annotation is not None |
451 |
472 ) |
452 return any((hasReturnAnnotation, hasArgsAnnotations, |
473 hasKwonlyargsAnnotations = any( |
453 hasKwargsAnnotations, hasKwonlyargsAnnotations)) |
474 a for a in funcNode.args.kwonlyargs if a.annotation is not None |
454 |
475 ) |
|
476 |
|
477 return any( |
|
478 ( |
|
479 hasReturnAnnotation, |
|
480 hasArgsAnnotations, |
|
481 hasKwargsAnnotations, |
|
482 hasKwonlyargsAnnotations, |
|
483 ) |
|
484 ) |
|
485 |
455 ####################################################################### |
486 ####################################################################### |
456 ## Annotations Complexity |
487 ## Annotations Complexity |
457 ## |
488 ## |
458 ## adapted from: flake8-annotations-complexity v0.0.6 |
489 ## adapted from: flake8-annotations-complexity v0.0.6 |
459 ####################################################################### |
490 ####################################################################### |
460 |
491 |
461 def __checkAnnotationComplexity(self): |
492 def __checkAnnotationComplexity(self): |
462 """ |
493 """ |
463 Private method to check the type annotation complexity. |
494 Private method to check the type annotation complexity. |
464 """ |
495 """ |
465 maxAnnotationComplexity = self.__args.get( |
496 maxAnnotationComplexity = self.__args.get( |
466 "MaximumComplexity", |
497 "MaximumComplexity", AnnotationsCheckerDefaultArgs["MaximumComplexity"] |
467 AnnotationsCheckerDefaultArgs["MaximumComplexity"]) |
498 ) |
468 maxAnnotationLength = self.__args.get( |
499 maxAnnotationLength = self.__args.get( |
469 "MaximumLength", AnnotationsCheckerDefaultArgs["MaximumLength"]) |
500 "MaximumLength", AnnotationsCheckerDefaultArgs["MaximumLength"] |
|
501 ) |
470 typeAnnotations = [] |
502 typeAnnotations = [] |
471 |
503 |
472 functionDefs = [ |
504 functionDefs = [ |
473 f for f in ast.walk(self.__tree) |
505 f |
|
506 for f in ast.walk(self.__tree) |
474 if isinstance(f, (ast.AsyncFunctionDef, ast.FunctionDef)) |
507 if isinstance(f, (ast.AsyncFunctionDef, ast.FunctionDef)) |
475 ] |
508 ] |
476 for functionDef in functionDefs: |
509 for functionDef in functionDefs: |
477 typeAnnotations += list(filter( |
510 typeAnnotations += list( |
478 None, [a.annotation for a in functionDef.args.args])) |
511 filter(None, [a.annotation for a in functionDef.args.args]) |
|
512 ) |
479 if functionDef.returns: |
513 if functionDef.returns: |
480 typeAnnotations.append(functionDef.returns) |
514 typeAnnotations.append(functionDef.returns) |
481 typeAnnotations += [a.annotation for a in ast.walk(self.__tree) |
515 typeAnnotations += [ |
482 if isinstance(a, ast.AnnAssign) and a.annotation] |
516 a.annotation |
|
517 for a in ast.walk(self.__tree) |
|
518 if isinstance(a, ast.AnnAssign) and a.annotation |
|
519 ] |
483 for annotation in typeAnnotations: |
520 for annotation in typeAnnotations: |
484 complexity = self.__getAnnotationComplexity(annotation) |
521 complexity = self.__getAnnotationComplexity(annotation) |
485 if complexity > maxAnnotationComplexity: |
522 if complexity > maxAnnotationComplexity: |
486 self.__error(annotation.lineno - 1, annotation.col_offset, |
523 self.__error( |
487 "A891", complexity, maxAnnotationComplexity) |
524 annotation.lineno - 1, |
488 |
525 annotation.col_offset, |
|
526 "A891", |
|
527 complexity, |
|
528 maxAnnotationComplexity, |
|
529 ) |
|
530 |
489 annotationLength = self.__getAnnotationLength(annotation) |
531 annotationLength = self.__getAnnotationLength(annotation) |
490 if annotationLength > maxAnnotationLength: |
532 if annotationLength > maxAnnotationLength: |
491 self.__error(annotation.lineno - 1, annotation.col_offset, |
533 self.__error( |
492 "A892", annotationLength, maxAnnotationLength) |
534 annotation.lineno - 1, |
493 |
535 annotation.col_offset, |
|
536 "A892", |
|
537 annotationLength, |
|
538 maxAnnotationLength, |
|
539 ) |
|
540 |
494 def __getAnnotationComplexity(self, annotationNode, defaultComplexity=1): |
541 def __getAnnotationComplexity(self, annotationNode, defaultComplexity=1): |
495 """ |
542 """ |
496 Private method to determine the annotation complexity. |
543 Private method to determine the annotation complexity. |
497 |
544 |
498 @param annotationNode reference to the node to determine the annotation |
545 @param annotationNode reference to the node to determine the annotation |
499 complexity for |
546 complexity for |
500 @type ast.AST |
547 @type ast.AST |
501 @param defaultComplexity default complexity value |
548 @param defaultComplexity default complexity value |
502 @type int |
549 @type int |