1717
1818See --help for more information
1919"""
20+
21+ from __future__ import annotations
22+
23+ import _imp
2024import argparse
21- import collections
2225import enum
2326import logging
2427import os
2730import sys
2831import sysconfig
2932import warnings
30- import _imp
3133
32- from importlib ._bootstrap import _load as bootstrap_load
34+ from collections .abc import Iterable
35+ from importlib ._bootstrap import _load as bootstrap_load # type: ignore[attr-defined]
3336from importlib .machinery import BuiltinImporter , ExtensionFileLoader , ModuleSpec
3437from importlib .util import spec_from_file_location , spec_from_loader
35- from typing import Iterable
38+ from typing import NamedTuple
3639
3740SRC_DIR = pathlib .Path (__file__ ).parent .parent .parent
3841
109112)
110113
111114
115+ @enum .unique
112116class ModuleState (enum .Enum ):
113117 # Makefile state "yes"
114118 BUILTIN = "builtin"
@@ -120,21 +124,23 @@ class ModuleState(enum.Enum):
120124 # disabled by Setup / makesetup rule
121125 DISABLED_SETUP = "disabled_setup"
122126
123- def __bool__ (self ):
127+ def __bool__ (self ) -> bool :
124128 return self .value in {"builtin" , "shared" }
125129
126130
127- ModuleInfo = collections .namedtuple ("ModuleInfo" , "name state" )
131+ class ModuleInfo (NamedTuple ):
132+ name : str
133+ state : ModuleState
128134
129135
130136class ModuleChecker :
131137 pybuilddir_txt = "pybuilddir.txt"
132138
133139 setup_files = (
134140 # see end of configure.ac
135- "Modules/Setup.local" ,
136- "Modules/Setup.stdlib" ,
137- "Modules/Setup.bootstrap" ,
141+ pathlib . Path ( "Modules/Setup.local" ) ,
142+ pathlib . Path ( "Modules/Setup.stdlib" ) ,
143+ pathlib . Path ( "Modules/Setup.bootstrap" ) ,
138144 SRC_DIR / "Modules/Setup" ,
139145 )
140146
@@ -146,15 +152,15 @@ def __init__(self, cross_compiling: bool = False, strict: bool = False):
146152 self .builddir = self .get_builddir ()
147153 self .modules = self .get_modules ()
148154
149- self .builtin_ok = []
150- self .shared_ok = []
151- self .failed_on_import = []
152- self .missing = []
153- self .disabled_configure = []
154- self .disabled_setup = []
155- self .notavailable = []
155+ self .builtin_ok : list [ ModuleInfo ] = []
156+ self .shared_ok : list [ ModuleInfo ] = []
157+ self .failed_on_import : list [ ModuleInfo ] = []
158+ self .missing : list [ ModuleInfo ] = []
159+ self .disabled_configure : list [ ModuleInfo ] = []
160+ self .disabled_setup : list [ ModuleInfo ] = []
161+ self .notavailable : list [ ModuleInfo ] = []
156162
157- def check (self ):
163+ def check (self ) -> None :
158164 if not hasattr (_imp , 'create_dynamic' ):
159165 logger .warning (
160166 ('Dynamic extensions not supported '
@@ -186,10 +192,10 @@ def check(self):
186192 assert modinfo .state == ModuleState .SHARED
187193 self .shared_ok .append (modinfo )
188194
189- def summary (self , * , verbose : bool = False ):
195+ def summary (self , * , verbose : bool = False ) -> None :
190196 longest = max ([len (e .name ) for e in self .modules ], default = 0 )
191197
192- def print_three_column (modinfos : list [ModuleInfo ]):
198+ def print_three_column (modinfos : list [ModuleInfo ]) -> None :
193199 names = [modinfo .name for modinfo in modinfos ]
194200 names .sort (key = str .lower )
195201 # guarantee zip() doesn't drop anything
@@ -259,12 +265,12 @@ def print_three_column(modinfos: list[ModuleInfo]):
259265 f"{ len (self .failed_on_import )} failed on import)"
260266 )
261267
262- def check_strict_build (self ):
268+ def check_strict_build (self ) -> None :
263269 """Fail if modules are missing and it's a strict build"""
264270 if self .strict_extensions_build and (self .failed_on_import or self .missing ):
265271 raise RuntimeError ("Failed to build some stdlib modules" )
266272
267- def list_module_names (self , * , all : bool = False ) -> set :
273+ def list_module_names (self , * , all : bool = False ) -> set [ str ] :
268274 names = {modinfo .name for modinfo in self .modules }
269275 if all :
270276 names .update (WINDOWS_MODULES )
@@ -277,9 +283,9 @@ def get_builddir(self) -> pathlib.Path:
277283 except FileNotFoundError :
278284 logger .error ("%s must be run from the top build directory" , __file__ )
279285 raise
280- builddir = pathlib .Path (builddir )
281- logger .debug ("%s: %s" , self .pybuilddir_txt , builddir )
282- return builddir
286+ builddir_path = pathlib .Path (builddir )
287+ logger .debug ("%s: %s" , self .pybuilddir_txt , builddir_path )
288+ return builddir_path
283289
284290 def get_modules (self ) -> list [ModuleInfo ]:
285291 """Get module info from sysconfig and Modules/Setup* files"""
@@ -364,7 +370,7 @@ def parse_setup_file(self, setup_file: pathlib.Path) -> Iterable[ModuleInfo]:
364370 case ["*disabled*" ]:
365371 state = ModuleState .DISABLED
366372 case ["*noconfig*" ]:
367- state = None
373+ continue
368374 case [* items ]:
369375 if state == ModuleState .DISABLED :
370376 # *disabled* can disable multiple modules per line
@@ -381,34 +387,41 @@ def parse_setup_file(self, setup_file: pathlib.Path) -> Iterable[ModuleInfo]:
381387 def get_spec (self , modinfo : ModuleInfo ) -> ModuleSpec :
382388 """Get ModuleSpec for builtin or extension module"""
383389 if modinfo .state == ModuleState .SHARED :
384- location = os .fspath (self .get_location (modinfo ))
390+ mod_location = self .get_location (modinfo )
391+ assert mod_location is not None
392+ location = os .fspath (mod_location )
385393 loader = ExtensionFileLoader (modinfo .name , location )
386- return spec_from_file_location (modinfo .name , location , loader = loader )
394+ spec = spec_from_file_location (modinfo .name , location , loader = loader )
395+ assert spec is not None
396+ return spec
387397 elif modinfo .state == ModuleState .BUILTIN :
388- return spec_from_loader (modinfo .name , loader = BuiltinImporter )
398+ spec = spec_from_loader (modinfo .name , loader = BuiltinImporter )
399+ assert spec is not None
400+ return spec
389401 else :
390402 raise ValueError (modinfo )
391403
392- def get_location (self , modinfo : ModuleInfo ) -> pathlib .Path :
404+ def get_location (self , modinfo : ModuleInfo ) -> pathlib .Path | None :
393405 """Get shared library location in build directory"""
394406 if modinfo .state == ModuleState .SHARED :
395407 return self .builddir / f"{ modinfo .name } { self .ext_suffix } "
396408 else :
397409 return None
398410
399- def _check_file (self , modinfo : ModuleInfo , spec : ModuleSpec ):
411+ def _check_file (self , modinfo : ModuleInfo , spec : ModuleSpec ) -> None :
400412 """Check that the module file is present and not empty"""
401- if spec .loader is BuiltinImporter :
413+ if spec .loader is BuiltinImporter : # type: ignore[comparison-overlap]
402414 return
403415 try :
416+ assert spec .origin is not None
404417 st = os .stat (spec .origin )
405418 except FileNotFoundError :
406419 logger .error ("%s (%s) is missing" , modinfo .name , spec .origin )
407420 raise
408421 if not st .st_size :
409422 raise ImportError (f"{ spec .origin } is an empty file" )
410423
411- def check_module_import (self , modinfo : ModuleInfo ):
424+ def check_module_import (self , modinfo : ModuleInfo ) -> None :
412425 """Attempt to import module and report errors"""
413426 spec = self .get_spec (modinfo )
414427 self ._check_file (modinfo , spec )
@@ -427,7 +440,7 @@ def check_module_import(self, modinfo: ModuleInfo):
427440 logger .exception ("Importing extension '%s' failed!" , modinfo .name )
428441 raise
429442
430- def check_module_cross (self , modinfo : ModuleInfo ):
443+ def check_module_cross (self , modinfo : ModuleInfo ) -> None :
431444 """Sanity check for cross compiling"""
432445 spec = self .get_spec (modinfo )
433446 self ._check_file (modinfo , spec )
@@ -440,6 +453,7 @@ def rename_module(self, modinfo: ModuleInfo) -> None:
440453
441454 failed_name = f"{ modinfo .name } _failed{ self .ext_suffix } "
442455 builddir_path = self .get_location (modinfo )
456+ assert builddir_path is not None
443457 if builddir_path .is_symlink ():
444458 symlink = builddir_path
445459 module_path = builddir_path .resolve ().relative_to (os .getcwd ())
@@ -463,7 +477,7 @@ def rename_module(self, modinfo: ModuleInfo) -> None:
463477 logger .debug ("Rename '%s' -> '%s'" , module_path , failed_path )
464478
465479
466- def main ():
480+ def main () -> None :
467481 args = parser .parse_args ()
468482 if args .debug :
469483 args .verbose = True
0 commit comments