@@ -3347,14 +3347,30 @@ def visit_with_stmt(self, s: WithStmt) -> None:
3347
3347
exit_ret_type = self .check_async_with_item (expr , target , s .unanalyzed_type is None )
3348
3348
else :
3349
3349
exit_ret_type = self .check_with_item (expr , target , s .unanalyzed_type is None )
3350
+
3351
+ # Based on the return type, determine if this context manager 'swallows'
3352
+ # exceptions or not. We determine this using a heuristic based on the
3353
+ # return type of the __exit__ method -- see the discussion in
3354
+ # https://github.com/python/mypy/issues/7214 and the section about context managers
3355
+ # in https://github.com/python/typeshed/blob/master/CONTRIBUTING.md#conventions
3356
+ # for more details.
3357
+
3358
+ exit_ret_type = get_proper_type (exit_ret_type )
3350
3359
if is_literal_type (exit_ret_type , "builtins.bool" , False ):
3351
3360
continue
3352
- if is_literal_type (exit_ret_type , "builtins.bool" , True ):
3353
- exceptions_maybe_suppressed = True
3354
- elif (isinstance (exit_ret_type , Instance )
3355
- and exit_ret_type .type .fullname () == 'builtins.bool' ):
3361
+
3362
+ if (is_literal_type (exit_ret_type , "builtins.bool" , True )
3363
+ or (isinstance (exit_ret_type , Instance )
3364
+ and exit_ret_type .type .fullname () == 'builtins.bool'
3365
+ and state .strict_optional )):
3366
+ # Note: if strict-optional is disabled, this bool instance
3367
+ # could actually be an Optional[bool].
3356
3368
exceptions_maybe_suppressed = True
3369
+
3357
3370
if exceptions_maybe_suppressed :
3371
+ # Treat this 'with' block in the same way we'd treat a 'try: BODY; except: pass'
3372
+ # block. This means control flow can continue after the 'with' even if the 'with'
3373
+ # block immediately returns.
3358
3374
with self .binder .frame_context (can_skip = True , try_frame = True ):
3359
3375
self .accept (s .body )
3360
3376
else :
0 commit comments