import_all_modules: Add error handling for import failures

Continue checking all modules when imports fail and provide detailed
error reporting. Exit with proper status code when failures occur.
This commit is contained in:
Lumir Balhar 2025-08-05 20:16:56 +02:00
commit 364d99f4e1
3 changed files with 78 additions and 12 deletions

View file

@ -7,6 +7,7 @@ import os
import re
import site
import sys
import traceback
from contextlib import contextmanager
from pathlib import Path
@ -93,11 +94,24 @@ def read_modules_from_all_args(args):
def import_modules(modules):
'''Procedure to perform import check for each module name from the given list of modules.
Return a list of failed modules.
'''
failed_modules = []
for module in modules:
print('Check import:', module, file=sys.stderr)
importlib.import_module(module)
try:
importlib.import_module(module)
except Exception:
traceback.print_exc(file=sys.stderr)
failed_modules.append(module)
if failed_modules:
print(f'Failed to import: {", ".join(failed_modules)}', file=sys.stderr)
return failed_modules
def argparser():
@ -164,7 +178,10 @@ def main(argv=None):
with remove_unwanteds_from_sys_path():
addsitedirs_from_environ()
import_modules(modules)
failed_modules = import_modules(modules)
if failed_modules:
raise SystemExit(1)
if __name__ == '__main__':

View file

@ -55,7 +55,7 @@ elseif posix.stat('macros.python-srpm') then
end
}
Version: %{__default_python3_version}
Release: 4%{?dist}
Release: 5%{?dist}
BuildArch: noarch
@ -167,6 +167,9 @@ grep -E '^#[^%%]*%%[^%%]' %{buildroot}%{rpmmacrodir}/macros.* && exit 1 || true
%changelog
* Mon Aug 11 2025 Lumír Balhar <lbalhar@redhat.com> - 3.14-5
- import_all_modules: Add error handling for import failures
* Fri Jul 25 2025 Fedora Release Engineering <releng@fedoraproject.org>
- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild

View file

@ -1,4 +1,4 @@
from import_all_modules import argparser, exclude_unwanted_module_globs
from import_all_modules import argparser, exclude_unwanted_module_globs, import_modules
from import_all_modules import main as modules_main
from import_all_modules import read_modules_from_cli, filter_top_level_modules_only
@ -119,7 +119,7 @@ def test_import_all_modules_does_not_import():
# We already imported it in this file once, make sure it's not imported
# from the cache
sys.modules.pop('import_all_modules')
with pytest.raises(ModuleNotFoundError):
with pytest.raises(SystemExit):
modules_main(['import_all_modules'])
@ -127,7 +127,7 @@ def test_modules_from_cwd_not_found(tmp_path, monkeypatch):
test_module = tmp_path / 'this_is_a_module_in_cwd.py'
test_module.write_text('')
monkeypatch.chdir(tmp_path)
with pytest.raises(ModuleNotFoundError):
with pytest.raises(SystemExit):
modules_main(['this_is_a_module_in_cwd'])
@ -175,7 +175,7 @@ def test_modules_from_files_are_found(tmp_path):
def test_nonexisting_modules_raise_exception_on_import(tmp_path):
test_file = tmp_path / 'this_is_a_file_in_tmp_path.txt'
test_file.write_text('nonexisting_module\nanother\n')
with pytest.raises(ModuleNotFoundError):
with pytest.raises(SystemExit):
modules_main(['-f', str(test_file)])
@ -203,7 +203,7 @@ def test_nested_modules_found_when_expected(tmp_path, monkeypatch, capsys):
sys.path.append(str(tmp_path))
monkeypatch.chdir(cwd_path)
with pytest.raises(ModuleNotFoundError):
with pytest.raises(SystemExit):
modules_main([
'this_is_a_module_in_level_0',
'nested.this_is_a_module_in_level_1',
@ -253,24 +253,70 @@ def test_non_existing_module_raises_exception(tmp_path):
test_module_1.write_text('')
sys.path.append(str(tmp_path))
with pytest.raises(ModuleNotFoundError):
with pytest.raises(SystemExit):
modules_main([
'this_is_a_module_in_tmp_path_1',
'this_is_a_module_in_tmp_path_2',
])
def test_module_with_error_propagates_exception(tmp_path):
def test_import_module_returns_failed_modules(tmp_path):
test_module_1 = tmp_path / 'this_is_a_module_in_tmp_path_1.py'
test_module_1.write_text('')
sys.path.append(str(tmp_path))
failed_modules = import_modules([
'this_is_a_module_in_tmp_path_1',
'this_is_a_module_in_tmp_path_2',
])
assert failed_modules == ['this_is_a_module_in_tmp_path_2']
def test_module_with_error_propagates_exception(tmp_path, capsys):
test_module_1 = tmp_path / 'this_is_a_module_in_tmp_path_1.py'
test_module_1.write_text('0/0')
sys.path.append(str(tmp_path))
# The correct exception must be raised
with pytest.raises(ZeroDivisionError):
with pytest.raises(SystemExit):
modules_main([
'this_is_a_module_in_tmp_path_1',
])
_, err = capsys.readouterr()
assert "ZeroDivisionError" in err
def test_import_module_returns_empty_list_when_no_modules_failed(tmp_path):
test_module_1 = tmp_path / 'this_is_a_module_in_tmp_path_1.py'
test_module_1.write_text('')
sys.path.append(str(tmp_path))
failed_modules = import_modules(['this_is_a_module_in_tmp_path_1'])
assert failed_modules == []
def test_all_modules_are_imported(tmp_path, capsys):
test_module_1 = tmp_path / 'this_is_a_module_in_tmp_path_1.py'
test_module_2 = tmp_path / 'this_is_a_module_in_tmp_path_2.py'
test_module_3 = tmp_path / 'this_is_a_module_in_tmp_path_3.py'
for module in (test_module_1, test_module_2, test_module_3):
module.write_text('')
sys.path.append(str(tmp_path))
with pytest.raises(SystemExit):
modules_main([
'this_is_a_module_in_tmp_path_1',
'missing_module',
'this_is_a_module_in_tmp_path_2',
'this_is_a_module_in_tmp_path_3',
])
_, err = capsys.readouterr()
for i in range(1, 4):
assert f"Check import: this_is_a_module_in_tmp_path_{i}" in err
assert "Failed to import: missing_module" in err
def test_correct_modules_are_excluded(tmp_path):