@@ -43,7 +43,7 @@ def __init__(self, node: ast.Call, funcname: str, packagename: str):
43
43
self .packagename = packagename
44
44
self .variable_name : Optional [str ] = None
45
45
self .shielded : bool = False
46
- self .timeout : bool = False
46
+ self .has_timeout : bool = False
47
47
48
48
if self .funcname == "CancelScope" :
49
49
for kw in node .keywords :
@@ -52,9 +52,9 @@ def __init__(self, node: ast.Call, funcname: str, packagename: str):
52
52
self .shielded = kw .value .value
53
53
# sets to True even if timeout is explicitly set to inf
54
54
if kw .arg == "deadline" :
55
- self .timeout = True
55
+ self .has_timeout = True
56
56
else :
57
- self .timeout = True
57
+ self .has_timeout = True
58
58
59
59
def __str__ (self ):
60
60
# Not supporting other ways of importing trio
@@ -89,7 +89,7 @@ class Visitor102(ast.NodeVisitor):
89
89
def __init__ (self ) -> None :
90
90
super ().__init__ ()
91
91
self .problems : List [Error ] = []
92
- self ._inside_finally : Optional [ ast . Try ] = None
92
+ self ._inside_finally : bool = False
93
93
self ._scopes : List [TrioScope ] = []
94
94
self ._context_manager = False
95
95
@@ -169,7 +169,7 @@ def visit_Try(self, node: ast.Try) -> None:
169
169
outer_scopes = self ._scopes
170
170
171
171
self ._scopes = []
172
- self ._inside_finally = node
172
+ self ._inside_finally = True
173
173
174
174
for item in node .finalbody :
175
175
self .visit (item )
@@ -180,12 +180,12 @@ def visit_Try(self, node: ast.Try) -> None:
180
180
def check_for_trio102 (self , node : Union [ast .Await , ast .AsyncFor , ast .AsyncWith ]):
181
181
# if we're inside a finally, and not inside a context_manager, and we're not
182
182
# inside a scope that doesn't have both a timeout and shield
183
- if self . _inside_finally is not None and not self . _context_manager :
184
- for scope in self ._scopes :
185
- if scope . timeout and scope . shielded :
186
- break
187
- else :
188
- self .problems .append (make_error (TRIO102 , node .lineno , node .col_offset ))
183
+ if (
184
+ self ._inside_finally
185
+ and not self . _context_manager
186
+ and not any ( scope . has_timeout and scope . shielded for scope in self . _scopes )
187
+ ) :
188
+ self .problems .append (make_error (TRIO102 , node .lineno , node .col_offset ))
189
189
190
190
191
191
class Visitor (ast .NodeVisitor ):
@@ -277,4 +277,4 @@ def run(self) -> Generator[Tuple[int, int, str, Type[Any]], None, None]:
277
277
278
278
TRIO100 = "TRIO100: {} context contains no checkpoints, add `await trio.sleep(0)`"
279
279
TRIO101 = "TRIO101: yield inside a nursery or cancel scope is only safe when implementing a context manager - otherwise, it breaks exception handling"
280
- TRIO102 = "TRIO102: await in finally without a cancel scope and shielding "
280
+ TRIO102 = "TRIO102: it's unsafe to await inside ` finally:` unless you use a shielded cancel scope with a timeout "
0 commit comments