Skip to content

Commit 04113d5

Browse files
committed
[mypyc] Fix using values from other modules that were reexported
There are a couple parts to this: * Compile module attribute accesses by compiling the LHS on its own instead of by loading its fullname (which will be what mypy thinks its source is) * Only use the static modules if they have actually been imported Closes #393.
1 parent 88e2b67 commit 04113d5

File tree

2 files changed

+30
-37
lines changed

2 files changed

+30
-37
lines changed

mypyc/genops.py

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ def f(x: int) -> int:
1616
from typing import (
1717
TypeVar, Callable, Dict, List, Tuple, Optional, Union, Sequence, Set, Any, cast
1818
)
19-
from typing_extensions import overload, ClassVar, NoReturn
19+
from typing_extensions import overload, NoReturn
20+
from collections import OrderedDict
2021
from abc import abstractmethod
21-
import sys
2222
import importlib.util
2323
import itertools
2424

@@ -192,7 +192,7 @@ def build_ir(modules: List[MypyFile],
192192
builder = IRBuilder(types, graph, errors, mapper, module_names, pbv, options)
193193
builder.visit_mypy_file(module)
194194
module_ir = ModuleIR(
195-
builder.imports,
195+
list(builder.imports),
196196
builder.functions,
197197
builder.classes,
198198
builder.final_names
@@ -1050,7 +1050,9 @@ def __init__(self,
10501050

10511051
self.errors = errors
10521052
self.mapper = mapper
1053-
self.imports = [] # type: List[str]
1053+
# We use an OrderedDict for this so we can maintain insertion order
1054+
# and do quick lookups
1055+
self.imports = OrderedDict() # type: OrderedDict[str, None]
10541056

10551057
def visit_mypy_file(self, mypyfile: MypyFile) -> None:
10561058
if mypyfile.fullname() in ('typing', 'abc'):
@@ -1518,29 +1520,8 @@ def allocate_class(self, cdef: ClassDef) -> None:
15181520
[self.load_globals_dict(), self.load_static_unicode(cdef.name),
15191521
tp], cdef.line)
15201522

1521-
# An unfortunate hack: for some stdlib modules, pull in modules
1522-
# that the stubs reexport things from. This works around #393
1523-
# in these cases.
1524-
import_maps = {
1525-
'os': tuple(['os.path'] + ([] if sys.platform == 'win32' else ['posix'])),
1526-
'os.path': ('os',),
1527-
'tokenize': ('token',),
1528-
'weakref': ('_weakref',),
1529-
'asyncio': ('asyncio.events', 'asyncio.tasks',),
1530-
'click': ('click.core', 'click.termui', 'click.decorators',
1531-
'click.exceptions', 'click.types'),
1532-
'ast': ('_ast',),
1533-
} # type: ClassVar[Dict[str, Sequence[str]]]
1534-
15351523
def gen_import(self, id: str, line: int) -> None:
1536-
if id in IRBuilder.import_maps:
1537-
for dep in IRBuilder.import_maps[id]:
1538-
self._gen_import(dep, line)
1539-
1540-
self._gen_import(id, line)
1541-
1542-
def _gen_import(self, id: str, line: int) -> None:
1543-
self.imports.append(id)
1524+
self.imports[id] = None
15441525

15451526
needs_import, out = BasicBlock(), BasicBlock()
15461527
first_load = self.add(LoadStatic(object_rprimitive, 'module', id))
@@ -2817,7 +2798,7 @@ def visit_name_expr(self, expr: NameExpr) -> Value:
28172798
if value is not None:
28182799
return value
28192800

2820-
if isinstance(expr.node, MypyFile):
2801+
if isinstance(expr.node, MypyFile) and expr.node.fullname() in self.imports:
28212802
return self.load_module(expr.node.fullname())
28222803

28232804
# If the expression is locally defined, then read the result from the corresponding
@@ -2853,11 +2834,11 @@ def visit_member_expr(self, expr: MemberExpr) -> Value:
28532834
if value is not None:
28542835
return value
28552836

2856-
if self.is_module_member_expr(expr):
2857-
return self.load_module_attr(expr)
2858-
else:
2859-
obj = self.accept(expr.expr)
2860-
return self.get_attr(obj, expr.name, self.node_type(expr), expr.line)
2837+
if isinstance(expr.node, MypyFile) and expr.node.fullname() in self.imports:
2838+
return self.load_module(expr.node.fullname())
2839+
2840+
obj = self.accept(expr.expr)
2841+
return self.get_attr(obj, expr.name, self.node_type(expr), expr.line)
28612842

28622843
def get_attr(self, obj: Value, attr: str, result_type: RType, line: int) -> Value:
28632844
if (isinstance(obj.type, RInstance) and obj.type.class_ir.is_ext_class
@@ -5210,7 +5191,8 @@ def load_global(self, expr: NameExpr) -> Value:
52105191
"""
52115192
# If the global is from 'builtins', turn it into a module attr load instead
52125193
if self.is_builtin_ref_expr(expr):
5213-
return self.load_module_attr(expr)
5194+
assert expr.node, "RefExpr not resolved"
5195+
return self.load_module_attr_by_fullname(expr.node.fullname(), expr.line)
52145196
if (self.is_native_module_ref_expr(expr) and isinstance(expr.node, TypeInfo)
52155197
and not self.is_synthetic_type(expr.node)):
52165198
assert expr.fullname is not None
@@ -5257,10 +5239,6 @@ def load_static_unicode(self, value: str) -> Value:
52575239
def load_module(self, name: str) -> Value:
52585240
return self.add(LoadStatic(object_rprimitive, 'module', name))
52595241

5260-
def load_module_attr(self, expr: RefExpr) -> Value:
5261-
assert expr.node, "RefExpr not resolved"
5262-
return self.load_module_attr_by_fullname(expr.node.fullname(), expr.line)
5263-
52645242
def load_module_attr_by_fullname(self, fullname: str, line: int) -> Value:
52655243
module, _, name = fullname.rpartition('.')
52665244
left = self.load_module(module)

mypyc/test-data/run.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4645,3 +4645,18 @@ from native import foo
46454645

46464646
assert foo(None) == None
46474647
assert foo([1, 2, 3]) == ((1, 2, 3), [1, 2, 3])
4648+
4649+
[case testReexport]
4650+
import a
4651+
def f(x: int) -> int:
4652+
return a.g(x)
4653+
4654+
[file a.py]
4655+
from b import g
4656+
4657+
[file b.py]
4658+
def g(x: int) -> int:
4659+
return x + 1
4660+
4661+
[file driver.py]
4662+
# I only care that it compiles

0 commit comments

Comments
 (0)