basedmypy
basedmypy copied to clipboard
sound reach-ability on context managers
The current solution is heuristic based and highly unsound.
This will require reworking AbstractContextManager to know about the types of Exception and return type of __exit__
class Base:
def __enter__(self): ...
class A(Base):
def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> True: ...
class B(Base):
def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> False | None:
with A():
raise Exception
print("hi") # should not see a reach-ability error
with B():
pass
print("hi") # should not see a reach-ability error
with B():
raise Exception
print("hi") # should see a reach-ability error
But return should always report unreachable.
- upstream at https://github.com/python/mypy/issues/8766
Will have to update the type of:
AbstractContextManager to have three params:
class AbstractContextManager[TEnter, TException: BaseException, TReturn: bool | None](ABC):
def __enter__(self) -> TEnter: ...
def __exit__(self, exc: TException) -> TReturn: ...
contextlib.contextmanager to capture the return type of the generator:
@overload
def contextmanager(func: Callable[_P, Generator[_T_co, object, True]]) -> Callable[_P, _GeneratorContextManager[_T_co, BaseException,True]]: ...
@overload
def contextmanager(func: Callable[_P, Iterator[_T_co]]) -> Callable[_P, _GeneratorContextManager[_T_co, object, bool | None]]: ...
And suppress:
class suppress[in_T_BaseException](AbstractContextManager[None, in_T_BaseException, True | None]):
def __init__(self, *exceptions: type[in_T_BaseExcaption]) -> None: ...
@overload
def __exit__(
self, exctype: type[in_T_BaseException], excinst: in_T_BaseException, exctb: TracebackType
) -> True: ...
@overload
def __exit__(
self, exctype: type[BaseException] | None, excinst: BaseException | None, exctb: TracebackType | None
) -> None: ...
related #522