Skip to content

Commit aa5c1e2

Browse files
committed
Fix bugs in test driver
1 parent 7f1a1d4 commit aa5c1e2

File tree

14 files changed

+604
-92
lines changed

14 files changed

+604
-92
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ python:
77

88
install: python setup.py install
99

10-
script: bash travis.sh
10+
script: python travis.py

mypy/build.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
DO_NOT_RUN = 'do-not-run' # Only type check, don't run the program
4141
VERBOSE = 'verbose' # More verbose messages (for troubleshooting)
4242
MODULE = 'module' # Build/run module as a script
43+
PROGRAM_TEXT = 'program-text' # Build/run command-line argument as a script
4344
TEST_BUILTINS = 'test-builtins' # Use stub builtins to speed up tests
4445

4546
# State ids. These describe the states a source file / module can be in a
@@ -84,6 +85,7 @@ def __init__(self, files: Dict[str, MypyFile],
8485
def build(program_path: str,
8586
target: int,
8687
module: str = None,
88+
argument: str = None,
8789
program_text: Union[str, bytes] = None,
8890
alt_lib_path: str = None,
8991
bin_dir: str = None,
@@ -152,9 +154,11 @@ def build(program_path: str,
152154
custom_typing_module=custom_typing_module,
153155
html_report_dir=html_report_dir)
154156

155-
program_path = program_path or lookup_program(module, lib_path)
156157
if program_text is None:
158+
program_path = program_path or lookup_program(module, lib_path)
157159
program_text = read_program(program_path)
160+
else:
161+
program_path = program_path or '<string>'
158162

159163
# Construct information that describes the initial file. __main__ is the
160164
# implicit module id and the import context is empty initially ([]).
@@ -447,7 +451,7 @@ def lookup_state(self, module: str) -> 'State':
447451
for state in self.states:
448452
if state.id == module:
449453
return state
450-
raise RuntimeError('%s not found' % str)
454+
raise RuntimeError('%s not found' % module)
451455

452456
def all_imported_modules_in_file(self,
453457
file: MypyFile) -> List[Tuple[str, int]]:
@@ -462,7 +466,7 @@ def correct_rel_imp(imp: Union[ImportFrom, ImportAll]) -> str:
462466
rel = imp.relative
463467
if rel == 0:
464468
return imp.id
465-
if os.path.basename(file.path) == '__init__.py':
469+
if os.path.basename(file.path).startswith('__init__.'):
466470
rel -= 1
467471
if rel != 0:
468472
file_id = ".".join(file_id.split(".")[:-rel])
@@ -505,7 +509,7 @@ def get_python_out_path(self, f: MypyFile) -> str:
505509
return os.path.join(self.output_dir, basename(f.path))
506510
else:
507511
components = f.fullname().split('.')
508-
if os.path.basename(f.path) == '__init__.py':
512+
if os.path.basename(f.path).startswith('__init__.'):
509513
components.append('__init__.py')
510514
else:
511515
components[-1] += '.py'

mypy/codec/pytokenize.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
from token import *
9393

9494
import token
95-
x = None
95+
x = None # type: str
9696
__all__ = [x for x in dir(token) if not x.startswith("_")]
9797
__all__ += ["COMMENT", "tokenize", "generate_tokens", "NL", "untokenize"]
9898
del x
@@ -187,7 +187,7 @@ def maybe(*choices): return group(*choices) + '?'
187187
'r': None, 'R': None, 'u': None, 'U': None,
188188
'b': None, 'B': None}
189189

190-
triple_quoted = {}
190+
triple_quoted = {} # type: Dict[str, str]
191191
for t in ("'''", '"""',
192192
"r'''", 'r"""', "R'''", 'R"""',
193193
"u'''", 'u"""', "U'''", 'U"""',
@@ -197,7 +197,7 @@ def maybe(*choices): return group(*choices) + '?'
197197
"br'''", 'br"""', "Br'''", 'Br"""',
198198
"bR'''", 'bR"""', "BR'''", 'BR"""'):
199199
triple_quoted[t] = t
200-
single_quoted = {}
200+
single_quoted = {} # type: Dict[str, str]
201201
for t in ("'", '"',
202202
"r'", 'r"', "R'", 'R"',
203203
"u'", 'u"', "U'", 'U"',
@@ -333,6 +333,10 @@ def generate_tokens(readline):
333333
contline = None
334334
indents = [0]
335335

336+
if 0:
337+
# type hints for mypy
338+
strstart = (0, 0)
339+
endprog = re.compile('')
336340
while 1: # loop over lines in stream
337341
try:
338342
line = readline()

mypy/main.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ def __init__(self) -> None:
2929

3030
def main() -> None:
3131
bin_dir = find_bin_directory()
32-
path, module, options = process_options(sys.argv[1:])
32+
path, module, program_text, options = process_options(sys.argv[1:])
3333
try:
3434
if options.target == build.TYPE_CHECK:
35-
type_check_only(path, module, bin_dir, options)
35+
type_check_only(path, module, program_text, bin_dir, options)
3636
else:
3737
raise RuntimeError('unsupported target %d' % options.target)
3838
except CompileError as e:
@@ -66,10 +66,11 @@ def readlinkabs(link: str) -> str:
6666
return os.path.join(os.path.dirname(link), path)
6767

6868

69-
def type_check_only(path: str, module: str, bin_dir: str, options: Options) -> None:
69+
def type_check_only(path: str, module: str, program_text: str, bin_dir: str, options: Options) -> None:
7070
# Type check the program and dependencies and translate to Python.
7171
build.build(path,
7272
module=module,
73+
program_text=program_text,
7374
bin_dir=bin_dir,
7475
target=build.TYPE_CHECK,
7576
pyversion=options.pyversion,
@@ -79,7 +80,7 @@ def type_check_only(path: str, module: str, bin_dir: str, options: Options) -> N
7980
python_path=options.python_path)
8081

8182

82-
def process_options(args: List[str]) -> Tuple[str, str, Options]:
83+
def process_options(args: List[str]) -> Tuple[str, str, str, Options]:
8384
"""Process command line arguments.
8485
8586
Return (mypy program path (or None),
@@ -99,7 +100,10 @@ def process_options(args: List[str]) -> Tuple[str, str, Options]:
99100
args = args[1:]
100101
elif args[0] == '-m' and args[1:]:
101102
options.build_flags.append(build.MODULE)
102-
return None, args[1], options
103+
return None, args[1], None, options
104+
elif args[0] == '-c' and args[1:]:
105+
options.build_flags.append(build.PROGRAM_TEXT)
106+
return None, None, args[1], options
103107
elif args[0] in ('-h', '--help'):
104108
help = True
105109
args = args[1:]
@@ -141,7 +145,7 @@ def process_options(args: List[str]) -> Tuple[str, str, Options]:
141145
usage('--py2 specified, '
142146
'but --use-python-path will search in sys.path of Python 3')
143147

144-
return args[0], None, options
148+
return args[0], None, None, options
145149

146150

147151
def usage(msg: str = None) -> None:
@@ -159,6 +163,7 @@ def usage(msg: str = None) -> None:
159163
-h, --help print this help message and exit
160164
--html-report dir generate a HTML report of type precision under dir/
161165
-m mod type check module
166+
-c string type check string
162167
--verbose more verbose messages
163168
--use-python-path search for modules in sys.path of running Python
164169
--version show the current version information

mypy/nodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def accept(self, visitor: NodeVisitor[T]) -> T:
159159

160160
def is_package_init_file(self) -> bool:
161161
return not (self.path is None) and len(self.path) != 0 \
162-
and os.path.basename(self.path) == '__init__.py'
162+
and os.path.basename(self.path).startswith('__init__.')
163163

164164
class ImportBase(Node):
165165
"""Base class for all import statements."""

mypy/waiter.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
from typing import Dict, List, Optional, Tuple
2+
3+
import os
4+
from subprocess import Popen
5+
import sys
6+
7+
8+
class WaiterError(Exception):
9+
pass
10+
11+
class LazySubprocess:
12+
13+
def __init__(self, name: str, args: List[str], *, cwd: Optional[str] = None, env: Optional[Dict[str, str]] = None) -> None:
14+
self.name = name
15+
self.args = args
16+
self.cwd = cwd
17+
self.env = env
18+
19+
def __call__(self) -> Popen:
20+
return Popen(self.args, cwd=self.cwd, env=self.env)
21+
22+
23+
class Waiter:
24+
"""Run subprocesses in parallel and wait for them.
25+
26+
Usage:
27+
28+
waiter = Waiter()
29+
waiter.add('sleep 9')
30+
waiter.add('sleep 10')
31+
if not waiter.run():
32+
print('error')
33+
"""
34+
def __init__(self, limit: int = 0) -> None:
35+
self.queue = [] # type: List[LazySubprocess]
36+
self.next = 0
37+
self.current = {} # type: Dict[int, Tuple[int, Popen]]
38+
if limit == 0:
39+
try:
40+
sched_getaffinity = os.sched_getaffinity
41+
except AttributeError:
42+
limit = 2
43+
else:
44+
# Note: only count CPUs we are allowed to use. It is a
45+
# major mistake to count *all* CPUs on the machine.
46+
limit = len(sched_getaffinity(0))
47+
try:
48+
os.waitid
49+
os.P_ALL
50+
os.WEXITED
51+
os.WNOWAIT
52+
except AttributeError:
53+
limit = 1
54+
# Temporarily force until test parallelization bugs are fixed.
55+
limit = 1
56+
self.limit = limit
57+
assert limit > 0
58+
print('%-8s %d' % ('PARALLEL', limit))
59+
60+
def add(self, cmd: LazySubprocess) -> int:
61+
rv = len(self.queue)
62+
self.queue.append(cmd)
63+
return rv
64+
65+
def _start1(self) -> None:
66+
cmd = self.queue[self.next]
67+
name = cmd.name
68+
proc = cmd()
69+
num = self.next
70+
self.current[proc.pid] = (num, proc)
71+
print('%-8s #%d %s' % ('START', num, name))
72+
self.next += 1
73+
74+
def _wait1(self) -> List[str]:
75+
if self.limit > 1:
76+
# WNOWAIT is not to be confused with WNOHANG
77+
pid = os.waitid(os.P_ALL, -1, os.WEXITED | os.WNOWAIT).si_pid
78+
num, proc = self.current.pop(pid)
79+
else:
80+
pid, (num, proc) = self.current.popitem()
81+
name = self.queue[num].name
82+
rc = proc.wait()
83+
if rc >= 0:
84+
msg = 'EXIT %d' % rc
85+
else:
86+
msg = 'SIG %d' % -rc
87+
print('%-8s #%d %s' % (msg, num, name))
88+
if rc != 0:
89+
return [name]
90+
else:
91+
return []
92+
93+
def run(self) -> None:
94+
print('SUMMARY %d tasks selected' % len(self.queue))
95+
failures = [] # type: List[str]
96+
while self.current or self.next < len(self.queue):
97+
while len(self.current) < self.limit and self.next < len(self.queue):
98+
self._start1()
99+
failures += self._wait1()
100+
if failures:
101+
print('SUMMARY %d/%d tasks failed' % (len(failures), len(self.queue)))
102+
for f in failures:
103+
print(' FAILURE %s' % f)
104+
print('SUMMARY %d/%d tasks failed' % (len(failures), len(self.queue)))
105+
sys.exit(1)
106+
else:
107+
print('SUMMARY all %d tasks passed' % len(self.queue))

samples/codec/example.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
# very simple script to test type annotations
44

5+
from typing import Tuple
56

6-
def f(x: int, y: str='abc') -> [int,
7+
def f(x: int, y: str='abc') -> Tuple[int,
78
str]:
89
return x, y
910

0 commit comments

Comments
 (0)