@@ -3312,14 +3312,30 @@ def visit_with_stmt(self, s: WithStmt) -> None:
33123312 exit_ret_type = self .check_async_with_item (expr , target , s .unanalyzed_type is None )
33133313 else :
33143314 exit_ret_type = self .check_with_item (expr , target , s .unanalyzed_type is None )
3315+
3316+ # Based on the return type, determine if this context manager 'swallows'
3317+ # exceptions or not. We determine this using a heuristic based on the
3318+ # return type of the __exit__ method -- see the discussion in
3319+ # https://github.com/python/mypy/issues/7214 and the section about context managers
3320+ # in https://github.com/python/typeshed/blob/master/CONTRIBUTING.md#conventions
3321+ # for more details.
3322+
3323+ exit_ret_type = get_proper_type (exit_ret_type )
33153324 if is_literal_type (exit_ret_type , "builtins.bool" , False ):
33163325 continue
3317- if is_literal_type (exit_ret_type , "builtins.bool" , True ):
3318- exceptions_maybe_suppressed = True
3319- elif (isinstance (exit_ret_type , Instance )
3320- and exit_ret_type .type .fullname () == 'builtins.bool' ):
3326+
3327+ if (is_literal_type (exit_ret_type , "builtins.bool" , True )
3328+ or (isinstance (exit_ret_type , Instance )
3329+ and exit_ret_type .type .fullname () == 'builtins.bool'
3330+ and state .strict_optional )):
3331+ # Note: if strict-optional is disabled, this bool instance
3332+ # could actually be an Optional[bool].
33213333 exceptions_maybe_suppressed = True
3334+
33223335 if exceptions_maybe_suppressed :
3336+ # Treat this 'with' block in the same way we'd treat a 'try: BODY; except: pass'
3337+ # block. This means control flow can continue after the 'with' even if the 'with'
3338+ # block immediately returns.
33233339 with self .binder .frame_context (can_skip = True , try_frame = True ):
33243340 self .accept (s .body )
33253341 else :
0 commit comments