Skip to content

Commit dbc130d

Browse files
committed
Fix bugs in test driver
1 parent e49d1cc commit dbc130d

File tree

17 files changed

+611
-100
lines changed

17 files changed

+611
-100
lines changed

mypy/build.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
DO_NOT_RUN = 'do-not-run' # Only type check, don't run the program
4242
VERBOSE = 'verbose' # More verbose messages (for troubleshooting)
4343
MODULE = 'module' # Build/run module as a script
44+
PROGRAM_TEXT = 'program-text' # Build/run command-line argument as a script
4445
TEST_BUILTINS = 'test-builtins' # Use stub builtins to speed up tests
4546
DUMP_TYPE_STATS = 'dump-type-stats'
4647
DUMP_INFER_STATS = 'dump-infer-stats'
@@ -87,6 +88,7 @@ def __init__(self, files: Dict[str, MypyFile],
8788
def build(program_path: str,
8889
target: int,
8990
module: str = None,
91+
argument: str = None,
9092
program_text: Union[str, bytes] = None,
9193
alt_lib_path: str = None,
9294
bin_dir: str = None,
@@ -145,9 +147,11 @@ def build(program_path: str,
145147
if alt_lib_path:
146148
lib_path.insert(0, alt_lib_path)
147149

148-
program_path = program_path or lookup_program(module, lib_path)
149150
if program_text is None:
151+
program_path = program_path or lookup_program(module, lib_path)
150152
program_text = read_program(program_path)
153+
else:
154+
program_path = program_path or '<string>'
151155

152156
reports = Reports(program_path, data_dir, report_dirs)
153157

@@ -173,6 +177,7 @@ def build(program_path: str,
173177

174178

175179
def default_data_dir(bin_dir: str) -> str:
180+
# TODO fix this logic
176181
if not bin_dir:
177182
# Default to current directory.
178183
return ''
@@ -220,6 +225,9 @@ def default_lib_path(data_dir: str, target: int, pyversion: int,
220225
for v in versions:
221226
path.append(os.path.join(data_dir, 'stubs', v))
222227

228+
# Add stubs for third-party libraries needed by mypy.
229+
path.append(os.path.join(data_dir, 'stubs', 'third-party'))
230+
223231
# Add fallback path that can be used if we have a broken installation.
224232
if sys.platform != 'win32':
225233
path.append('/usr/local/lib/mypy')
@@ -446,7 +454,7 @@ def lookup_state(self, module: str) -> 'State':
446454
for state in self.states:
447455
if state.id == module:
448456
return state
449-
raise RuntimeError('%s not found' % str)
457+
raise RuntimeError('%s not found' % module)
450458

451459
def all_imported_modules_in_file(self,
452460
file: MypyFile) -> List[Tuple[str, int]]:
@@ -461,7 +469,7 @@ def correct_rel_imp(imp: Union[ImportFrom, ImportAll]) -> str:
461469
rel = imp.relative
462470
if rel == 0:
463471
return imp.id
464-
if os.path.basename(file.path) == '__init__.py':
472+
if os.path.basename(file.path).startswith('__init__.'):
465473
rel -= 1
466474
if rel != 0:
467475
file_id = ".".join(file_id.split(".")[:-rel])
@@ -504,7 +512,7 @@ def get_python_out_path(self, f: MypyFile) -> str:
504512
return os.path.join(self.output_dir, basename(f.path))
505513
else:
506514
components = f.fullname().split('.')
507-
if os.path.basename(f.path) == '__init__.py':
515+
if os.path.basename(f.path).startswith('__init__.'):
508516
components.append('__init__.py')
509517
else:
510518
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:]
@@ -142,7 +146,7 @@ def process_options(args: List[str]) -> Tuple[str, str, Options]:
142146
usage('--py2 specified, '
143147
'but --use-python-path will search in sys.path of Python 3')
144148

145-
return args[0], None, options
149+
return args[0], None, None, options
146150

147151

148152
# Don't generate this from mypy.reports, not all are meant to be public.
@@ -177,6 +181,7 @@ def usage(msg: str = None) -> None:
177181
--<fmt>-report dir generate a <fmt> report of type precision under dir/
178182
<fmt> may be one of: %s
179183
-m mod type check module
184+
-c string type check string
180185
--verbose more verbose messages
181186
--use-python-path search for modules in sys.path of running Python
182187
--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

setup.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,7 @@ def find_data_files(base, globs):
4949

5050
data_files = []
5151

52-
# Is there a reason not to just use:
53-
#data_files += find_data_files('stubs', ...)
54-
55-
for py_version in ['3.4', '3.3', '3.2', '2.7']:
56-
base = os.path.join('stubs', py_version)
57-
data_files += find_data_files(base, ['*.py', '*.pyi'])
52+
data_files += find_data_files('stubs', ['*.py', '*.pyi'])
5853

5954
data_files += find_data_files('xml', ['*.xsd', '*.xslt', '*.css'])
6055

0 commit comments

Comments
 (0)