Skip to content

Commit 8b56c1e

Browse files
x3k6a2matthiaskramm
authored andcommitted
Add all the files which pytype fails on during import. (#1720)
All of these files can not be used with pytype at HEAD. They fail during import with various error messages. Make the test script actually load the files and all dependencies of it, instead of just verifying that it can be parsed. This list was generated by running pytype with more options. 1: typeshed-location was pointed to the current typeshed instead of the one shipped with pytype. 2: --module-name=<foo> was provided as a parameter. Without this parameter wildcard imports "from _ast import *" misbehave. Files with a "# parse only" comment will only be parsed and not loaded.
1 parent 533a05b commit 8b56c1e

File tree

2 files changed

+131
-21
lines changed

2 files changed

+131
-21
lines changed

tests/pytype_blacklist.txt

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,70 @@ stdlib/3/typing.pyi
1111
# pytype doesn't yet support aliases with implicit type parameters
1212
# (e.g., here, FutureT = Future[T])
1313
stdlib/3.4/asyncio/tasks.pyi
14+
15+
# builtins not found
16+
stdlib/2/os/__init__.pyi # parse only
17+
18+
# Below files can not be loaded by PyType without an error.
19+
# Notice that the current travis configuration loads these files with Python 2.7.
20+
# The files will be parsed but not loaded to verify them.
21+
stdlib/3.4/asyncio/__init__.pyi # parse only
22+
stdlib/3.4/asyncio/events.pyi # parse only
23+
stdlib/3.4/asyncio/futures.pyi # parse only
24+
stdlib/3.4/asyncio/locks.pyi # parse only
25+
stdlib/3.4/asyncio/protocols.pyi # parse only
26+
stdlib/3.4/asyncio/queues.pyi # parse only
27+
stdlib/3.4/asyncio/streams.pyi # parse only
28+
stdlib/3.4/asyncio/subprocess.pyi # parse only
29+
stdlib/3.5/zipapp.pyi # parse only
30+
stdlib/3/_compression.pyi # parse only
31+
stdlib/3/ast.pyi # parse only
32+
stdlib/3/collections/__init__.pyi # parse only.pyi
33+
stdlib/3/concurrent/futures/__init__.pyi # parse only
34+
stdlib/3/concurrent/futures/process.pyi # parse only
35+
stdlib/3/concurrent/futures/thread.pyi # parse only
36+
stdlib/3/curses/__init__.pyi # parse only
37+
stdlib/3/email/__init__.pyi # parse only
38+
stdlib/3/email/contentmanager.pyi # parse only
39+
stdlib/3/email/encoders.pyi # parse only
40+
stdlib/3/email/feedparser.pyi # parse only
41+
stdlib/3/email/generator.pyi # parse only
42+
stdlib/3/email/header.pyi # parse only
43+
stdlib/3/email/headerregistry.pyi # parse only
44+
stdlib/3/email/iterators.pyi # parse only
45+
stdlib/3/email/message.pyi # parse only
46+
stdlib/3/email/mime/base.pyi # parse only
47+
stdlib/3/email/mime/message.pyi # parse only
48+
stdlib/3/email/mime/multipart.pyi # parse only
49+
stdlib/3/email/parser.pyi # parse only
50+
stdlib/3/email/policy.pyi # parse only
51+
stdlib/3/email/utils.pyi # parse only
52+
stdlib/3/gzip.pyi # parse only
53+
stdlib/3/html/parser.pyi # parse only
54+
stdlib/3/http/client.pyi # parse only
55+
stdlib/3/http/cookiejar.pyi # parse only
56+
stdlib/3/http/server.pyi # parse only
57+
stdlib/3/imp.pyi # parse only
58+
stdlib/3/importlib/__init__.pyi # parse only
59+
stdlib/3/importlib/abc.pyi # parse only
60+
stdlib/3/importlib/machinery.pyi # parse only
61+
stdlib/3/io.pyi # parse only
62+
stdlib/3/json/__init__.pyi # parse only
63+
stdlib/3/multiprocessing/__init__.pyi # parse only
64+
stdlib/3/multiprocessing/context.pyi # parse only
65+
stdlib/3/multiprocessing/managers.pyi # parse only
66+
stdlib/3/multiprocessing/pool.pyi # parse only
67+
stdlib/3/multiprocessing/process.pyi # parse only
68+
stdlib/3/multiprocessing/synchronize.pyi # parse only
69+
stdlib/3/os/__init__.pyi # parse only
70+
stdlib/3/pipes.pyi # parse only
71+
stdlib/3/smtplib.pyi # parse only
72+
stdlib/3/sqlite3/__init__.pyi # parse only
73+
stdlib/3/sre_parse.pyi # parse only
74+
stdlib/3/time.pyi # parse only
75+
stdlib/3/tkinter/__init__.pyi # parse only
76+
stdlib/3/tkinter/ttk.pyi # parse only
77+
stdlib/3/tokenize.pyi # parse only
78+
stdlib/3/types.pyi # parse only
79+
stdlib/3/urllib/error.pyi # parse only
80+
stdlib/3/urllib/request.pyi # parse only

tests/pytype_test.py

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@
44
Depends on mypy and pytype being installed.
55
66
If pytype is installed:
7-
1. For every pyi, run "pytd <foo.pyi>" in a separate process
7+
1. For every pyi, do nothing if it is in pytype_blacklist.txt.
8+
2. If the blacklist line has a "# parse only" comment run
9+
"pytd <foo.pyi>" in a separate process.
10+
3. If the file is not in the blacklist run
11+
"pytype --typeshed-location=typeshed_location --module-name=foo \
12+
--convert-to-pickle=tmp_file <foo.pyi>.
13+
Option two will parse the file, mostly syntactical correctness. Option three
14+
will load the file and all the builtins, typeshed dependencies. This will
15+
also discover incorrect usage of imported modules.
816
"""
917

1018
import os
@@ -25,32 +33,44 @@ def main():
2533
code, runs = pytype_test(args)
2634

2735
if code:
28-
print("--- exit status %d ---" % code)
36+
print('--- exit status %d ---' % code)
2937
sys.exit(code)
3038
if not runs:
31-
print("--- nothing to do; exit 1 ---")
39+
print('--- nothing to do; exit 1 ---')
3240
sys.exit(1)
3341

3442

3543
def load_blacklist():
3644
filename = os.path.join(os.path.dirname(__file__), "pytype_blacklist.txt")
37-
regex = r"^\s*([^\s#]+)\s*(?:#.*)?$"
45+
skip_re = re.compile(r'^\s*([^\s#]+)\s*(?:#.*)?$')
46+
parse_only_re = re.compile(r'^\s*([^\s#]+)\s*#\s*parse only\s*')
47+
skip = []
48+
parse_only = []
3849

3950
with open(filename) as f:
40-
return re.findall(regex, f.read(), flags=re.M)
51+
for line in f:
52+
parse_only_match = parse_only_re.match(line)
53+
skip_match = skip_re.match(line)
54+
if parse_only_match:
55+
parse_only.append(parse_only_match.group(1))
56+
elif skip_match:
57+
skip.append(skip_match.group(1))
4158

59+
return skip, parse_only
4260

43-
class PytdRun(object):
61+
62+
class BinaryRun(object):
4463
def __init__(self, args, dry_run=False):
4564
self.args = args
65+
4666
self.dry_run = dry_run
4767
self.results = None
4868

4969
if dry_run:
50-
self.results = (0, "", "")
70+
self.results = (0, '', '')
5171
else:
5272
self.proc = subprocess.Popen(
53-
["pytd"] + args,
73+
self.args,
5474
stdout=subprocess.PIPE,
5575
stderr=subprocess.PIPE)
5676

@@ -63,29 +83,54 @@ def communicate(self):
6383
return self.results
6484

6585

86+
def _get_module_name(filename):
87+
"""Converts a filename stdblib/m.n/module/foo to module.foo."""
88+
return '.'.join(filename.split(os.path.sep)[2:]).replace(
89+
'.pyi', '').replace('.__init__', '')
90+
91+
6692
def pytype_test(args):
6793
try:
68-
PytdRun(["-h"]).communicate()
94+
BinaryRun(['pytd', '-h']).communicate()
6995
except OSError:
70-
print("Cannot run pytd. Did you install pytype?")
96+
print('Cannot run pytd. Did you install pytype?')
7197
return 0, 0
7298

73-
wanted = re.compile(r"stdlib/.*\.pyi$")
74-
skipped = re.compile("(%s)$" % "|".join(load_blacklist()))
75-
files = []
99+
skip, parse_only = load_blacklist()
100+
wanted = re.compile(r'stdlib/.*\.pyi$')
101+
skipped = re.compile('(%s)$' % '|'.join(skip))
102+
parse_only = re.compile('(%s)$' % '|'.join(parse_only))
103+
104+
pytype_run = []
105+
pytd_run = []
76106

77-
for root, _, filenames in os.walk("stdlib"):
107+
for root, _, filenames in os.walk('stdlib'):
78108
for f in sorted(filenames):
79109
f = os.path.join(root, f)
80-
if wanted.search(f) and not skipped.search(f):
81-
files.append(f)
110+
if wanted.search(f):
111+
if parse_only.search(f):
112+
pytd_run.append(f)
113+
elif not skipped.search(f):
114+
pytype_run.append(f)
82115

83116
running_tests = collections.deque()
84117
max_code, runs, errors = 0, 0, 0
85-
print("Running pytype tests...")
118+
files = pytype_run + pytd_run
86119
while 1:
87120
while files and len(running_tests) < args.num_parallel:
88-
test_run = PytdRun([files.pop()], dry_run=args.dry_run)
121+
f = files.pop()
122+
if f in pytype_run:
123+
test_run = BinaryRun(
124+
['pytype',
125+
'--typeshed-location=%s' % os.getcwd(),
126+
'--module-name=%s' % _get_module_name(f),
127+
'--convert-to-pickle=%s' % os.devnull,
128+
f],
129+
dry_run=args.dry_run)
130+
elif f in pytd_run:
131+
test_run = BinaryRun(['pytd', f], dry_run=args.dry_run)
132+
else:
133+
raise ValueError('Unknown action for file: %s' % f)
89134
running_tests.append(test_run)
90135

91136
if not running_tests:
@@ -97,13 +142,11 @@ def pytype_test(args):
97142
runs += 1
98143

99144
if code:
100-
print("pytd error processing \"%s\":" % test_run.args[0])
101145
print(stderr)
102146
errors += 1
103147

104-
print("Ran pytype with %d pyis, got %d errors." % (runs, errors))
148+
print('Ran pytype with %d pyis, got %d errors.' % (runs, errors))
105149
return max_code, runs
106150

107-
108151
if __name__ == '__main__':
109152
main()

0 commit comments

Comments
 (0)