8000 Don't expand global variables in body of a function with constrained … · python/mypy@28f92ac · GitHub
[go: up one dir, main page]

Skip to content

Commit 28f92ac

Browse files
ilevkivskyiIvan Levkivskyi
and
Ivan Levkivskyi
authored
Don't expand global variables in body of a function with constrained type variables (#9882)
See first test case for the scenario where this causes issues (the second test case is already handled elsewhere and just added for completeness). This is an old issue, but it is important to fix it now because recent changes to PEP 484 about re-export force people to use function aliases. Co-authored-by: Ivan Levkivskyi <ilevkivskyi@dropbox.com>
1 parent b55bfe0 commit 28f92ac

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

mypy/test/testtransform.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def test_transform(testcase: DataDrivenTestCase) -> None:
6060
and not os.path.splitext(
6161
os.path.basename(f.path))[0].endswith('_')):
6262
t = TypeAssertTransformVisitor()
63+
t.test_only = True
6364
f = t.mypyfile(f)
6465
a += str(f).split('\n')
6566
except CompileError as e:

mypy/treetransform.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
YieldFromExpr, NamedTupleExpr, TypedDictExpr, NonlocalDecl, SetComprehension,
2121
DictionaryComprehension, ComplexExpr, TypeAliasExpr, EllipsisExpr,
2222
YieldExpr, ExecStmt, Argument, BackquoteExpr, AwaitExpr, AssignmentExpr,
23-
OverloadPart, EnumCallExpr, REVEAL_TYPE
23+
OverloadPart, EnumCallExpr, REVEAL_TYPE, GDEF
2424
)
2525
from mypy.types import Type, FunctionLike, ProperType
2626from mypy.traverser import TraverserVisitor
@@ -37,6 +37,8 @@ class TransformVisitor(NodeVisitor[Node]):
3737
3838
Notes:
3939
40+
* This can only be used to transform functions or classes, not top-level
41+
statements, and/or modules as a whole.
4042
* Do not duplicate TypeInfo nodes. This would generally not be desirable.
4143
* Only update some name binding cross-references, but only those that
4244
refer to Var, Decorator or FuncDef nodes, not those targeting ClassDef or
@@ -48,6 +50,9 @@ class TransformVisitor(NodeVisitor[Node]):
4850
"""
4951

5052
def __init__(self) -> None:
53+
# To simplify testing, set this flag to True if you want to transform
54+
# all statements in a file (this is prohibited in normal mode).
55+
self.test_only = False
5156
# There may be multiple references to a Var node. Keep track of
5257
# Var translations using a dictionary.
5358
self.var_map = {} # type: Dict[Var, Var]
@@ -58,6 +63,7 @@ def __init__(self) -> None:
5863
self.func_placeholder_map = {} # type: Dict[FuncDef, FuncDef]
5964

6065
def visit_mypy_file(self, node: MypyFile) -> MypyFile:
66+
assert self.test_only, "This visitor should not be used for whole files."
6167
# NOTE: The 'names' and 'imports' instance variables will be empty!
6268
ignored_lines = {line: codes[:]
6369
for line, codes in node.ignored_lines.items()}
@@ -358,7 +364,10 @@ def copy_ref(self, new: RefExpr, original: RefExpr) -> None:
358364
new.fullname = original.fullname
359365
target = original.node
360366
if isinstance(target, Var):
361-
target = self.visit_var(target)
367+
# Do not transform references to global variables. See
368+
# testGenericFunctionAliasExpand for an example where this is important.
369+
if original.kind != GDEF:
370+
target = self.visit_var(target)
362371
elif isinstance(target, Decorator):
363372
target = self.visit_var(target.var)
364373
elif isinstance(target, FuncDef):

test-data/unit/check-generics.test

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2402,3 +2402,31 @@ def func(tp: Type[C[S]]) -> S:
24022402
else:
24032403
return reg[tp.test].test[0]
24042404
[builtins fixtures/dict.pyi]
2405+
2406+
[case testGenericFunctionAliasExpand]
2407+
from typing import Optional, TypeVar
2408+
2409+
T = TypeVar("T")
2410+
def gen(x: T) -> T: ...
2411+
gen_a = gen
2412+
2413+
S = TypeVar("S", int, str)
2414+
class C: ...
2415+
def test() -> Optional[S]:
2416+
reveal_type(gen_a(C())) # N: Revealed type is '__main__.C*'
2417+
return None
2418+
2419+
[case testGenericFunctionMemberExpand]
2420+
from typing import Optional, TypeVar, Callable
2421+
2422+
T = TypeVar("T")
2423+
2424+
class A:
2425+
def __init__(self) -> None:
2426+
self.gen: Callable[[T], T]
2427+
2428+
S = TypeVar("S", int, str)
2429+
class C: ...
2430+
def test() -> Optional[S]:
2431+
reveal_type(A().gen(C())) # N: Revealed type is '__main__.C*'
2432+
return None

0 commit comments

Comments
 (0)
0