2
2
3
3
from mypy .nodes import (
4
4
ARG_POS , MDEF , Argument , Block , CallExpr , ClassDef , Expression , SYMBOL_FUNCBASE_TYPES ,
5
- FuncDef , PassStmt , RefExpr , SymbolTableNode , Var , JsonDict ,
5
+ FuncDef , PassStmt , RefExpr , SymbolTableNode , Var , JsonDict , NameExpr ,
6
6
)
7
7
from mypy .plugin import CheckerPluginInterface , ClassDefContext , SemanticAnalyzerPluginInterface
8
8
from mypy .semanal import set_callable_name , ALLOW_INCOMPATIBLE_OVERRIDE
@@ -24,25 +24,36 @@ def _get_decorator_bool_argument(
24
24
25
25
This handles both @decorator(...) and @decorator.
26
26
"""
27
- if isinstance (ctx .reason , CallExpr ):
27
+ if isinstance (ctx .reason , CallExpr ): # @decorator(...)
28
28
return _get_bool_argument (ctx , ctx .reason , name , default )
29
- else :
30
- return default
29
+ # @decorator - no call. Try to get default value from decorator definition.
30
+ if isinstance (ctx .reason , NameExpr ) and isinstance (ctx .reason .node , FuncDef ):
31
+ default_value = _get_default_bool_value (ctx .reason .node , name , default )
32
+ if default_value is not None :
33
+ return default_value
34
+ # If we are here, no value was passed in call, default was found in def and it is None.
35
+ # Should we ctx.api.fail here?
36
+ return default
31
37
32
38
33
39
def _get_bool_argument (ctx : ClassDefContext , expr : CallExpr ,
34
40
name : str , default : bool ) -> bool :
35
- """Return the boolean value for an argument to a call or the
36
- default if it's not found.
41
+ """Return the boolean value for an argument to a call.
42
+
43
+ If the argument was not passed, try to find out the default value of the argument and return
44
+ that. If a default value cannot be automatically determined, return the value of the `default`
45
+ argument of this function.
37
46
"""
38
47
attr_value = _get_argument (expr , name )
39
48
if attr_value :
40
49
ret = ctx .api .parse_bool (attr_value )
41
- if ret is None :
42
- ctx .api .fail (f'"{ name } " argument must be True or False.' , expr )
43
- return default
44
- return ret
45
- return default
50
+ else :
51
+ # This argument was not passed in the call. Try to extract default from function def.
52
+ ret = _get_default_bool_value (expr , name , default )
53
+ if ret is None :
54
+ ctx .api .fail (f'"{ name } " argument must be True or False.' , expr )
55
+ return default
56
+ return ret
46
57
47
58
48
59
def _get_argument (call : CallExpr , name : str ) -> Optional [Expression ]:
@@ -82,6 +93,36 @@ def _get_argument(call: CallExpr, name: str) -> Optional[Expression]:
82
93
return None
83
94
84
95
96
+ def _get_default_bool_value (
97
+ expr : Union [CallExpr , FuncDef , Expression ], name : str , default : Optional [bool ] = None
98
+ ) -> Optional [bool ]:
99
+ """Return the default value for the argument with this name from an expression.
100
+
101
+ Try to extract the default optional bool value from the definition. If cannot extract
102
+ default value from the code, return the analyzer-defined default instead.
103
+ """
104
+ if isinstance (expr , CallExpr ): # We have a @decorator(...) situation.
105
+ if isinstance (expr .callee , RefExpr ):
106
+ callee_node = expr .callee .node
107
+ if isinstance (callee_node , FuncDef ):
108
+ expr = callee_node # Will enter next if clause.
109
+ if isinstance (expr , FuncDef ):
110
+ try :
111
+ initializer = expr .arguments [expr .arg_names .index (name )].initializer
112
+ except ValueError : # name not in func_def.arg_names
113
+ return default
114
+ if initializer is None or not isinstance (initializer , NameExpr ):
115
+ # No default was defined in the code or it is a complex expression.
116
+ return default # Return analyzer-defined default.
117
+ if initializer .fullname == 'builtins.True' :
118
+ return True
119
+ if initializer .fullname == 'builtins.False' :
120
+ return False
121
+ if initializer .fullname == 'builtins.None' :
122
+ return None
123
+ return default # Cannot extract default from code, return analyzer-defined default.
124
+
125
+
85
126
def add_method (
86
127
ctx : ClassDefContext ,
87
128
name : str ,
0 commit comments