1
1
#!/usr/bin/env python3
2
- """Run mypy on various typeshed directories, with varying command-line arguments.
2
+ """Run mypy on typeshed's stdlib and third-party stubs."""
3
3
4
- Depends on mypy being installed.
5
- """
6
4
from __future__ import annotations
7
5
8
6
import argparse
9
7
import os
10
8
import re
11
- import shutil
12
- import subprocess
13
9
import sys
14
10
import tempfile
15
- from collections .abc import Iterable
16
11
from contextlib import redirect_stderr , redirect_stdout
17
12
from dataclasses import dataclass
18
13
from io import StringIO
26
21
from typing_extensions import Annotated , TypeAlias
27
22
28
23
import tomli
29
- from colors import colored , print_error , print_success_msg
24
+ from utils import colored , print_error , print_success_msg , read_dependencies
30
25
31
26
SUPPORTED_VERSIONS = [(3 , 11 ), (3 , 10 ), (3 , 9 ), (3 , 8 ), (3 , 7 )]
32
27
SUPPORTED_PLATFORMS = ("linux" , "win32" , "darwin" )
33
- TYPESHED_DIRECTORIES = frozenset ({"stdlib" , "stubs" , "test_cases" })
28
+ TYPESHED_DIRECTORIES = frozenset ({"stdlib" , "stubs" })
34
29
35
30
ReturnCode : TypeAlias = int
36
31
MajorVersion : TypeAlias = int
@@ -58,7 +53,9 @@ class CommandLineArgs(argparse.Namespace):
58
53
filter : list [str ]
59
54
60
55
61
- parser = argparse .ArgumentParser (description = "Test runner for typeshed. Patterns are unanchored regexps on the full path." )
56
+ parser = argparse .ArgumentParser (
57
+ description = "Typecheck typeshed's stubs with mypy. Patterns are unanchored regexps on the full path."
58
+ )
62
59
parser .add_argument ("-v" , "--verbose" , action = "count" , default = 0 , help = "More output" )
63
60
parser .add_argument ("-x" , "--exclude" , type = str , nargs = "*" , help = "Exclude pattern" )
64
61
parser .add_argument (
@@ -239,20 +236,8 @@ def run_mypy(args: TestConfig, configurations: list[MypyDistConf], files: list[P
239
236
return exit_code
240
237
241
238
242
- def run_mypy_as_subprocess (directory : StrPath , flags : Iterable [str ]) -> ReturnCode :
243
- result = subprocess .run ([sys .executable , "-m" , "mypy" , directory , * flags ], capture_output = True )
244
- stdout , stderr = result .stdout , result .stderr
245
- if stderr :
246
- print_error (stderr .decode ())
247
- if stdout :
248
- print_error (stdout .decode ())
249
- return result .returncode
250
-
251
-
252
- def get_mypy_flags (
253
- args : TestConfig , temp_name : str | None , * , strict : bool = False , enforce_error_codes : bool = True
254
- ) -> list [str ]:
255
- flags = [
239
+ def get_mypy_flags (args : TestConfig , temp_name : str ) -> list [str ]:
240
+ return [
256
241
"--python-version" ,
257
242
f"{ args .major } .{ args .minor } " ,
258
243
"--show-traceback" ,
@@ -264,29 +249,15 @@ def get_mypy_flags(
264
249
"--no-site-packages" ,
265
250
"--custom-typeshed-dir" ,
266
251
str (Path (__file__ ).parent .parent ),
252
+ "--no-implicit-optional" ,
253
+ "--disallow-untyped-decorators" ,
254
+ "--disallow-any-generics" ,
255
+ "--strict-equality" ,
256
+ "--enable-error-code" ,
257
+ "ignore-without-code" ,
258
+ "--config-file" ,
259
+ temp_name ,
267
260
]
268
- if strict :
269
- flags .append ("--strict" )
270
- else :
271
- flags .extend (["--no-implicit-optional" , "--disallow-untyped-decorators" , "--disallow-any-generics" , "--strict-equality" ])
272
- if temp_name is not None :
273
- flags .extend (["--config-file" , temp_name ])
274
- if enforce_error_codes :
275
- flags .extend (["--enable-error-code" , "ignore-without-code" ])
276
- return flags
277
-
278
-
279
- def read_dependencies (distribution : str ) -> list [str ]:
280
- with Path ("stubs" , distribution , "METADATA.toml" ).open ("rb" ) as f :
281
- data = tomli .load (f )
282
- requires = data .get ("requires" , [])
283
- assert isinstance (requires , list )
284
- dependencies = []
285
- for dependency in requires :
286
- assert isinstance (dependency , str )
287
- assert dependency .startswith ("types-" )
288
- dependencies .append (dependency [6 :].split ("<" )[0 ])
289
- return dependencies
290
261
291
262
292
263
def add_third_party_files (
@@ -382,23 +353,6 @@ def test_third_party_stubs(code: int, args: TestConfig) -> TestResults:
382
353
return TestResults (code , files_checked )
383
354
384
355
385
- def test_the_test_cases (code : int , args : TestConfig ) -> TestResults :
386
- test_case_files = list (map (str , Path ("test_cases" ).rglob ("*.py" )))
387
- num_test_case_files = len (test_case_files )
388
- flags = get_mypy_flags (args , None , strict = True , enforce_error_codes = False )
389
- print (f"Running mypy on the test_cases directory ({ num_test_case_files } files)..." )
390
- print ("Running mypy " + " " .join (flags ))
391
- # --warn-unused-ignores doesn't work for files inside typeshed.
392
- # SO, to work around this, we copy the test_cases directory into a TemporaryDirectory.
393
- with tempfile .TemporaryDirectory () as td :
394
- shutil .copytree (Path ("test_cases" ), Path (td ) / "test_cases" )
395
- this_code = run_mypy_as_subprocess (td , flags )
396
- if not this_code :
397
- print_success_msg ()
398
- code = max (code , this_code )
399
- return TestResults (code , num_test_case_files )
400
-
401
-
402
356
def test_typeshed (code : int , args : TestConfig ) -> TestResults :
403
357
print (f"*** Testing Python { args .major } .{ args .minor } on { args .platform } " )
404
358
files_checked_this_version = 0
@@ -412,11 +366,6 @@ def test_typeshed(code: int, args: TestConfig) -> TestResults:
412
366
files_checked_this_version += third_party_files_checked
413
367
print ()
414
368
415
- if "test_cases" in args .directories :
416
- code , test_case_files_checked = test_the_test_cases (code , args )
417
- files_checked_this_version += test_case_files_checked
418
- print ()
419
-
420
369
return TestResults (code , files_checked_this_version )
421
370
422
371
0 commit comments