From 98c0f33ab6c5c394a3596b9f70fd01ab22f7956c Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Wed, 27 Sep 2017 16:03:44 -0700 Subject: [PATCH] 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 #3895, as it removes the subprocesses. Fixes #1671 --- mypy/test/testpythoneval.py | 55 ++------ test-data/unit/python2eval.test | 44 +------ test-data/unit/pythoneval-asyncio.test | 40 +----- test-data/unit/pythoneval.test | 171 +------------------------ 4 files changed, 12 insertions(+), 298 deletions(-) diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 635b99cbc0ff..1a060a35e3c3 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -10,12 +10,9 @@ this suite would slow down the main suite too much. """ -from contextlib import contextmanager -import errno import os import os.path import re -import subprocess import sys import pytest # type: ignore # no pytest in typeshed @@ -25,6 +22,7 @@ from mypy.test.data import DataDrivenTestCase, parse_test_cases, DataSuite from mypy.test.helpers import assert_string_arrays_equal from mypy.util import try_find_python2_interpreter +from mypy.api import run # Files which contain test case descriptions. python_eval_files = ['pythoneval.test', @@ -61,73 +59,38 @@ def test_python_evaluation(testcase: DataDrivenTestCase) -> None: version. """ assert testcase.old_cwd is not None, "test was not properly set up" - mypy_cmdline = [ - python3_path, - os.path.join(testcase.old_cwd, 'scripts', 'mypy'), - '--show-traceback', - ] + mypy_cmdline = ['--show-traceback'] py2 = testcase.name.lower().endswith('python2') if py2: mypy_cmdline.append('--py2') - interpreter = try_find_python2_interpreter() - if interpreter is None: - # Skip, can't find a Python 2 interpreter. - pytest.skip() - # placate the type checker - return - else: - interpreter = python3_path - # Write the program to a file. program = '_' + testcase.name + '.py' - mypy_cmdline.append(program) program_path = os.path.join(test_temp_dir, program) + mypy_cmdline.append(program_path) with open(program_path, 'w') as file: for s in testcase.input: file.write('{}\n'.format(s)) # Type check the program. # This uses the same PYTHONPATH as the current process. - returncode, out = run(mypy_cmdline) - if returncode == 0: - # Execute the program. - returncode, interp_out = run([interpreter, program]) - out += interp_out + out, err, returncode = run(mypy_cmdline) + output = split_lines(out, err) # Remove temp file. os.remove(program_path) - assert_string_arrays_equal(adapt_output(testcase), out, + assert_string_arrays_equal(adapt_output(testcase), output, 'Invalid output ({}, line {})'.format( testcase.file, testcase.line)) -def split_lines(*streams: bytes) -> List[str]: +def split_lines(*streams: str) -> List[str]: """Returns a single list of string lines from the byte streams in args.""" return [ s.rstrip('\n\r') for stream in streams - for s in str(stream, 'utf8').splitlines() + for s in stream.splitlines() ] def adapt_output(testcase: DataDrivenTestCase) -> List[str]: """Translates the generic _program.py into the actual filename.""" program = '_' + testcase.name + '.py' - return [program_re.sub(program, line) for line in testcase.output] - - -def run( - cmdline: List[str], *, env: Optional[Dict[str, str]] = None, timeout: int = 30 -) -> Tuple[int, List[str]]: - """A poor man's subprocess.run() for 3.3 and 3.4 compatibility.""" - process = subprocess.Popen( - cmdline, - env=env, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - cwd=test_temp_dir, - ) - try: - out, err = process.communicate(timeout=timeout) - except subprocess.TimeoutExpired: - out = err = b'' - process.kill() - return process.returncode, split_lines(out, err) + return [test_temp_dir + os.sep + program_re.sub(program, line) for line in testcase.output] diff --git a/test-data/unit/python2eval.test b/test-data/unit/python2eval.test index b750de8b12a1..54b471fbff89 100644 --- a/test-data/unit/python2eval.test +++ b/test-data/unit/python2eval.test @@ -1,5 +1,4 @@ --- Test cases for type checking mypy programs using full stubs and running --- using CPython (Python 2 mode). +-- Test cases for type checking mypy programs using full stubs -- -- These are mostly regression tests -- no attempt is made to make these -- complete. @@ -22,8 +21,6 @@ print x x = u'foo' print repr(x) [out] -xyz -u'foo' [case testXrangeAndRange_python2] for i in xrange(2): @@ -31,18 +28,12 @@ for i in xrange(2): for i in range(3): print i [out] -0 -1 -0 -1 -2 [case testIterator_python2] import typing, sys x = iter('bar') print x.next(), x.next() [out] -b a [case testEncodeAndDecode_python2] print 'a'.encode('latin1') @@ -50,18 +41,12 @@ print 'b'.decode('latin1') print u'c'.encode('latin1') print u'd'.decode('latin1') [out] -a -b -c -d [case testHasKey_python2] d = {1: 'x'} print d.has_key(1) print d.has_key(2) [out] -True -False [case testIntegerDivision_python2] x = 1 / 2 @@ -86,8 +71,6 @@ def f(x): # type: (AnyStr) -> AnyStr print f('') print f(u'') [out] -foo -zar [case testGenericPatterns_python2] from typing import Pattern @@ -98,7 +81,6 @@ b = None # type: Pattern[str] b = re.compile('foo*') print(p.match(u'fooo').group(0)) [out] -fooo [case testGenericMatch_python2] from typing import Match @@ -107,26 +89,22 @@ def f(m): # type: (Match[str]) -> None print(m.group(0)) f(re.match('x*', 'xxy')) [out] -xx [case testVariableLengthTuple_python2] from typing import Tuple, cast x = cast(Tuple[int, ...], ()) print(x) [out] -() [case testFromFuturePrintFunction_python2] from __future__ import print_function print('a', 'b') [out] -a b [case testFromFutureImportUnicodeLiterals_python2] from __future__ import unicode_literals print '>', ['a', b'b', u'c'] [out] -> [u'a', 'b', u'c'] [case testUnicodeLiteralsKwargs_python2] from __future__ import unicode_literals @@ -182,7 +160,6 @@ def f(a): # type: (Sequence[T]) -> None print a f(tuple()) [out] -() [case testReadOnlyProperty_python2] import typing @@ -192,7 +169,6 @@ class A: return 1 print(A().foo + 2) [out] -3 [case testIOTypes_python2] 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. f.close() print('ok') [out] -ok [case testStringIO_python2] import typing @@ -228,7 +203,6 @@ c = io.StringIO() c.write(u'\x89') print(repr(c.getvalue())) [out] -u'\x89' [case testBytesIO_python2] import typing @@ -237,7 +211,6 @@ c = io.BytesIO() c.write('\x89') print(repr(c.getvalue())) [out] -'\x89' [case testTextIOWrapper_python2] import typing @@ -246,7 +219,6 @@ b = io.BytesIO(u'\xab'.encode('utf8')) w = io.TextIOWrapper(b, encoding='utf8') print(repr(w.read())) [out] -u'\xab' [case testIoOpen_python2] import typing @@ -257,7 +229,6 @@ if 1 == 2: # Only type check, do not execute f.close() print 'ok' [out] -ok [case testUnionType_python2] from typing import Union @@ -269,8 +240,6 @@ def f(x): # type: (Union[int, str]) -> str print f(12) print f('ab') [out] -12 -ab [case testStrAdd_python2] import typing @@ -301,7 +270,6 @@ X = namedtuple('X', ['a', 'b']) x = X(a=1, b='s') print x.a, x.b [out] -1 s [case testNamedTupleError_python2] import typing @@ -328,9 +296,6 @@ print 5 + 8j print 3j * 2.0 print 4j / 2.0 [out] -(5+8j) -6j -2j [case testNamedTupleWithTypes_python2] from typing import NamedTuple @@ -341,9 +306,6 @@ a, b = n print a, b print n[0] [out] -N(a=1, b='x') -1 x -1 [case testUnionTypeAlias_python2] from typing import Union @@ -363,7 +325,6 @@ class A(object): __metaclass__ = MyType print(type(A()).__name__) [out] -Ax [case testSequenceIndexAndCount_python2] from typing import Sequence @@ -372,8 +333,6 @@ def f(x): # type: (Sequence[int]) -> None print(x.count(1)) f([0, 0, 1, 1, 1]) [out] -2 -3 [case testOptional_python2] from typing import Optional @@ -419,7 +378,6 @@ class B(A): b = B() print b.x + 1 [out] -4 [case testReModuleBytesPython2] # Regression tests for various overloads in the re module -- bytes version diff --git a/test-data/unit/pythoneval-asyncio.test b/test-data/unit/pythoneval-asyncio.test index b7f78beb5201..cd553fcc234b 100644 --- a/test-data/unit/pythoneval-asyncio.test +++ b/test-data/unit/pythoneval-asyncio.test @@ -1,5 +1,4 @@ --- Test cases for type checking mypy programs using full stubs and running --- using CPython. +-- Test cases for type checking mypy programs using full stubs -- -- These are mostly regression tests -- no attempt is made to make these -- complete. @@ -10,7 +9,6 @@ import asyncio print('Imported') [out] -Imported [case testSimpleCoroutineSleep] from typing import Any, Generator @@ -32,16 +30,6 @@ try: finally: loop.close() [out] -Prev 0 -After 0 -Prev 1 -After 1 -Prev 2 -After 2 -Prev 3 -After 3 -Prev 4 -After 4 [case testCoroutineCallingOtherCoroutine] from typing import Generator, Any @@ -63,8 +51,6 @@ loop = asyncio.get_event_loop() loop.run_until_complete(print_sum(1, 2)) loop.close() [out] -Compute 1 + 2 ... -1 + 2 = 3 [case testCoroutineChangingFuture] from typing import Generator, Any @@ -83,7 +69,6 @@ loop.run_until_complete(future) print(future.result()) loop.close() [out] -Future is done! [case testFunctionAssignedAsCallback] import typing @@ -109,7 +94,6 @@ try: finally: loop.close() [out] -Callback works! [case testMultipleTasks] import typing @@ -133,16 +117,6 @@ tasks = [ loop.run_until_complete(asyncio.wait(tasks)) loop.close() [out] -Task A: Compute factorial(2)... -Task B: Compute factorial(2)... -Task C: Compute factorial(2)... -Task A: factorial(2) = 2 -Task B: Compute factorial(3)... -Task C: Compute factorial(3)... -Task B: factorial(3) = 6 -Task C: Compute factorial(4)... -Task C: factorial(4) = 24 - [case testConcatenatedCoroutines] import typing @@ -179,10 +153,6 @@ loop.run_until_complete(h()) print("Outside %s" % future.result()) loop.close() [out] -h3: 42 -h2: 42 -h: 42 -Outside 42 [case testConcatenatedCoroutinesReturningFutures] import typing @@ -222,11 +192,6 @@ loop = asyncio.get_event_loop() loop.run_until_complete(h()) loop.close() [out] -Before -42 -Future -Future> - [case testCoroutineWithOwnClass] import typing @@ -250,9 +215,6 @@ loop.run_until_complete(h()) print("Outside %s" % future.result().x) loop.close() [out] -h: 42 -Outside 42 - -- Errors diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 80bbaeeb0c94..fda9e5dfc16d 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1,5 +1,4 @@ --- Test cases for type checking mypy programs using full stubs and running --- using CPython. +-- Test cases for type checking mypy programs using full stubs -- -- These are mostly regression tests -- no attempt is made to make these -- complete. @@ -9,7 +8,6 @@ import typing print('hello, world') [out] -hello, world -- Skipped because different typing package versions have different repr()s. [case testAbstractBaseClasses-skip] @@ -33,13 +31,6 @@ def f(): f() [out] -'x' True -[1] typing.Sequence True -{1: 3} typing.Sequence False - typing.Iterator True -'x' typing.Iterable True -{} typing.Mapping True -{1} typing.AbstractSet True [case testSized] from typing import Sized @@ -47,7 +38,6 @@ class A(Sized): def __len__(self): return 5 print(len(A())) [out] -5 [case testReversed] from typing import Reversible @@ -59,11 +49,6 @@ print(list(reversed([1,2,3]))) print(list(reversed('abc'))) print(list(reversed(A()))) [out] --- Duplicate [ at line beginning. -[[4, 3, 2, 1, 0] -[[3, 2, 1] -[['c', 'b', 'a'] -[['f', 'o', 'o'] [case testIntAndFloatConversion] from typing import SupportsInt, SupportsFloat @@ -79,13 +64,6 @@ print(int(A())) print(float(-9)) print(float(B())) [out] -1 -6 -3 -4 -5 --9.0 -1.2 [case testAbs] from typing import SupportsAbs @@ -96,9 +74,6 @@ print(abs(-1)) print(abs(-1.2)) print(abs(A())) [out] -1 -1.2 -5.5 [case testAbs2] @@ -120,15 +95,11 @@ print(round(1.6)) print(round(A())) print(round(A(), 2)) [out] -2 -x0 -x2 [case testCallMethodViaTypeObject] import typing print(list.__add__([1, 2], [3, 4])) [out] -[[1, 2, 3, 4] [case testClassDataAttribute] import typing @@ -138,8 +109,6 @@ print(A.x) A.x += 1 print(A.x) [out] -0 -1 [case testInheritedClassAttribute] import typing @@ -151,8 +120,6 @@ class B(A): B.f(None) print(B.x) [out] -f -1 [case testFunctionDecorator] from typing import TypeVar, cast @@ -173,14 +140,6 @@ def foo(s: str) -> str: print(foo('y')) print(foo('x')) [out] -enter foo -foo y -exit foo -y! -enter foo -foo x -exit foo -x! [case testModuleAttributes] import math @@ -190,10 +149,6 @@ print(type(math.__dict__)) print(type(math.__doc__ or '')) print(math.__class__) [out] -math - - - [case testSpecialAttributes] import typing @@ -201,8 +156,6 @@ class A: pass print(object().__doc__) print(A().__class__) [out] -The most base type - [case testFunctionAttributes] import typing @@ -211,9 +164,6 @@ print(type(ord.__doc__ + '')) print(ord.__name__) print(ord.__module__) [out] - -ord -builtins [case testTypeAttributes] import typing @@ -223,11 +173,6 @@ print(str.__name__) print(str.__module__) print(str.__dict__ is not None) [out] - - -str -builtins -True [case testBoolCompatibilityWithInt] import typing @@ -236,8 +181,6 @@ x = True print(bool('x')) print(bool('')) [out] -True -False [case testCallBuiltinTypeObjectsWithoutArguments] import typing @@ -247,11 +190,6 @@ print(repr(bytes())) print(float()) print(bool()) [out] -0 -'' -b'' -0.0 -False [case testIntegerDivision] import typing @@ -267,8 +205,6 @@ class A: print(A.f('12')) print(A().f('34')) [out] -12 -34 [case testClassmethod] import typing @@ -278,8 +214,6 @@ class A: print(A.f('12')) print(A().f('34')) [out] -12 -34 [case testIntMethods] import typing @@ -289,10 +223,6 @@ print(n.from_bytes(b'ac', 'big')) print(n.from_bytes([2, 3], 'big')) print(n.to_bytes(2, 'big')) [out] -24930 -24931 -515 -b'\x00\x00' [case testFloatMethods] import typing @@ -301,10 +231,6 @@ print(1.5.hex()) print(2.0.is_integer()) print(float.fromhex('0x1.8')) [out] -(3, 2) -0x1.8000000000000p+0 -True -1.5 [case testArray] import typing @@ -328,7 +254,6 @@ class A: return self.x + 1 print(A().f) [out] -3 [case testIsinstanceWithTuple] from typing import cast, Any @@ -336,7 +261,6 @@ x = cast(Any, (1, 'x')) if isinstance(x, tuple): print(x[0], x[1]) [out] -1 x [case testTypevarValues] from typing import TypeVar @@ -349,8 +273,6 @@ def f(x: T) -> T: print(f('')) print(f(b'')) [out] -foo -b'bar' [case testAnyStr] from typing import AnyStr @@ -362,8 +284,6 @@ def f(x: AnyStr) -> AnyStr: print(f('')) print(f(b'')) [out] -foo -b'zar' [case testNameNotImportedFromTyping] import typing @@ -436,7 +356,6 @@ b = None # type: Pattern[bytes] b = re.compile(b'foo*') print(p.match('fooo').group(0)) [out] -fooo [case testGenericMatch] from typing import Match @@ -445,7 +364,6 @@ def f(m: Match[bytes]) -> None: print(m.group(0)) f(re.match(b'x*', b'xxy')) [out] -b'xx' [case testMultipleTypevarsWithValues] from typing import TypeVar @@ -463,7 +381,6 @@ _program.py:7: error: Unsupported operand types for + ("str" and "int") import typing print(SystemExit(5).code) [out] -5 [case testIntFloatDucktyping] @@ -480,8 +397,6 @@ import typing print(1.5 + 1.5) print(1.5 + 1) [out] -3.0 -2.5 [case testMathFunctionWithIntArgument] import typing @@ -516,8 +431,6 @@ import typing print(2 / 0.5) print(' ', 2 * [3, 4]) [out] -4.0 - [3, 4, 3, 4] [case testNotImplemented] import typing @@ -532,8 +445,6 @@ class B: print(A() + 1) print(A() + B()) [out] -2 -x [case testMappingMethods] # Regression test @@ -542,8 +453,6 @@ x = {'x': 'y'} # type: Mapping[str, str] print('x' in x) print('y' in x) [out] -True -False [case testOverlappingOperatorMethods] @@ -567,8 +476,6 @@ import typing print(b'ab' < bytearray(b'b')) print(bytearray(b'ab') < b'a') [out] -True -False [case testBytesAndBytearrayComparisons2] import typing @@ -588,8 +495,6 @@ a = [1] print('', a.__iadd__([2])) print('', a) [out] - [1, 2] - [1, 2] [case testListInplaceAdd] import typing @@ -597,7 +502,6 @@ a = [1] a += iter([2, 3]) print(tuple(a)) [out] -(1, 2, 3) [case testListConcatenateWithIterable] import typing @@ -612,11 +516,6 @@ a = [['x', 'x'], 'fo', s, iter('foo'), {'aa'}] for i, x in enumerate(a): print(i, next(iter(x))) [out] -0 x -1 f -2 x -3 f -4 aa [case testTextIOProperties] import typing @@ -627,8 +526,6 @@ sys.stdin.line_buffering sys.stdin.buffer sys.stdin.newlines [out] - - [case testIOProperties] import typing @@ -636,27 +533,22 @@ import sys print(sys.stdin.name) print(sys.stdin.buffer.mode) [out] - -rb [case testSetUnion] import typing s = {'x', 'y'} print('>', sorted(s.union('foo'))) [out] -> ['f', 'o', 'x', 'y'] [case testFromFuturePrintFunction] from __future__ import print_function print('a', 'b') [out] -a b [case testLenOfTuple] import typing print(len((1, 'x'))) [out] -2 [case testListMethods] import typing @@ -699,15 +591,6 @@ m.sort(reverse=False) m.sort(key=lambda x: -x, reverse=True) print('>', m) [out] -> [0] -> [0, 1, 2, 3, 4] -0 -0 -0 -> [1, 0] -1 -0 -> [1, 2, 3, 4] [case testListOperators] import typing @@ -734,24 +617,6 @@ l[:3] = [] print('setslice', l) print('reversed', list(reversed(l))) [out] -+ [0, 1, 2] -* [0, 1, 0, 1] -* [0, 1, 0, 1] -in True -== False -!= True -> False ->= False -< True -<= True ->[0] 0 -+= [0, 1, 2] -*= [0, 1, 2, 0, 1, 2] -iter [0, 1, 2, 0, 1, 2] -len 6 -repr [0, 1, 2, 0, 1, 2] -setslice [0, 1, 2] -reversed [2, 1, 0] [case testTupleAsSubtypeOfSequence] from typing import TypeVar, Sequence @@ -759,7 +624,6 @@ T = TypeVar('T') def f(a: Sequence[T]) -> None: print(a) f(tuple()) [out] -() [case testMapWithLambdaSpecialCase-skip] # TODO: Fix this; this was broken at some point but not sure why. @@ -768,7 +632,6 @@ a = [[1], [3]] b = map(lambda y: y[0], a) print('>', list(b)) [out] -> [1, 3] [case testInternalBuiltinDefinition] import typing @@ -801,7 +664,6 @@ X = namedtuple('X', ['a', 'b']) x = X(a=1, b='s') print(x.a, x.b) [out] -1 s [case testNamedTupleShortSyntax] import typing @@ -810,7 +672,6 @@ X = namedtuple('X', ' a b ') x = X(a=1, b='s') print(x.a, x.b) [out] -1 s [case testNamedTupleError] import typing @@ -833,10 +694,6 @@ print(x.index(1)) print(x.count(1)) print(x + x) [out] -2 -0 -1 -(1, 's', 1, 's') [case testNamedTupleWithTypes] from typing import NamedTuple @@ -847,9 +704,6 @@ a, b = n print(a, b) print(n[0]) [out] -N(a=1, b='x') -1 x -1 [case testRelativeImport] import typing @@ -860,7 +714,6 @@ from .n import x [file m/n.py] x = 1 [out] -1 [case testRelativeImport2] import typing @@ -872,7 +725,6 @@ from .nn import x [file m/nn.py] x = 2 [out] -2 [case testPyiTakesPrecedenceOverPy] import m @@ -902,9 +754,6 @@ print(5 + 8j) print(3j * 2.0) print(4J / 2.0) [out] -(5+8j) -6j -2j [case testComplexArithmetic2] import typing @@ -949,7 +798,6 @@ class MyType(type): class A(metaclass=MyType): pass print(type(A()).__name__) [out] -Ax [case testSequenceIndexAndCount] from typing import Sequence @@ -958,15 +806,11 @@ def f(x: Sequence[int]) -> None: print(x.count(1)) f([0, 0, 1, 1, 1]) [out] -2 -3 [case testEscapeInTripleQuotedStrLiteral] print('''\'''') print(r"""\"""$""") [out] -' -\"""$ [case testSubclassBothGenericAndNonGenericABC] from typing import Generic, TypeVar @@ -1001,7 +845,6 @@ print('a') exit(2) print('b') [out] -a [case testTypeVariableTypeComparability] from typing import TypeVar @@ -1014,8 +857,6 @@ def eq(x: T, y: T, z: T) -> T: print(eq(1, 2, 3)) print(eq('x', 'x', 'z')) [out] -3 -x [case testIntDecimalCompatibility] import typing @@ -1026,11 +867,6 @@ print(1 + Decimal('2.34')) print(1 - Decimal('2.34')) print(2 * Decimal('2.34')) [out] -3 --1 -3.34 --1.34 -4.68 [case testInstantiateBuiltinTypes] from typing import Dict, Set, List @@ -1053,9 +889,6 @@ def p(t: Tuple[int, ...]) -> None: print(n) p((1, 3, 2)) [out] -1 -3 -2 [case testVariableLengthTupleError] from typing import Tuple @@ -1223,7 +1056,6 @@ class B(metaclass=A): print(getattr(B(), 'x')) [out] -7 [case testSortedNoError] from typing import Iterable, Callable, TypeVar, List, Dict @@ -1244,7 +1076,6 @@ class B(A): b = B() print(b.x + 1) [out] -4 [case testInferenceWithLambda] from typing import TypeVar, Iterable, Iterator, List