Skip to content

Commit a3e890f

Browse files
asottiletushar-deepsource
authored andcommitted
Fix regression when a contextmanager yields a generic (python#11870)
Resolves python#11852
1 parent 0a77194 commit a3e890f

File tree

2 files changed

+33
-45
lines changed

2 files changed

+33
-45
lines changed

mypy/plugins/default.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from mypy.subtypes import is_subtype
1616
from mypy.typeops import make_simplified_union
1717
from mypy.checkexpr import is_literal_type_like
18-
from mypy.checker import detach_callable
1918

2019

2120
class DefaultPlugin(Plugin):
@@ -192,12 +191,12 @@ def contextmanager_callback(ctx: FunctionContext) -> Type:
192191
and isinstance(default_return, CallableType)):
193192
# The stub signature doesn't preserve information about arguments so
194193
# add them back here.
195-
return detach_callable(default_return.copy_modified(
194+
return default_return.copy_modified(
196195
arg_types=arg_type.arg_types,
197196
arg_kinds=arg_type.arg_kinds,
198197
arg_names=arg_type.arg_names,
199198
variables=arg_type.variables,
200-
is_ellipsis_args=arg_type.is_ellipsis_args))
199+
is_ellipsis_args=arg_type.is_ellipsis_args)
201200
return ctx.default_return_type
202201

203202

test-data/unit/check-default-plugin.test

+31-42
Original file line numberDiff line numberDiff line change
@@ -24,56 +24,15 @@ f = g # E: Incompatible types in assignment (expression has type "Callable[[Any,
2424
[typing fixtures/typing-medium.pyi]
2525
[builtins fixtures/tuple.pyi]
2626

27-
[case testContextManagerWithGenericFunctionAndSendType]
28-
from contextlib import contextmanager
29-
from typing import TypeVar, Generator
30-
31-
T = TypeVar('T')
32-
S = TypeVar('S')
33-
34-
@contextmanager
35-
def yield_id(item: T) -> Generator[T, S, None]:
36-
yield item
37-
38-
reveal_type(yield_id) # N: Revealed type is "def [T] (item: T`-1) -> contextlib.GeneratorContextManager[T`-1]"
39-
40-
with yield_id(1) as x:
41-
reveal_type(x) # N: Revealed type is "builtins.int*"
42-
43-
f = yield_id
44-
def g(x, y): pass
45-
f = g # E: Incompatible types in assignment (expression has type "Callable[[Any, Any], Any]", variable has type "Callable[[T], GeneratorContextManager[T]]")
46-
[typing fixtures/typing-medium.pyi]
47-
[builtins fixtures/tuple.pyi]
48-
4927
[case testAsyncContextManagerWithGenericFunction]
5028
# flags: --python-version 3.7
5129
from contextlib import asynccontextmanager
52-
from typing import TypeVar, AsyncIterator
53-
54-
T = TypeVar('T')
55-
56-
@asynccontextmanager
57-
async def yield_id(item: T) -> AsyncIterator[T]:
58-
yield item
59-
60-
reveal_type(yield_id) # N: Revealed type is "def [T] (item: T`-1) -> typing.AsyncContextManager[T`-1]"
61-
62-
async with yield_id(1) as x:
63-
reveal_type(x) # N: Revealed type is "builtins.int*"
64-
[typing fixtures/typing-async.pyi]
65-
[builtins fixtures/tuple.pyi]
66-
67-
[case testAsyncContextManagerWithGenericFunctionAndSendType]
68-
# flags: --python-version 3.7
69-
from contextlib import asynccontextmanager
7030
from typing import TypeVar, AsyncGenerator
7131

7232
T = TypeVar('T')
73-
S = TypeVar('S')
7433

7534
@asynccontextmanager
76-
async def yield_id(item: T) -> AsyncGenerator[T, S]:
35+
async def yield_id(item: T) -> AsyncGenerator[T, None]:
7736
yield item
7837

7938
reveal_type(yield_id) # N: Revealed type is "def [T] (item: T`-1) -> typing.AsyncContextManager[T`-1]"
@@ -83,6 +42,36 @@ async with yield_id(1) as x:
8342
[typing fixtures/typing-async.pyi]
8443
[builtins fixtures/tuple.pyi]
8544

45+
[case testContextManagerReturnsGenericFunction]
46+
import contextlib
47+
from typing import Callable
48+
from typing import Generator
49+
from typing import Iterable
50+
from typing import TypeVar
51+
52+
TArg = TypeVar('TArg')
53+
TRet = TypeVar('TRet')
54+
55+
@contextlib.contextmanager
56+
def _thread_mapper(maxsize: int) -> Generator[
57+
Callable[[Callable[[TArg], TRet], Iterable[TArg]], Iterable[TRet]],
58+
None, None,
59+
]:
60+
# defined inline as there isn't a builtins.map fixture
61+
def my_map(f: Callable[[TArg], TRet], it: Iterable[TArg]) -> Iterable[TRet]:
62+
for x in it:
63+
yield f(x)
64+
65+
yield my_map
66+
67+
def identity(x: int) -> int: return x
68+
69+
with _thread_mapper(1) as m:
70+
lst = list(m(identity, [2, 3]))
71+
reveal_type(lst) # N: Revealed type is "builtins.list[builtins.int*]"
72+
[typing fixtures/typing-medium.pyi]
73+
[builtins fixtures/list.pyi]
74+
8675
[case testContextManagerWithUnspecifiedArguments]
8776
from contextlib import contextmanager
8877
from typing import Callable, Iterator

0 commit comments

Comments
 (0)