@@ -3347,14 +3347,30 @@ def visit_with_stmt(self, s: WithStmt) -> None:
33473347 exit_ret_type = self .check_async_with_item (expr , target , s .unanalyzed_type is None )
33483348 else :
33493349 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 )
33503359 if is_literal_type (exit_ret_type , "builtins.bool" , False ):
33513360 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].
33563368 exceptions_maybe_suppressed = True
3369+
33573370 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.
33583374 with self .binder .frame_context (can_skip = True , try_frame = True ):
33593375 self .accept (s .body )
33603376 else :
0 commit comments