Skip to content

Commit 0fdf81e

Browse files
committed
chore: ContextManagerError message update
1 parent bbfcf6e commit 0fdf81e

File tree

3 files changed

+341
-12
lines changed

3 files changed

+341
-12
lines changed

crates/ty_python_semantic/resources/mdtest/with/async.md

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,117 @@ async def main():
8484
...
8585
```
8686

87+
## Union context managers with specific member issues
88+
89+
### Union where one member lacks `__aenter__`
90+
91+
```py
92+
async def _(flag: bool):
93+
class Bound:
94+
async def __aenter__(self) -> str:
95+
return "foo"
96+
97+
async def __aexit__(self, exc_type, exc_value, traceback): ...
98+
99+
class EnterUnbound:
100+
async def __aexit__(self): ...
101+
102+
context_expr = Bound() if flag else EnterUnbound()
103+
104+
# error: [invalid-context-manager] "Object of type `Bound | EnterUnbound` cannot be used with `async with` because the method `__aenter__` of `EnterUnbound` is possibly unbound"
105+
async with context_expr as f:
106+
reveal_type(f) # revealed: CoroutineType[Any, Any, str]
107+
```
108+
109+
### Union where one member lacks `__aexit__`
110+
111+
```py
112+
async def _(flag: bool):
113+
class Bound:
114+
async def __aenter__(self) -> str:
115+
return "foo"
116+
117+
async def __aexit__(self, exc_type, exc_value, traceback): ...
118+
119+
class ExitUnbound:
120+
async def __aenter__(self): ...
121+
122+
context_expr = Bound() if flag else ExitUnbound()
123+
124+
# error: [invalid-context-manager] "Object of type `Bound | ExitUnbound` cannot be used with `async with` because the method `__aexit__` of `ExitUnbound` is possibly unbound"
125+
async with context_expr as f:
126+
reveal_type(f) # revealed: str | Unknown
127+
```
128+
129+
### Union where one member lacks both methods
130+
131+
```py
132+
async def _(flag: bool):
133+
class Bound:
134+
async def __aenter__(self) -> str:
135+
return "foo"
136+
137+
async def __aexit__(self, exc_type, exc_value, traceback): ...
138+
139+
class Unbound: ...
140+
context_expr = Bound() if flag else Unbound()
141+
142+
# error: [invalid-context-manager] "Object of type `Bound | Unbound` cannot be used with `async with` because the methods `__aenter__` and `__aexit__` of `Unbound` are possibly unbound"
143+
async with context_expr as f:
144+
reveal_type(f) # revealed: CoroutineType[Any, Any, str]
145+
```
146+
147+
### Complex union with multiple issues
148+
149+
```py
150+
async def _(flag: int):
151+
class Bound:
152+
async def __aenter__(self) -> str:
153+
return "foo"
154+
155+
async def __aexit__(self, exc_type, exc_value, traceback): ...
156+
157+
class EnterUnbound:
158+
async def __aexit__(self): ...
159+
160+
class ExitUnbound:
161+
async def __aenter__(self): ...
162+
163+
if flag == 0:
164+
context_expr = Bound()
165+
elif flag == 1:
166+
context_expr = EnterUnbound()
167+
else:
168+
context_expr = ExitUnbound()
169+
170+
# error: [invalid-context-manager] "Object of type `Bound | EnterUnbound | ExitUnbound` cannot be used with `async with` because the method `__aenter__` of `EnterUnbound` is possibly unbound, and the method `__aexit__` of `ExitUnbound` is possibly unbound"
171+
async with context_expr as f:
172+
reveal_type(f) # revealed: CoroutineType[Any, Any, str] | Unknown
173+
```
174+
175+
### Union with multiple members missing the same methods
176+
177+
```py
178+
async def _(flag: int):
179+
class EnterUnbound:
180+
async def __aexit__(self): ...
181+
182+
class ExitUnbound:
183+
async def __aenter__(self): ...
184+
185+
class Unbound: ...
186+
if flag == 0:
187+
context_expr = EnterUnbound()
188+
elif flag == 1:
189+
context_expr = ExitUnbound()
190+
else:
191+
context_expr = Unbound()
192+
193+
# error: [invalid-context-manager] "Object of type `EnterUnbound | ExitUnbound | Unbound` cannot be used with `async with` because the method `__aenter__` of `EnterUnbound` and `Unbound` are possibly unbound, and the method `__aexit__` of `ExitUnbound` and `Unbound` are possibly unbound"
194+
async with context_expr:
195+
...
196+
```
197+
87198
## Context manager with non-callable `__aexit__` attribute
88199

89200
```py
@@ -113,7 +224,7 @@ async def _(flag: bool):
113224
class NotAContextManager: ...
114225
context_expr = Manager1() if flag else NotAContextManager()
115226

116-
# error: [invalid-context-manager] "Object of type `Manager1 | NotAContextManager` cannot be used with `async with` because the methods `__aenter__` and `__aexit__` are possibly unbound"
227+
# error: [invalid-context-manager] "Object of type `Manager1 | NotAContextManager` cannot be used with `async with` because the methods `__aenter__` and `__aexit__` of `NotAContextManager` are possibly unbound"
117228
async with context_expr as f:
118229
reveal_type(f) # revealed: str
119230
```

crates/ty_python_semantic/resources/mdtest/with/sync.md

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,117 @@ with Manager():
8585
...
8686
```
8787

88+
## Union context managers with specific member issues
89+
90+
### Union where one member lacks `__enter__`
91+
92+
```py
93+
def _(flag: bool):
94+
class Bound:
95+
def __enter__(self) -> str:
96+
return "foo"
97+
98+
def __exit__(self, exc_type, exc_value, traceback): ...
99+
100+
class EnterUnbound:
101+
def __exit__(self): ...
102+
103+
context_expr = Bound() if flag else EnterUnbound()
104+
105+
# error: [invalid-context-manager] "Object of type `Bound | EnterUnbound` cannot be used with `with` because the method `__enter__` of `EnterUnbound` is possibly unbound"
106+
with context_expr as f:
107+
reveal_type(f) # revealed: str
108+
```
109+
110+
### Union where one member lacks `__exit__`
111+
112+
```py
113+
def _(flag: bool):
114+
class Bound:
115+
def __enter__(self) -> str:
116+
return "foo"
117+
118+
def __exit__(self, exc_type, exc_value, traceback): ...
119+
120+
class ExitUnbound:
121+
def __enter__(self): ...
122+
123+
context_expr = Bound() if flag else ExitUnbound()
124+
125+
# error: [invalid-context-manager] "Object of type `Bound | ExitUnbound` cannot be used with `with` because the method `__exit__` of `ExitUnbound` is possibly unbound"
126+
with context_expr as f:
127+
reveal_type(f) # revealed: str | Unknown
128+
```
129+
130+
### Union where one member lacks both methods
131+
132+
```py
133+
def _(flag: bool):
134+
class Bound:
135+
def __enter__(self) -> str:
136+
return "foo"
137+
138+
def __exit__(self, exc_type, exc_value, traceback): ...
139+
140+
class Unbound: ...
141+
context_expr = Bound() if flag else Unbound()
142+
143+
# error: [invalid-context-manager] "Object of type `Bound | Unbound` cannot be used with `with` because the methods `__enter__` and `__exit__` of `Unbound` are possibly unbound"
144+
with context_expr as f:
145+
reveal_type(f) # revealed: str
146+
```
147+
148+
### Complex union with multiple issues
149+
150+
```py
151+
def _(flag: int):
152+
class Bound:
153+
def __enter__(self) -> str:
154+
return "foo"
155+
156+
def __exit__(self, exc_type, exc_value, traceback): ...
157+
158+
class EnterUnbound:
159+
def __exit__(self): ...
160+
161+
class ExitUnbound:
162+
def __enter__(self): ...
163+
164+
if flag == 0:
165+
context_expr = Bound()
166+
elif flag == 1:
167+
context_expr = EnterUnbound()
168+
else:
169+
context_expr = ExitUnbound()
170+
171+
# error: [invalid-context-manager] "Object of type `Bound | EnterUnbound | ExitUnbound` cannot be used with `with` because the method `__enter__` of `EnterUnbound` is possibly unbound, and the method `__exit__` of `ExitUnbound` is possibly unbound"
172+
with context_expr as f:
173+
reveal_type(f) # revealed: str | Unknown
174+
```
175+
176+
### Union with multiple members missing the same methods
177+
178+
```py
179+
def _(flag: int):
180+
class EnterUnbound:
181+
def __exit__(self): ...
182+
183+
class ExitUnbound:
184+
def __enter__(self): ...
185+
186+
class Unbound: ...
187+
if flag == 0:
188+
context_expr = EnterUnbound()
189+
elif flag == 1:
190+
context_expr = ExitUnbound()
191+
else:
192+
context_expr = Unbound()
193+
194+
# error: [invalid-context-manager] "Object of type `EnterUnbound | ExitUnbound | Unbound` cannot be used with `with` because the method `__enter__` of `EnterUnbound` and `Unbound` are possibly unbound, and the method `__exit__` of `ExitUnbound` and `Unbound` are possibly unbound"
195+
with context_expr:
196+
...
197+
```
198+
88199
## Context manager with non-callable `__exit__` attribute
89200

90201
```py
@@ -113,7 +224,7 @@ def _(flag: bool):
113224
class NotAContextManager: ...
114225
context_expr = Manager1() if flag else NotAContextManager()
115226

116-
# error: [invalid-context-manager] "Object of type `Manager1 | NotAContextManager` cannot be used with `with` because the methods `__enter__` and `__exit__` are possibly unbound"
227+
# error: [invalid-context-manager] "Object of type `Manager1 | NotAContextManager` cannot be used with `with` because the methods `__enter__` and `__exit__` of `NotAContextManager` are possibly unbound"
117228
with context_expr as f:
118229
reveal_type(f) # revealed: str
119230
```

0 commit comments

Comments
 (0)