Skip to content

Commit 1ef02bb

Browse files
authored
[mypyc] Fix using values from other modules that were reexported (python#7496)
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 Fixes mypyc/mypyc#393.
1 parent 50eb922 commit 1ef02bb

File tree

2 files changed

+44
-37
lines changed

2 files changed

+44
-37
lines changed

mypyc/genops.py

Lines changed: 16 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,10 @@ def __init__(self,
10501050

10511051
self.errors = errors
10521052
self.mapper = mapper
1053-
self.imports = [] # type: List[str]
1053+
# Notionally a list of all of the modules imported by the
1054+
# module being compiled, but stored as an OrderedDict so we
1055+
# can also do quick lookups.
1056+
self.imports = OrderedDict() # type: OrderedDict[str, None]
10541057

10551058
def visit_mypy_file(self, mypyfile: MypyFile) -> None:
10561059
if mypyfile.fullname() in ('typing', 'abc'):
@@ -1518,29 +1521,8 @@ def allocate_class(self, cdef: ClassDef) -> None:
15181521
[self.load_globals_dict(), self.load_static_unicode(cdef.name),
15191522
tp], cdef.line)
15201523

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-
15351524
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)
1525+
self.imports[id] = None
15441526

15451527
needs_import, out = BasicBlock(), BasicBlock()
15461528
first_load = self.add(LoadStatic(object_rprimitive, 'module', id))
@@ -2817,7 +2799,7 @@ def visit_name_expr(self, expr: NameExpr) -> Value:
28172799
if value is not None:
28182800
return value
28192801

2820-
if isinstance(expr.node, MypyFile):
2802+
if isinstance(expr.node, MypyFile) and expr.node.fullname() in self.imports:
28212803
return self.load_module(expr.node.fullname())
28222804

28232805
# If the expression is locally defined, then read the result from the corresponding
@@ -2853,11 +2835,11 @@ def visit_member_expr(self, expr: MemberExpr) -> Value:
28532835
if value is not None:
28542836
return value
28552837

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)
2838+
if isinstance(expr.node, MypyFile) and expr.node.fullname() in self.imports:
2839+
return self.load_module(expr.node.fullname())
2840+
2841+
obj = self.accept(expr.expr)
2842+
return self.get_attr(obj, expr.name, self.node_type(expr), expr.line)
28612843

28622844
def get_attr(self, obj: Value, attr: str, result_type: RType, line: int) -> Value:
28632845
if (isinstance(obj.type, RInstance) and obj.type.class_ir.is_ext_class
@@ -5210,7 +5192,8 @@ def load_global(self, expr: NameExpr) -> Value:
52105192
"""
52115193
# If the global is from 'builtins', turn it into a module attr load instead
52125194
if self.is_builtin_ref_expr(expr):
5213-
return self.load_module_attr(expr)
5195+
assert expr.node, "RefExpr not resolved"
5196+
return self.load_module_attr_by_fullname(expr.node.fullname(), expr.line)
52145197
if (self.is_native_module_ref_expr(expr) and isinstance(expr.node, TypeInfo)
52155198
and not self.is_synthetic_type(expr.node)):
52165199
assert expr.fullname is not None
@@ -5257,10 +5240,6 @@ def load_static_unicode(self, value: str) -> Value:
52575240
def load_module(self, name: str) -> Value:
52585241
return self.add(LoadStatic(object_rprimitive, 'module', name))
52595242

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-
52645243
def load_module_attr_by_fullname(self, fullname: str, line: int) -> Value:
52655244
module, _, name = fullname.rpartition('.')
52665245
left = self.load_module(module)

mypyc/test-data/run.test

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4645,3 +4645,31 @@ 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+
# Test that we properly handle accessing values that have been reexported
4651+
import a
4652+
def f(x: int) -> int:
4653+
return a.g(x) + a.foo + a.b.foo
4654+
4655+
whatever = a.A()
4656+
4657+
[file a.py]
4658+
from b import g as g, A as A, foo as foo
4659+
import b
4660+
4661+
[file b.py]
4662+
def g(x: int) -> int:
4663+
return x + 1
4664+
4665+
class A:
4666+
pass
4667+
4668+
foo = 20
4669+
4670+
[file driver.py]
4671+
from native import f, whatever
4672+
import b
4673+
4674+
assert f(20) == 61
4675+
assert isinstance(whatever, b.A)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy