17
17
18
18
See --help for more information
19
19
"""
20
+
21
+ from __future__ import annotations
22
+
23
+ import _imp
20
24
import argparse
21
- import collections
22
25
import enum
23
26
import logging
24
27
import os
27
30
import sys
28
31
import sysconfig
29
32
import warnings
30
- import _imp
31
33
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]
33
36
from importlib .machinery import BuiltinImporter , ExtensionFileLoader , ModuleSpec
34
37
from importlib .util import spec_from_file_location , spec_from_loader
35
- from typing import Iterable
38
+ from typing import NamedTuple
36
39
37
40
SRC_DIR = pathlib .Path (__file__ ).parent .parent .parent
38
41
109
112
)
110
113
111
114
115
+ @enum .unique
112
116
class ModuleState (enum .Enum ):
113
117
# Makefile state "yes"
114
118
BUILTIN = "builtin"
@@ -120,21 +124,23 @@ class ModuleState(enum.Enum):
120
124
# disabled by Setup / makesetup rule
121
125
DISABLED_SETUP = "disabled_setup"
122
126
123
- def __bool__ (self ):
127
+ def __bool__ (self ) -> bool :
124
128
return self .value in {"builtin" , "shared" }
125
129
126
130
127
- ModuleInfo = collections .namedtuple ("ModuleInfo" , "name state" )
131
+ class ModuleInfo (NamedTuple ):
132
+ name : str
133
+ state : ModuleState
128
134
129
135
130
136
class ModuleChecker :
131
137
pybuilddir_txt = "pybuilddir.txt"
132
138
133
139
setup_files = (
134
140
# 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" ) ,
138
144
SRC_DIR / "Modules/Setup" ,
139
145
)
140
146
@@ -146,15 +152,15 @@ def __init__(self, cross_compiling: bool = False, strict: bool = False):
146
152
self .builddir = self .get_builddir ()
147
153
self .modules = self .get_modules ()
148
154
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 ] = []
156
162
157
- def check (self ):
163
+ def check (self ) -> None :
158
164
if not hasattr (_imp , 'create_dynamic' ):
159
165
logger .warning (
160
166
('Dynamic extensions not supported '
@@ -186,10 +192,10 @@ def check(self):
186
192
assert modinfo .state == ModuleState .SHARED
187
193
self .shared_ok .append (modinfo )
188
194
189
- def summary (self , * , verbose : bool = False ):
195
+ def summary (self , * , verbose : bool = False ) -> None :
190
196
longest = max ([len (e .name ) for e in self .modules ], default = 0 )
191
197
192
- def print_three_column (modinfos : list [ModuleInfo ]):
198
+ def print_three_column (modinfos : list [ModuleInfo ]) -> None :
193
199
names = [modinfo .name for modinfo in modinfos ]
194
200
names .sort (key = str .lower )
195
201
# guarantee zip() doesn't drop anything
@@ -259,12 +265,12 @@ def print_three_column(modinfos: list[ModuleInfo]):
259
265
f"{ len (self .failed_on_import )} failed on import)"
260
266
)
261
267
262
- def check_strict_build (self ):
268
+ def check_strict_build (self ) -> None :
263
269
"""Fail if modules are missing and it's a strict build"""
264
270
if self .strict_extensions_build and (self .failed_on_import or self .missing ):
265
271
raise RuntimeError ("Failed to build some stdlib modules" )
266
272
267
- def list_module_names (self , * , all : bool = False ) -> set :
273
+ def list_module_names (self , * , all : bool = False ) -> set [ str ] :
268
274
names = {modinfo .name for modinfo in self .modules }
269
275
if all :
270
276
names .update (WINDOWS_MODULES )
@@ -277,9 +283,9 @@ def get_builddir(self) -> pathlib.Path:
277
283
except FileNotFoundError :
278
284
logger .error ("%s must be run from the top build directory" , __file__ )
279
285
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
283
289
284
290
def get_modules (self ) -> list [ModuleInfo ]:
285
291
"""Get module info from sysconfig and Modules/Setup* files"""
@@ -364,7 +370,7 @@ def parse_setup_file(self, setup_file: pathlib.Path) -> Iterable[ModuleInfo]:
364
370
case ["*disabled*" ]:
365
371
state = ModuleState .DISABLED
366
372
case ["*noconfig*" ]:
367
- state = None
373
+ continue
368
374
case [* items ]:
369
375
if state == ModuleState .DISABLED :
370
376
# *disabled* can disable multiple modules per line
@@ -381,34 +387,41 @@ def parse_setup_file(self, setup_file: pathlib.Path) -> Iterable[ModuleInfo]:
381
387
def get_spec (self , modinfo : ModuleInfo ) -> ModuleSpec :
382
388
"""Get ModuleSpec for builtin or extension module"""
383
389
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 )
385
393
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
387
397
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
389
401
else :
390
402
raise ValueError (modinfo )
391
403
392
- def get_location (self , modinfo : ModuleInfo ) -> pathlib .Path :
404
+ def get_location (self , modinfo : ModuleInfo ) -> pathlib .Path | None :
393
405
"""Get shared library location in build directory"""
394
406
if modinfo .state == ModuleState .SHARED :
395
407
return self .builddir / f"{ modinfo .name } { self .ext_suffix } "
396
408
else :
397
409
return None
398
410
399
- def _check_file (self , modinfo : ModuleInfo , spec : ModuleSpec ):
411
+ def _check_file (self , modinfo : ModuleInfo , spec : ModuleSpec ) -> None :
400
412
"""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]
402
414
return
403
415
try :
416
+ assert spec .origin is not None
404
417
st = os .stat (spec .origin )
405
418
except FileNotFoundError :
406
419
logger .error ("%s (%s) is missing" , modinfo .name , spec .origin )
407
420
raise
408
421
if not st .st_size :
409
422
raise ImportError (f"{ spec .origin } is an empty file" )
410
423
411
- def check_module_import (self , modinfo : ModuleInfo ):
424
+ def check_module_import (self , modinfo : ModuleInfo ) -> None :
412
425
"""Attempt to import module and report errors"""
413
426
spec = self .get_spec (modinfo )
414
427
self ._check_file (modinfo , spec )
@@ -427,7 +440,7 @@ def check_module_import(self, modinfo: ModuleInfo):
427
440
logger .exception ("Importing extension '%s' failed!" , modinfo .name )
428
441
raise
429
442
430
- def check_module_cross (self , modinfo : ModuleInfo ):
443
+ def check_module_cross (self , modinfo : ModuleInfo ) -> None :
431
444
"""Sanity check for cross compiling"""
432
445
spec = self .get_spec (modinfo )
433
446
self ._check_file (modinfo , spec )
@@ -440,6 +453,7 @@ def rename_module(self, modinfo: ModuleInfo) -> None:
440
453
441
454
failed_name = f"{ modinfo .name } _failed{ self .ext_suffix } "
442
455
builddir_path = self .get_location (modinfo )
456
+ assert builddir_path is not None
443
457
if builddir_path .is_symlink ():
444
458
symlink = builddir_path
445
459
module_path = builddir_path .resolve ().relative_to (os .getcwd ())
@@ -463,7 +477,7 @@ def rename_module(self, modinfo: ModuleInfo) -> None:
463
477
logger .debug ("Rename '%s' -> '%s'" , module_path , failed_path )
464
478
465
479
466
- def main ():
480
+ def main () -> None :
467
481
args = parser .parse_args ()
468
482
if args .debug :
469
483
args .verbose = True
0 commit comments