Skip to content

Commit 98c0f33

Browse files
committed
Move pythoneval to use mypy.api.run
This replaces the old subprocess based method. In addition, the test cases are no longer run, in order to reduce test time. This has led to a 10-20% speedup in pytest. This might help with python#3895, as it removes the subprocesses. Fixes python#1671
1 parent 9b58e78 commit 98c0f33

File tree

4 files changed

+12
-298
lines changed

4 files changed

+12
-298
lines changed

mypy/test/testpythoneval.py

Lines changed: 9 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,9 @@
1010
this suite would slow down the main suite too much.
1111
"""
1212

13-
from contextlib import contextmanager
14-
import errno
1513
import os
1614
import os.path
1715
import re
18-
import subprocess
1916
import sys
2017

2118
import pytest # type: ignore # no pytest in typeshed
@@ -25,6 +22,7 @@
2522
from mypy.test.data import DataDrivenTestCase, parse_test_cases, DataSuite
2623
from mypy.test.helpers import assert_string_arrays_equal
2724
from mypy.util import try_find_python2_interpreter
25+
from mypy.api import run
2826

2927
# Files which contain test case descriptions.
3028
python_eval_files = ['pythoneval.test',
@@ -61,73 +59,38 @@ def test_python_evaluation(testcase: DataDrivenTestCase) -> None:
6159
version.
6260
"""
6361
assert testcase.old_cwd is not None, "test was not properly set up"
64-
mypy_cmdline = [
65-
python3_path,
66-
os.path.join(testcase.old_cwd, 'scripts', 'mypy'),
67-
'--show-traceback',
68-
]
62+
mypy_cmdline = ['--show-traceback']
6963
py2 = testcase.name.lower().endswith('python2')
7064
if py2:
7165
mypy_cmdline.append('--py2')
72-
interpreter = try_find_python2_interpreter()
73-
if interpreter is None:
74-
# Skip, can't find a Python 2 interpreter.
75-
pytest.skip()
76-
# placate the type checker
77-
return
78-
else:
79-
interpreter = python3_path
80-
8166
# Write the program to a file.
8267
program = '_' + testcase.name + '.py'
83-
mypy_cmdline.append(program)
8468
program_path = os.path.join(test_temp_dir, program)
69+
mypy_cmdline.append(program_path)
8570
with open(program_path, 'w') as file:
8671
for s in testcase.input:
8772
file.write('{}\n'.format(s))
8873
# Type check the program.
8974
# This uses the same PYTHONPATH as the current process.
90-
returncode, out = run(mypy_cmdline)
91-
if returncode == 0:
92-
# Execute the program.
93-
returncode, interp_out = run([interpreter, program])
94-
out += interp_out
75+
out, err, returncode = run(mypy_cmdline)
76+
output = split_lines(out, err)
9577
# Remove temp file.
9678
os.remove(program_path)
97-
assert_string_arrays_equal(adapt_output(testcase), out,
79+
assert_string_arrays_equal(adapt_output(testcase), output,
9880
'Invalid output ({}, line {})'.format(
9981
testcase.file, testcase.line))
10082

10183

102-
def split_lines(*streams: bytes) -> List[str]:
84+
def split_lines(*streams: str) -> List[str]:
10385
"""Returns a single list of string lines from the byte streams in args."""
10486
return [
10587
s.rstrip('\n\r')
10688
for stream in streams
107-
for s in str(stream, 'utf8').splitlines()
89+
for s in stream.splitlines()
10890
]
10991

11092

11193
def adapt_output(testcase: DataDrivenTestCase) -> List[str]:
11294
"""Translates the generic _program.py into the actual filename."""
11395
program = '_' + testcase.name + '.py'
114-
return [program_re.sub(program, line) for line in testcase.output]
115-
116-
117-
def run(
118-
cmdline: List[str], *, env: Optional[Dict[str, str]] = None, timeout: int = 30
119-
) -> Tuple[int, List[str]]:
120-
"""A poor man's subprocess.run() for 3.3 and 3.4 compatibility."""
121-
process = subprocess.Popen(
122-
cmdline,
123-
env=env,
124-
stdout=subprocess.PIPE,
125-
stderr=subprocess.PIPE,
126-
cwd=test_temp_dir,
127-
)
128-
try:
129-
out, err = process.communicate(timeout=timeout)
130-
except subprocess.TimeoutExpired:
131-
out = err = b''
132-
process.kill()
133-
return process.returncode, split_lines(out, err)
96+
return [test_temp_dir + os.sep + program_re.sub(program, line) for line in testcase.output]

test-data/unit/python2eval.test

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
-- Test cases for type checking mypy programs using full stubs and running
2-
-- using CPython (Python 2 mode).
1+
-- Test cases for type checking mypy programs using full stubs
32
--
43
-- These are mostly regression tests -- no attempt is made to make these
54
-- complete.
@@ -22,46 +21,32 @@ print x
2221
x = u'foo'
2322
print repr(x)
2423
[out]
25-
xyz
26-
u'foo'
2724

2825
[case testXrangeAndRange_python2]
2926
for i in xrange(2):
3027
print i
3128
for i in range(3):
3229
print i
3330
[out]
34-
0
35-
1
36-
0
37-
1
38-
2
3931

4032
[case testIterator_python2]
4133
import typing, sys
4234
x = iter('bar')
4335
print x.next(), x.next()
4436
[out]
45-
b a
4637

4738
[case testEncodeAndDecode_python2]
4839
print 'a'.encode('latin1')
4940
print 'b'.decode('latin1')
5041
print u'c'.encode('latin1')
5142
print u'd'.decode('latin1')
5243
[out]
53-
a
54-
b
55-
c
56-
d
5744

5845
[case testHasKey_python2]
5946
d = {1: 'x'}
6047
print d.has_key(1)
6148
print d.has_key(2)
6249
[out]
63-
True
64-
False
6550

6651
[case testIntegerDivision_python2]
6752
x = 1 / 2
@@ -86,8 +71,6 @@ def f(x): # type: (AnyStr) -> AnyStr
8671
print f('')
8772
print f(u'')
8873
[out]
89-
foo
90-
zar
9174

9275
[case testGenericPatterns_python2]
9376
from typing import Pattern
@@ -98,7 +81,6 @@ b = None # type: Pattern[str]
9881
b = re.compile('foo*')
9982
print(p.match(u'fooo').group(0))
10083
[out]
101-
fooo
10284

10385
[case testGenericMatch_python2]
10486
from typing import Match
@@ -107,26 +89,22 @@ def f(m): # type: (Match[str]) -> None
10789
print(m.group(0))
10890
f(re.match('x*', 'xxy'))
10991
[out]
110-
xx
11192

11293
[case testVariableLengthTuple_python2]
11394
from typing import Tuple, cast
11495
x = cast(Tuple[int, ...], ())
11596
print(x)
11697
[out]
117-
()
11898

11999
[case testFromFuturePrintFunction_python2]
120100
from __future__ import print_function
121101
print('a', 'b')
122102
[out]
123-
a b
124103

125104
[case testFromFutureImportUnicodeLiterals_python2]
126105
from __future__ import unicode_literals
127106
print '>', ['a', b'b', u'c']
128107
[out]
129-
> [u'a', 'b', u'c']
130108

131109
[case testUnicodeLiteralsKwargs_python2]
132110
from __future__ import unicode_literals
@@ -182,7 +160,6 @@ def f(a): # type: (Sequence[T]) -> None
182160
print a
183161
f(tuple())
184162
[out]
185-
()
186163

187164
[case testReadOnlyProperty_python2]
188165
import typing
@@ -192,7 +169,6 @@ class A:
192169
return 1
193170
print(A().foo + 2)
194171
[out]
195-
3
196172

197173
[case testIOTypes_python2]
198174
from typing import IO, TextIO, BinaryIO, Any
@@ -219,7 +195,6 @@ if 1 == 2: # Don't want to run the code below, since it would create a file.
219195
f.close()
220196
print('ok')
221197
[out]
222-
ok
223198

224199
[case testStringIO_python2]
225200
import typing
@@ -228,7 +203,6 @@ c = io.StringIO()
228203
c.write(u'\x89')
229204
print(repr(c.getvalue()))
230205
[out]
231-
u'\x89'
232206

233207
[case testBytesIO_python2]
234208
import typing
@@ -237,7 +211,6 @@ c = io.BytesIO()
237211
c.write('\x89')
238212
print(repr(c.getvalue()))
239213
[out]
240-
'\x89'
241214

242215
[case testTextIOWrapper_python2]
243216
import typing
@@ -246,7 +219,6 @@ b = io.BytesIO(u'\xab'.encode('utf8'))
246219
w = io.TextIOWrapper(b, encoding='utf8')
247220
print(repr(w.read()))
248221
[out]
249-
u'\xab'
250222

251223
[case testIoOpen_python2]
252224
import typing
@@ -257,7 +229,6 @@ if 1 == 2: # Only type check, do not execute
257229
f.close()
258230
print 'ok'
259231
[out]
260-
ok
261232

262233
[case testUnionType_python2]
263234
from typing import Union
@@ -269,8 +240,6 @@ def f(x): # type: (Union[int, str]) -> str
269240
print f(12)
270241
print f('ab')
271242
[out]
272-
12
273-
ab
274243

275244
[case testStrAdd_python2]
276245
import typing
@@ -301,7 +270,6 @@ X = namedtuple('X', ['a', 'b'])
301270
x = X(a=1, b='s')
302271
print x.a, x.b
303272
[out]
304-
1 s
305273

306274
[case testNamedTupleError_python2]
307275
import typing
@@ -328,9 +296,6 @@ print 5 + 8j
328296
print 3j * 2.0
329297
print 4j / 2.0
330298
[out]
331-
(5+8j)
332-
6j
333-
2j
334299

335300
[case testNamedTupleWithTypes_python2]
336301
from typing import NamedTuple
@@ -341,9 +306,6 @@ a, b = n
341306
print a, b
342307
print n[0]
343308
[out]
344-
N(a=1, b='x')
345-
1 x
346-
1
347309

348310
[case testUnionTypeAlias_python2]
349311
from typing import Union
@@ -363,7 +325,6 @@ class A(object):
363325
__metaclass__ = MyType
364326
print(type(A()).__name__)
365327
[out]
366-
Ax
367328

368329
[case testSequenceIndexAndCount_python2]
369330
from typing import Sequence
@@ -372,8 +333,6 @@ def f(x): # type: (Sequence[int]) -> None
372333
print(x.count(1))
373334
f([0, 0, 1, 1, 1])
374335
[out]
375-
2
376-
3
377336

378337
[case testOptional_python2]
379338
from typing import Optional
@@ -419,7 +378,6 @@ class B(A):
419378
b = B()
420379
print b.x + 1
421380
[out]
422-
4
423381

424382
[case testReModuleBytesPython2]
425383
# Regression tests for various overloads in the re module -- bytes version

0 commit comments

Comments
 (0)