crates/ty_python_semantic/resources/mdtest/with/sync.md
with statementThe type of the target variable in a with statement is the return type from the context manager's
__enter__ method.
class Target: ...
class Manager:
def __enter__(self) -> Target:
return Target()
def __exit__(self, exc_type, exc_value, traceback): ...
with Manager() as f:
reveal_type(f) # revealed: Target
def _(flag: bool):
class Manager1:
def __enter__(self) -> str:
return "foo"
def __exit__(self, exc_type, exc_value, traceback): ...
class Manager2:
def __enter__(self) -> int:
return 42
def __exit__(self, exc_type, exc_value, traceback): ...
context_expr = Manager1() if flag else Manager2()
with context_expr as f:
reveal_type(f) # revealed: str | int
[environment]
python-version = "3.12"
from typing import Self, TypeAlias
from typing_extensions import TypeAliasType
class A:
def __enter__(self) -> Self:
return self
def __exit__(self, exc_type, exc_value, traceback) -> None: ...
class B:
def __enter__(self) -> Self:
return self
def __exit__(self, exc_type, exc_value, traceback) -> None: ...
UnionAB1: TypeAlias = A | B
type UnionAB2 = A | B
UnionAB3 = TypeAliasType("UnionAB3", A | B)
def f1(x: UnionAB1) -> None:
with x as y:
reveal_type(y) # revealed: A | B
def f2(x: UnionAB2) -> None:
with x as y:
reveal_type(y) # revealed: A | B
def f3(x: UnionAB3) -> None:
with x as y:
reveal_type(y) # revealed: A | B
__enter__ or __exit__ methodclass Manager: ...
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because it does not implement `__enter__` and `__exit__`"
with Manager():
pass
__enter__ methodclass Manager:
def __exit__(self, exc_tpe, exc_value, traceback): ...
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because it does not implement `__enter__`"
with Manager():
pass
__exit__ methodclass Manager:
def __enter__(self): ...
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because it does not implement `__exit__`"
with Manager():
pass
__enter__ attributeclass Manager:
__enter__: int = 42
def __exit__(self, exc_tpe, exc_value, traceback): ...
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because it does not correctly implement `__enter__`"
with Manager():
pass
__exit__ attributefrom typing_extensions import Self
class Manager:
def __enter__(self) -> Self:
return self
__exit__: int = 32
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because it does not correctly implement `__exit__`"
with Manager():
pass
def _(flag: bool):
class Manager1:
def __enter__(self) -> str:
return "foo"
def __exit__(self, exc_type, exc_value, traceback): ...
class NotAContextManager: ...
context_expr = Manager1() if flag else NotAContextManager()
# error: [invalid-context-manager] "Object of type `Manager1 | NotAContextManager` cannot be used with `with` because the methods `__enter__` and `__exit__` are possibly missing"
with context_expr as f:
reveal_type(f) # revealed: str
def _(flag1: bool, flag2: bool):
class GoodManager:
def __enter__(self) -> str:
return "foo"
def __exit__(self, exc_type, exc_value, traceback): ...
class MissingExitManager:
def __enter__(self) -> str:
return "bar"
class NotAContextManager: ...
context_expr = GoodManager() if flag1 else MissingExitManager() if flag2 else NotAContextManager()
# error: [invalid-context-manager] "Object of type `GoodManager | MissingExitManager | NotAContextManager` cannot be used with `with` because the methods `__enter__` and `__exit__` are possibly missing"
with context_expr as f:
reveal_type(f) # revealed: str
If every union element implements the context manager protocol but at least one implements it
incorrectly (e.g. with a non-callable __exit__ attribute), the diagnostic should reflect that —
not report the dunder as "possibly missing".
def _(flag: bool):
class GoodManager:
def __enter__(self) -> str:
return "foo"
def __exit__(self, exc_type, exc_value, traceback): ...
class BadManager:
def __enter__(self) -> str:
return "bar"
# `__exit__` is present but not callable
__exit__: int = 32
context_expr = GoodManager() if flag else BadManager()
# error: [invalid-context-manager] "Object of type `GoodManager | BadManager` cannot be used with `with` because it does not correctly implement `__exit__`"
with context_expr as f:
reveal_type(f) # revealed: str
__enter__ methoddef _(flag: bool):
class Manager:
if flag:
def __enter__(self) -> str:
return "abcd"
def __exit__(self, *args): ...
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because the method `__enter__` may be missing"
with Manager() as f:
reveal_type(f) # revealed: str
__enter__ signatureclass Manager:
def __enter__() -> str:
return "foo"
def __exit__(self, exc_type, exc_value, traceback): ...
context_expr = Manager()
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because it does not correctly implement `__enter__`"
with context_expr as f:
reveal_type(f) # revealed: str
withIf a synchronous with statement is used on a type with __aenter__ and __aexit__, we show a
diagnostic hint that the user might have intended to use async with instead.
class Manager:
async def __aenter__(self): ...
async def __aexit__(self, *args): ...
# snapshot: invalid-context-manager
with Manager():
pass
error[invalid-context-manager]: Object of type `Manager` cannot be used with `with` because it does not implement `__enter__` and `__exit__`
--> src/mdtest_snippet.py:6:6
|
6 | with Manager():
| ^^^^^^^^^
|
info: Objects of type `Manager` can be used as async context managers
info: Consider using `async with` here
The sub-diagnostic is also provided if the signatures of __aenter__ and __aexit__ do not match
the expected signatures for a context manager:
class Manager:
async def __aenter__(self): ...
async def __aexit__(self, typ: str, exc, traceback): ...
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because it does not implement `__enter__` and `__exit__`"
with Manager():
pass
Similarly, we also show the hint if the functions have the wrong number of arguments:
class Manager:
async def __aenter__(self, wrong_extra_arg): ...
async def __aexit__(self, typ, exc, traceback, wrong_extra_arg): ...
# error: [invalid-context-manager] "Object of type `Manager` cannot be used with `with` because it does not implement `__enter__` and `__exit__`"
with Manager():
pass