1717
1818See --help for more information
1919"""
20+
21+ from __future__ import annotations
22+
2023import _imp
2124import argparse
2225import collections
2932import sysconfig
3033import warnings
3134from collections .abc import Iterable
32- from importlib ._bootstrap import _load as bootstrap_load
35+ from importlib ._bootstrap import _load as bootstrap_load # type: ignore[attr-defined]
3336from importlib .machinery import (
3437 BuiltinImporter ,
3538 ExtensionFileLoader ,
3639 ModuleSpec ,
3740)
3841from importlib .util import spec_from_file_location , spec_from_loader
42+ from typing import NamedTuple
3943
4044SRC_DIR = pathlib .Path (__file__ ).parent .parent .parent
4145
112116)
113117
114118
119+ @enum .unique
115120class ModuleState (enum .Enum ):
116121 # Makefile state "yes"
117122 BUILTIN = "builtin"
@@ -123,21 +128,23 @@ class ModuleState(enum.Enum):
123128 # disabled by Setup / makesetup rule
124129 DISABLED_SETUP = "disabled_setup"
125130
126- def __bool__ (self ):
131+ def __bool__ (self ) -> bool :
127132 return self .value in {"builtin" , "shared" }
128133
129134
130- ModuleInfo = collections .namedtuple ("ModuleInfo" , "name state" )
135+ class ModuleInfo (NamedTuple ):
136+ name : str
137+ state : ModuleState
131138
132139
133140class ModuleChecker :
134141 pybuilddir_txt = "pybuilddir.txt"
135142
136143 setup_files = (
137144 # see end of configure.ac
138- "Modules/Setup.local" ,
139- "Modules/Setup.stdlib" ,
140- "Modules/Setup.bootstrap" ,
145+ pathlib . Path ( "Modules/Setup.local" ) ,
146+ pathlib . Path ( "Modules/Setup.stdlib" ) ,
147+ pathlib . Path ( "Modules/Setup.bootstrap" ) ,
141148 SRC_DIR / "Modules/Setup" ,
142149 )
143150
@@ -149,15 +156,15 @@ def __init__(self, cross_compiling: bool = False, strict: bool = False):
149156 self .builddir = self .get_builddir ()
150157 self .modules = self .get_modules ()
151158
152- self .builtin_ok = []
153- self .shared_ok = []
154- self .failed_on_import = []
155- self .missing = []
156- self .disabled_configure = []
157- self .disabled_setup = []
158- self .notavailable = []
159+ self .builtin_ok : list [ ModuleInfo ] = []
160+ self .shared_ok : list [ ModuleInfo ] = []
161+ self .failed_on_import : list [ ModuleInfo ] = []
162+ self .missing : list [ ModuleInfo ] = []
163+ self .disabled_configure : list [ ModuleInfo ] = []
164+ self .disabled_setup : list [ ModuleInfo ] = []
165+ self .notavailable : list [ ModuleInfo ] = []
159166
160- def check (self ):
167+ def check (self ) -> None :
161168 if not hasattr (_imp , 'create_dynamic' ):
162169 logger .warning (
163170 ('Dynamic extensions not supported '
@@ -189,10 +196,10 @@ def check(self):
189196 assert modinfo .state == ModuleState .SHARED
190197 self .shared_ok .append (modinfo )
191198
192- def summary (self , * , verbose : bool = False ):
199+ def summary (self , * , verbose : bool = False ) -> None :
193200 longest = max ([len (e .name ) for e in self .modules ], default = 0 )
194201
195- def print_three_column (modinfos : list [ModuleInfo ]):
202+ def print_three_column (modinfos : list [ModuleInfo ]) -> None :
196203 names = [modinfo .name for modinfo in modinfos ]
197204 names .sort (key = str .lower )
198205 # guarantee zip() doesn't drop anything
@@ -262,12 +269,12 @@ def print_three_column(modinfos: list[ModuleInfo]):
262269 f"{ len (self .failed_on_import )} failed on import)"
263270 )
264271
265- def check_strict_build (self ):
272+ def check_strict_build (self ) -> None :
266273 """Fail if modules are missing and it's a strict build"""
267274 if self .strict_extensions_build and (self .failed_on_import or self .missing ):
268275 raise RuntimeError ("Failed to build some stdlib modules" )
269276
270- def list_module_names (self , * , all : bool = False ) -> set :
277+ def list_module_names (self , * , all : bool = False ) -> set [ str ] :
271278 names = {modinfo .name for modinfo in self .modules }
272279 if all :
273280 names .update (WINDOWS_MODULES )
@@ -280,9 +287,9 @@ def get_builddir(self) -> pathlib.Path:
280287 except FileNotFoundError :
281288 logger .error ("%s must be run from the top build directory" , __file__ )
282289 raise
283- builddir = pathlib .Path (builddir )
284- logger .debug ("%s: %s" , self .pybuilddir_txt , builddir )
285- return builddir
290+ builddir_path = pathlib .Path (builddir )
291+ logger .debug ("%s: %s" , self .pybuilddir_txt , builddir_path )
292+ return builddir_path
286293
287294 def get_modules (self ) -> list [ModuleInfo ]:
288295 """Get module info from sysconfig and Modules/Setup* files"""
@@ -367,7 +374,7 @@ def parse_setup_file(self, setup_file: pathlib.Path) -> Iterable[ModuleInfo]:
367374 case ["*disabled*" ]:
368375 state = ModuleState .DISABLED
369376 case ["*noconfig*" ]:
370- state = None
377+ continue
371378 case [* items ]:
372379 if state == ModuleState .DISABLED :
373380 # *disabled* can disable multiple modules per line
@@ -384,34 +391,41 @@ def parse_setup_file(self, setup_file: pathlib.Path) -> Iterable[ModuleInfo]:
384391 def get_spec (self , modinfo : ModuleInfo ) -> ModuleSpec :
385392 """Get ModuleSpec for builtin or extension module"""
386393 if modinfo .state == ModuleState .SHARED :
387- location = os .fspath (self .get_location (modinfo ))
394+ mod_location = self .get_location (modinfo )
395+ assert mod_location is not None
396+ location = os .fspath (mod_location )
388397 loader = ExtensionFileLoader (modinfo .name , location )
389- return spec_from_file_location (modinfo .name , location , loader = loader )
398+ spec = spec_from_file_location (modinfo .name , location , loader = loader )
399+ assert spec is not None
400+ return spec
390401 elif modinfo .state == ModuleState .BUILTIN :
391- return spec_from_loader (modinfo .name , loader = BuiltinImporter )
402+ spec = spec_from_loader (modinfo .name , loader = BuiltinImporter )
403+ assert spec is not None
404+ return spec
392405 else :
393406 raise ValueError (modinfo )
394407
395- def get_location (self , modinfo : ModuleInfo ) -> pathlib .Path :
408+ def get_location (self , modinfo : ModuleInfo ) -> pathlib .Path | None :
396409 """Get shared library location in build directory"""
397410 if modinfo .state == ModuleState .SHARED :
398411 return self .builddir / f"{ modinfo .name } { self .ext_suffix } "
399412 else :
400413 return None
401414
402- def _check_file (self , modinfo : ModuleInfo , spec : ModuleSpec ):
415+ def _check_file (self , modinfo : ModuleInfo , spec : ModuleSpec ) -> None :
403416 """Check that the module file is present and not empty"""
404- if spec .loader is BuiltinImporter :
417+ if spec .loader is BuiltinImporter : # type: ignore[comparison-overlap]
405418 return
406419 try :
420+ assert spec .origin is not None
407421 st = os .stat (spec .origin )
408422 except FileNotFoundError :
409423 logger .error ("%s (%s) is missing" , modinfo .name , spec .origin )
410424 raise
411425 if not st .st_size :
412426 raise ImportError (f"{ spec .origin } is an empty file" )
413427
414- def check_module_import (self , modinfo : ModuleInfo ):
428+ def check_module_import (self , modinfo : ModuleInfo ) -> None :
415429 """Attempt to import module and report errors"""
416430 spec = self .get_spec (modinfo )
417431 self ._check_file (modinfo , spec )
@@ -430,7 +444,7 @@ def check_module_import(self, modinfo: ModuleInfo):
430444 logger .exception ("Importing extension '%s' failed!" , modinfo .name )
431445 raise
432446
433- def check_module_cross (self , modinfo : ModuleInfo ):
447+ def check_module_cross (self , modinfo : ModuleInfo ) -> None :
434448 """Sanity check for cross compiling"""
435449 spec = self .get_spec (modinfo )
436450 self ._check_file (modinfo , spec )
@@ -443,6 +457,7 @@ def rename_module(self, modinfo: ModuleInfo) -> None:
443457
444458 failed_name = f"{ modinfo .name } _failed{ self .ext_suffix } "
445459 builddir_path = self .get_location (modinfo )
460+ assert builddir_path is not None
446461 if builddir_path .is_symlink ():
447462 symlink = builddir_path
448463 module_path = builddir_path .resolve ().relative_to (os .getcwd ())
@@ -466,7 +481,7 @@ def rename_module(self, modinfo: ModuleInfo) -> None:
466481 logger .debug ("Rename '%s' -> '%s'" , module_path , failed_path )
467482
468483
469- def main ():
484+ def main () -> None :
470485 args = parser .parse_args ()
471486 if args .debug :
472487 args .verbose = True
0 commit comments