Skip to content

Commit 94c854d

Browse files
add testcase which closes #5758
1 parent c714d26 commit 94c854d

File tree

1 file changed

+140
-70
lines changed

1 file changed

+140
-70
lines changed

mypy/test/testpep561.py

+140-70
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from contextlib import contextmanager
2+
from enum import Enum
23
import os
34
import sys
45
import tempfile
5-
from typing import Tuple, List, Generator, Optional
6+
from typing import Tuple, List, Generator, Optional, Mapping, Any
67
from unittest import TestCase, main
78

89
import mypy.api
@@ -21,9 +22,8 @@
2122
reveal_type(a)
2223
"""
2324

24-
NAMESPACE_PROGRAM = """
25-
from typedpkg_nested.nested_package.nested_module import nested_func
26-
from typedpkg_namespace.alpha.alpha_module import alpha_func
25+
_NAMESPACE_PROGRAM = """
26+
{import_style}
2727
2828
nested_func("abc")
2929
alpha_func(False)
@@ -33,26 +33,79 @@
3333
"""
3434

3535

36-
def check_mypy_run(cmd_line: List[str],
37-
python_executable: str = sys.executable,
38-
expected_out: str = '',
39-
expected_err: str = '',
40-
expected_returncode: int = 1,
41-
venv_dir: Optional[str] = None) -> None:
42-
"""Helper to run mypy and check the output."""
43-
if venv_dir is not None:
44-
old_dir = os.getcwd()
45-
os.chdir(venv_dir)
46-
try:
47-
if python_executable != sys.executable:
48-
cmd_line.append('--python-executable={}'.format(python_executable))
49-
out, err, returncode = mypy.api.run(cmd_line)
50-
assert out == expected_out, err
51-
assert err == expected_err, out
52-
assert returncode == expected_returncode, returncode
53-
finally:
36+
class NamespaceProgramImportStyle(Enum):
37+
from_import = """\
38+
from typedpkg_nested.nested_package.nested_module import nested_func
39+
from typedpkg_namespace.alpha.alpha_module import alpha_func"""
40+
import_as = """\
41+
import typedpkg_nested.nested_package.nested_module as nm; nested_func = nm.nested_func
42+
import typedpkg_namespace.alpha.alpha_module as am; alpha_func = am.alpha_func"""
43+
regular_import = """\
44+
import typedpkg_nested.nested_package.nested_module; \
45+
nested_func = typedpkg_nested.nested_package.nested_module.nested_func
46+
import typedpkg_namespace.alpha.alpha_module; \
47+
alpha_func = typedpkg_namespace.alpha.alpha_module.alpha_func"""
48+
49+
50+
class SimpleProgramMessage(Enum):
51+
msg_dne = "{tempfile}:3: error: Module 'typedpkg' has no attribute 'dne'"
52+
msg_list = "{tempfile}:5: error: Revealed type is 'builtins.list[builtins.str]'"
53+
msg_tuple = "{tempfile}:5: error: Revealed type is 'builtins.tuple[builtins.str]'"
54+
55+
56+
class NamespaceProgramMessage(Enum):
57+
bool_str = ('{tempfile}:8: error: Argument 1 to "nested_func" has incompatible type '
58+
'"bool"; expected "str"')
59+
int_bool = ('{tempfile}:9: error: Argument 1 to "alpha_func" has incompatible type '
60+
'"int"; expected "bool"')
61+
62+
63+
def create_namespace_program_source(import_style: NamespaceProgramImportStyle) -> str:
64+
return _NAMESPACE_PROGRAM.format(import_style=import_style.value)
65+
66+
67+
class ExampleProgram(object):
68+
def __init__(self, source_code: str) -> None:
69+
self._source_code = source_code
70+
71+
self._temp_file = None # type: Any
72+
73+
def init(self) -> None:
74+
self._temp_file = tempfile.NamedTemporaryFile(mode='w+')
75+
self._temp_file.write(self._source_code)
76+
self._temp_file.flush()
77+
78+
def cleanup(self) -> None:
79+
if self._temp_file:
80+
self._temp_file.close()
81+
82+
def build_msg(self, *msgs: Enum) -> str:
83+
return '\n'.join(
84+
msg.value.format(tempfile=self._temp_file.name)
85+
for msg in msgs
86+
) + '\n'
87+
88+
def check_mypy_run(self,
89+
python_executable: str,
90+
expected_out: List[Enum],
91+
expected_err: str = '',
92+
expected_returncode: int = 1,
93+
venv_dir: Optional[str] = None) -> None:
94+
"""Helper to run mypy and check the output."""
95+
cmd_line = [self._temp_file.name]
5496
if venv_dir is not None:
55-
os.chdir(old_dir)
97+
old_dir = os.getcwd()
98+
os.chdir(venv_dir)
99+
try:
100+
if python_executable != sys.executable:
101+
cmd_line.append('--python-executable={}'.format(python_executable))
102+
out, err, returncode = mypy.api.run(cmd_line)
103+
assert out == self.build_msg(*expected_out), err
104+
assert err == expected_err, out
105+
assert returncode == expected_returncode, returncode
106+
finally:
107+
if venv_dir is not None:
108+
os.chdir(old_dir)
56109

57110

58111
class TestPEP561(TestCase):
@@ -102,127 +155,144 @@ def install_package(self, pkg: str,
102155
self.fail('\n'.join(lines))
103156

104157
def setUp(self) -> None:
105-
self.temp_file_dir = tempfile.TemporaryDirectory()
106-
self.tempfile = os.path.join(self.temp_file_dir.name, 'simple.py')
107-
with open(self.tempfile, 'w+') as file:
108-
file.write(SIMPLE_PROGRAM)
109-
self.namespace_tempfile = os.path.join(self.temp_file_dir.name, 'namespace_program.py')
110-
with open(self.namespace_tempfile, 'w+') as file:
111-
file.write(NAMESPACE_PROGRAM)
112-
113-
self.msg_dne = \
114-
"{}:3: error: Module 'typedpkg' has no attribute 'dne'\n".format(self.tempfile)
115-
self.msg_list = \
116-
"{}:5: error: Revealed type is 'builtins.list[builtins.str]'\n".format(self.tempfile)
117-
self.msg_tuple = \
118-
"{}:5: error: Revealed type is 'builtins.tuple[builtins.str]'\n".format(self.tempfile)
119-
120-
self.namespace_msg_bool_str = (
121-
'{0}:8: error: Argument 1 to "nested_func" has incompatible type "bool"; '
122-
'expected "str"\n'.format(self.namespace_tempfile))
123-
self.namespace_msg_int_bool = (
124-
'{0}:9: error: Argument 1 to "alpha_func" has incompatible type "int"; '
125-
'expected "bool"\n'.format(self.namespace_tempfile))
158+
self.simple_example_program = ExampleProgram(SIMPLE_PROGRAM)
159+
self.from_namespace_example_program = ExampleProgram(
160+
create_namespace_program_source(NamespaceProgramImportStyle.from_import))
161+
self.import_as_namespace_example_program = ExampleProgram(
162+
create_namespace_program_source(NamespaceProgramImportStyle.from_import))
163+
self.regular_import_namespace_example_program = ExampleProgram(
164+
create_namespace_program_source(NamespaceProgramImportStyle.from_import))
126165

127166
def tearDown(self) -> None:
128-
self.temp_file_dir.cleanup()
167+
self.simple_example_program.cleanup()
168+
self.from_namespace_example_program.cleanup()
129169

130170
def test_get_pkg_dirs(self) -> None:
131171
"""Check that get_package_dirs works."""
132172
dirs = get_site_packages_dirs(sys.executable)
133173
assert dirs
134174

135175
def test_typedpkg_stub_package(self) -> None:
176+
self.simple_example_program.init()
136177
with self.virtualenv() as venv:
137178
venv_dir, python_executable = venv
138179
self.install_package('typedpkg-stubs', python_executable)
139-
check_mypy_run(
140-
[self.tempfile],
180+
self.simple_example_program.check_mypy_run(
141181
python_executable,
142-
expected_out=self.msg_dne + self.msg_list,
182+
[SimpleProgramMessage.msg_dne,
183+
SimpleProgramMessage.msg_list],
143184
venv_dir=venv_dir,
144185
)
145186

146187
def test_typedpkg(self) -> None:
188+
self.simple_example_program.init()
147189
with self.virtualenv() as venv:
148190
venv_dir, python_executable = venv
149191
self.install_package('typedpkg', python_executable)
150-
check_mypy_run(
151-
[self.tempfile],
192+
self.simple_example_program.check_mypy_run(
152193
python_executable,
153-
expected_out=self.msg_tuple,
194+
[SimpleProgramMessage.msg_tuple],
154195
venv_dir=venv_dir,
155196
)
156197

157198
def test_stub_and_typed_pkg(self) -> None:
199+
self.simple_example_program.init()
158200
with self.virtualenv() as venv:
159201
venv_dir, python_executable = venv
160202
self.install_package('typedpkg', python_executable)
161203
self.install_package('typedpkg-stubs', python_executable)
162-
check_mypy_run(
163-
[self.tempfile],
204+
self.simple_example_program.check_mypy_run(
164205
python_executable,
165-
expected_out=self.msg_list,
206+
[SimpleProgramMessage.msg_list],
166207
venv_dir=venv_dir,
167208
)
168209

169210
def test_typedpkg_stubs_python2(self) -> None:
211+
self.simple_example_program.init()
170212
python2 = try_find_python2_interpreter()
171213
if python2:
172214
with self.virtualenv(python2) as venv:
173215
venv_dir, py2 = venv
174216
self.install_package('typedpkg-stubs', py2)
175-
check_mypy_run(
176-
[self.tempfile],
217+
self.simple_example_program.check_mypy_run(
177218
py2,
178-
expected_out=self.msg_dne + self.msg_list,
219+
[SimpleProgramMessage.msg_dne,
220+
SimpleProgramMessage.msg_list],
179221
venv_dir=venv_dir,
180222
)
181223

182224
def test_typedpkg_python2(self) -> None:
225+
self.simple_example_program.init()
183226
python2 = try_find_python2_interpreter()
184227
if python2:
185228
with self.virtualenv(python2) as venv:
186229
venv_dir, py2 = venv
187230
self.install_package('typedpkg', py2)
188-
check_mypy_run(
189-
[self.tempfile],
231+
self.simple_example_program.check_mypy_run(
190232
py2,
191-
expected_out=self.msg_tuple,
233+
[SimpleProgramMessage.msg_tuple],
192234
venv_dir=venv_dir,
193235
)
194236

195237
def test_typedpkg_egg(self) -> None:
238+
self.simple_example_program.init()
196239
with self.virtualenv() as venv:
197240
venv_dir, python_executable = venv
198241
self.install_package('typedpkg', python_executable, use_pip=False)
199-
check_mypy_run(
200-
[self.tempfile],
242+
self.simple_example_program.check_mypy_run(
201243
python_executable,
202-
expected_out=self.msg_tuple,
244+
[SimpleProgramMessage.msg_tuple],
203245
venv_dir=venv_dir,
204246
)
205247

206248
def test_typedpkg_editable(self) -> None:
249+
self.simple_example_program.init()
207250
with self.virtualenv() as venv:
208251
venv_dir, python_executable = venv
209252
self.install_package('typedpkg', python_executable, editable=True)
210-
check_mypy_run(
211-
[self.tempfile],
253+
self.simple_example_program.check_mypy_run(
254+
python_executable,
255+
[SimpleProgramMessage.msg_tuple],
256+
venv_dir=venv_dir,
257+
)
258+
259+
def test_nested_and_namespace_from_import(self) -> None:
260+
self.from_namespace_example_program.init()
261+
with self.virtualenv() as venv:
262+
venv_dir, python_executable = venv
263+
self.install_package('typedpkg_nested', python_executable)
264+
self.install_package('typedpkg_namespace-alpha', python_executable)
265+
self.from_namespace_example_program.check_mypy_run(
266+
python_executable,
267+
[NamespaceProgramMessage.bool_str,
268+
NamespaceProgramMessage.int_bool],
269+
venv_dir=venv_dir,
270+
)
271+
272+
def test_nested_and_namespace_import_as(self) -> None:
273+
self.import_as_namespace_example_program.init()
274+
with self.virtualenv() as venv:
275+
venv_dir, python_executable = venv
276+
self.install_package('typedpkg_nested', python_executable)
277+
self.install_package('typedpkg_namespace-alpha', python_executable)
278+
self.import_as_namespace_example_program.check_mypy_run(
212279
python_executable,
213-
expected_out=self.msg_tuple,
280+
[NamespaceProgramMessage.bool_str,
281+
NamespaceProgramMessage.int_bool],
214282
venv_dir=venv_dir,
215283
)
216284

217-
def test_nested_and_namespace(self) -> None:
285+
def test_nested_and_namespace_regular_import(self) -> None:
286+
# This test case addresses https://github.com/python/mypy/issues/5767
287+
self.regular_import_namespace_example_program.init()
218288
with self.virtualenv() as venv:
219289
venv_dir, python_executable = venv
220290
self.install_package('typedpkg_nested', python_executable)
221291
self.install_package('typedpkg_namespace-alpha', python_executable)
222-
check_mypy_run(
223-
[self.namespace_tempfile],
292+
self.regular_import_namespace_example_program.check_mypy_run(
224293
python_executable,
225-
expected_out=self.namespace_msg_bool_str + self.namespace_msg_int_bool,
294+
[NamespaceProgramMessage.bool_str,
295+
NamespaceProgramMessage.int_bool],
226296
venv_dir=venv_dir,
227297
)
228298

0 commit comments

Comments
 (0)