54 annotation (defaults to False) |
55 annotation (defaults to False) |
55 @type bool (optional) |
56 @type bool (optional) |
56 @param hasTypeComment flag indicating the presence of a type comment |
57 @param hasTypeComment flag indicating the presence of a type comment |
57 (defaults to False) |
58 (defaults to False) |
58 @type bool (optional) |
59 @type bool (optional) |
|
60 @param isDynamicallyTyped flag indicating dynamic typing (defaults to False) |
|
61 @type bool (optional) |
59 """ |
62 """ |
60 self.argname = argname |
63 self.argname = argname |
61 self.lineno = lineno |
64 self.lineno = lineno |
62 self.col_offset = col_offset |
65 self.col_offset = col_offset |
63 self.annotationType = annotationType |
66 self.annotationType = annotationType |
64 self.hasTypeAnnotation = hasTypeAnnotation |
67 self.hasTypeAnnotation = hasTypeAnnotation |
65 self.has3107Annotation = has3107Annotation |
68 self.has3107Annotation = has3107Annotation |
66 self.hasTypeComment = hasTypeComment |
69 self.hasTypeComment = hasTypeComment |
|
70 self.isDynamicallyTyped = isDynamicallyTyped |
67 |
71 |
68 @classmethod |
72 @classmethod |
69 def fromNode(cls, node, annotationTypeName): |
73 def fromNode(cls, node, annotationTypeName): |
70 """ |
74 """ |
71 Class method to create an Argument object based on the given node. |
75 Class method to create an Argument object based on the given node. |
87 |
91 |
88 if node.type_comment: |
92 if node.type_comment: |
89 newArg.hasTypeAnnotation = True |
93 newArg.hasTypeAnnotation = True |
90 newArg.hasTypeComment = True |
94 newArg.hasTypeComment = True |
91 |
95 |
|
96 if cls._isAnnotatedAny(node.type_comment): |
|
97 newArg.isDynamicallyTyped = True |
|
98 |
92 return newArg |
99 return newArg |
|
100 |
|
101 @staticmethod |
|
102 def _isAnnotatedAny(argExpr): |
|
103 """ |
|
104 Static method to check if the provided expression node is annotated with |
|
105 'typing.Any'. |
|
106 |
|
107 Support is provided for the following patterns: |
|
108 * 'from typing import Any; foo: Any' |
|
109 * 'import typing; foo: typing.Any' |
|
110 * 'import typing as <alias>; foo: <alias>.Any' |
|
111 |
|
112 Type comments are also supported. Inline type comments are assumed to be |
|
113 passed here as 'str', and function-level type comments are assumed to be |
|
114 passed as 'ast.expr'. |
|
115 |
|
116 @param argExpr DESCRIPTION |
|
117 @type ast.expr or str |
|
118 @return flag indicating an annotation with 'typing.Any' |
|
119 @rtype bool |
|
120 """ |
|
121 if isinstance(argExpr, ast.Name): |
|
122 if argExpr.id == "Any": |
|
123 return True |
|
124 elif isinstance(argExpr, ast.Attribute): |
|
125 if argExpr.attr == "Any": |
|
126 return True |
|
127 elif isinstance(argExpr, str): # __IGNORE_WARNING_Y102__ |
|
128 if argExpr.split(".", maxsplit=1)[-1] == "Any": |
|
129 return True |
|
130 |
|
131 return False |
93 |
132 |
94 |
133 |
95 class Function: |
134 class Function: |
96 """ |
135 """ |
97 Class representing a function. |
136 Class representing a function. |
316 ) |
355 ) |
317 if node.returns: |
356 if node.returns: |
318 returnArg.hasTypeAnnotation = True |
357 returnArg.hasTypeAnnotation = True |
319 returnArg.has3107Annotation = True |
358 returnArg.has3107Annotation = True |
320 newFunction.isReturnAnnotated = True |
359 newFunction.isReturnAnnotated = True |
|
360 |
|
361 if Argument._isAnnotatedAny(node.returns): |
|
362 returnArg.isDynamicallyTyped = True |
321 |
363 |
322 newFunction.args.append(returnArg) |
364 newFunction.args.append(returnArg) |
323 |
365 |
324 # Type comments in-line with input arguments are handled by the |
366 # Type comments in-line with input arguments are handled by the |
325 # Argument class. If a function-level type comment is present, attempt |
367 # Argument class. If a function-level type comment is present, attempt |
424 |
466 |
425 if arg and hintComment: |
467 if arg and hintComment: |
426 arg.hasTypeAnnotation = True |
468 arg.hasTypeAnnotation = True |
427 arg.hasTypeComment = True |
469 arg.hasTypeComment = True |
428 |
470 |
|
471 if Argument._isAnnotatedAny(hintComment): |
|
472 arg.isDynamicallyTyped = True |
|
473 |
429 # Return arg is always last |
474 # Return arg is always last |
430 funcObj.args[-1].hasTypeAnnotation = True |
475 funcObj.args[-1].hasTypeAnnotation = True |
431 funcObj.args[-1].hasTypeComment = True |
476 funcObj.args[-1].hasTypeComment = True |
432 funcObj.isReturnAnnotated = True |
477 funcObj.isReturnAnnotated = True |
|
478 if Argument._isAnnotatedAny(hintTree.returns): |
|
479 arg.isDynamicallyTyped = True |
433 |
480 |
434 return funcObj |
481 return funcObj |
435 |
482 |
436 @staticmethod |
483 @staticmethod |
437 def _maybeInjectClassArgument(hintTree, funcObj): |
484 def _maybeInjectClassArgument(hintTree, funcObj): |
474 """ |
521 """ |
475 if not funcObj.isClassMethod: |
522 if not funcObj.isClassMethod: |
476 # Short circuit |
523 # Short circuit |
477 return hintTree |
524 return hintTree |
478 |
525 |
479 if funcObj.classDecoratorType != ClassDecoratorType.STATICMETHOD and len( |
526 if funcObj.classDecoratorType != ClassDecoratorType.STATICMETHOD and ( |
480 hintTree.argtypes |
527 len(hintTree.argtypes) < (len(funcObj.args) - 1) |
481 ) < (len(funcObj.args) - 1): |
528 ): |
482 # Subtract 1 to skip return arg |
529 # Subtract 1 to skip return arg |
483 hintTree.argtypes = [ast.Ellipsis()] + hintTree.argtypes |
530 hintTree.argtypes = [ast.Ellipsis()] + hintTree.argtypes |
484 |
531 |
485 return hintTree |
532 return hintTree |
486 |
533 |