428 lines
14 KiB
Python
428 lines
14 KiB
Python
from import_all_modules_py3_12 import argparser, exclude_unwanted_module_globs
|
|
from import_all_modules_py3_12 import main as modules_main
|
|
from import_all_modules_py3_12 import read_modules_from_cli, filter_top_level_modules_only
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import shlex
|
|
import sys
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def preserve_sys_path():
|
|
original_sys_path = list(sys.path)
|
|
yield
|
|
sys.path = original_sys_path
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def preserve_sys_modules():
|
|
original_sys_modules = dict(sys.modules)
|
|
yield
|
|
sys.modules = original_sys_modules
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'args, imports',
|
|
[
|
|
('six', ['six']),
|
|
('five six seven', ['five', 'six', 'seven']),
|
|
('six,seven, eight', ['six', 'seven', 'eight']),
|
|
('six.quarter six.half,, SIX', ['six.quarter', 'six.half', 'SIX']),
|
|
('six.quarter six.half,, SIX \\ ', ['six.quarter', 'six.half', 'SIX']),
|
|
]
|
|
)
|
|
def test_read_modules_from_cli(args, imports):
|
|
argv = shlex.split(args)
|
|
cli_args = argparser().parse_args(argv)
|
|
assert read_modules_from_cli(cli_args.modules) == imports
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'all_mods, imports',
|
|
[
|
|
(['six'], ['six']),
|
|
(['five', 'six', 'seven'], ['five', 'six', 'seven']),
|
|
(['six.seven', 'eight'], ['eight']),
|
|
(['SIX', 'six.quarter', 'six.half.and.sth', 'seven'], ['SIX', 'seven']),
|
|
],
|
|
)
|
|
def test_filter_top_level_modules_only(all_mods, imports):
|
|
assert filter_top_level_modules_only(all_mods) == imports
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'globs, expected',
|
|
[
|
|
(['*.*'], ['foo', 'boo']),
|
|
(['?oo'], ['foo.bar', 'foo.bar.baz', 'foo.baz']),
|
|
(['*.baz'], ['foo', 'foo.bar', 'boo']),
|
|
(['foo'], ['foo.bar', 'foo.bar.baz', 'foo.baz', 'boo']),
|
|
(['foo*'], ['boo']),
|
|
(['foo*', '*bar'], ['boo']),
|
|
(['foo', 'bar'], ['foo.bar', 'foo.bar.baz', 'foo.baz', 'boo']),
|
|
(['*'], []),
|
|
]
|
|
)
|
|
def test_exclude_unwanted_module_globs(globs, expected):
|
|
my_modules = ['foo', 'foo.bar', 'foo.bar.baz', 'foo.baz', 'boo']
|
|
tested = exclude_unwanted_module_globs(globs, my_modules)
|
|
assert tested == expected
|
|
|
|
|
|
def test_cli_with_all_args():
|
|
'''A smoke test, all args must be parsed correctly.'''
|
|
mods = ['foo', 'foo.bar', 'baz']
|
|
files = ['-f', './foo']
|
|
top = ['-t']
|
|
exclude = ['-e', 'foo*']
|
|
cli_args = argparser().parse_args([*mods, *files, *top, *exclude])
|
|
|
|
assert cli_args.filename == [Path('foo')]
|
|
assert cli_args.top_level is True
|
|
assert cli_args.modules == ['foo', 'foo.bar', 'baz']
|
|
assert cli_args.exclude == ['foo*']
|
|
|
|
|
|
def test_cli_without_filename_toplevel():
|
|
'''Modules provided on command line (without files) must be parsed correctly.'''
|
|
mods = ['foo', 'foo.bar', 'baz']
|
|
cli_args = argparser().parse_args(mods)
|
|
|
|
assert cli_args.filename is None
|
|
assert cli_args.top_level is False
|
|
assert cli_args.modules == ['foo', 'foo.bar', 'baz']
|
|
|
|
|
|
def test_cli_with_filename_no_cli_mods():
|
|
'''Files (without any modules provided on command line) must be parsed correctly.'''
|
|
|
|
files = ['-f', './foo', '-f', './bar', '-f', './baz']
|
|
cli_args = argparser().parse_args(files)
|
|
|
|
assert cli_args.filename == [Path('foo'), Path('./bar'), Path('./baz')]
|
|
assert not cli_args.top_level
|
|
|
|
|
|
def test_main_raises_error_when_no_modules_provided():
|
|
'''If no filename nor modules were provided, ValueError is raised.'''
|
|
|
|
with pytest.raises(ValueError):
|
|
modules_main([])
|
|
|
|
|
|
def test_import_all_modules_does_not_import():
|
|
'''Ensure the files from /usr/lib/rpm/redhat cannot be imported and
|
|
checked for import'''
|
|
|
|
# We already imported it in this file once, make sure it's not imported
|
|
# from the cache
|
|
sys.modules.pop('import_all_modules_py3_12')
|
|
with pytest.raises(ModuleNotFoundError):
|
|
modules_main(['import_all_modules_py3_12'])
|
|
|
|
|
|
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):
|
|
modules_main(['this_is_a_module_in_cwd'])
|
|
|
|
|
|
def test_modules_from_sys_path_found(tmp_path):
|
|
test_module = tmp_path / 'this_is_a_module_in_sys_path.py'
|
|
test_module.write_text('')
|
|
sys.path.append(str(tmp_path))
|
|
modules_main(['this_is_a_module_in_sys_path'])
|
|
assert 'this_is_a_module_in_sys_path' in sys.modules
|
|
|
|
|
|
def test_modules_from_file_are_found(tmp_path):
|
|
test_file = tmp_path / 'this_is_a_file_in_tmp_path.txt'
|
|
test_file.write_text('math\nwave\ncsv\n')
|
|
|
|
# Make sure the tested modules are not already in sys.modules
|
|
for m in ('math', 'wave', 'csv'):
|
|
sys.modules.pop(m, None)
|
|
|
|
modules_main(['-f', str(test_file)])
|
|
|
|
assert 'csv' in sys.modules
|
|
assert 'math' in sys.modules
|
|
assert 'wave' in sys.modules
|
|
|
|
|
|
def test_modules_from_files_are_found(tmp_path):
|
|
test_file_1 = tmp_path / 'this_is_a_file_in_tmp_path_1.txt'
|
|
test_file_2 = tmp_path / 'this_is_a_file_in_tmp_path_2.txt'
|
|
test_file_3 = tmp_path / 'this_is_a_file_in_tmp_path_3.txt'
|
|
|
|
test_file_1.write_text('math\nwave\n')
|
|
test_file_2.write_text('csv\npathlib\n')
|
|
test_file_3.write_text('logging\ncsv\n')
|
|
|
|
# Make sure the tested modules are not already in sys.modules
|
|
for m in ('math', 'wave', 'csv', 'pathlib', 'logging'):
|
|
sys.modules.pop(m, None)
|
|
|
|
modules_main(['-f', str(test_file_1), '-f', str(test_file_2), '-f', str(test_file_3), ])
|
|
for module in ('csv', 'math', 'wave', 'pathlib', 'logging'):
|
|
assert module in sys.modules
|
|
|
|
|
|
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):
|
|
modules_main(['-f', str(test_file)])
|
|
|
|
|
|
def test_nested_modules_found_when_expected(tmp_path, monkeypatch, capsys):
|
|
|
|
# This one is supposed to raise an error
|
|
cwd_path = tmp_path / 'test_cwd'
|
|
Path.mkdir(cwd_path)
|
|
test_module_1 = cwd_path / 'this_is_a_module_in_cwd.py'
|
|
|
|
# Nested structure that is supposed to be importable
|
|
nested_path_1 = tmp_path / 'nested'
|
|
nested_path_2 = nested_path_1 / 'more_nested'
|
|
|
|
for path in (nested_path_1, nested_path_2):
|
|
Path.mkdir(path)
|
|
|
|
test_module_2 = tmp_path / 'this_is_a_module_in_level_0.py'
|
|
test_module_3 = nested_path_1 / 'this_is_a_module_in_level_1.py'
|
|
test_module_4 = nested_path_2 / 'this_is_a_module_in_level_2.py'
|
|
|
|
for module in (test_module_1, test_module_2, test_module_3, test_module_4):
|
|
module.write_text('')
|
|
|
|
sys.path.append(str(tmp_path))
|
|
monkeypatch.chdir(cwd_path)
|
|
|
|
with pytest.raises(ModuleNotFoundError):
|
|
modules_main([
|
|
'this_is_a_module_in_level_0',
|
|
'nested.this_is_a_module_in_level_1',
|
|
'nested.more_nested.this_is_a_module_in_level_2',
|
|
'this_is_a_module_in_cwd'])
|
|
|
|
_, err = capsys.readouterr()
|
|
assert 'Check import: this_is_a_module_in_level_0' in err
|
|
assert 'Check import: nested.this_is_a_module_in_level_1' in err
|
|
assert 'Check import: nested.more_nested.this_is_a_module_in_level_2' in err
|
|
assert 'Check import: this_is_a_module_in_cwd' in err
|
|
|
|
|
|
def test_modules_both_from_files_and_cli_are_imported(tmp_path):
|
|
test_file_1 = tmp_path / 'this_is_a_file_in_tmp_path_1.txt'
|
|
test_file_1.write_text('this_is_a_module_in_tmp_path_1')
|
|
|
|
test_file_2 = tmp_path / 'this_is_a_file_in_tmp_path_2.txt'
|
|
test_file_2.write_text('this_is_a_module_in_tmp_path_2')
|
|
|
|
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))
|
|
modules_main([
|
|
'-f', str(test_file_1),
|
|
'this_is_a_module_in_tmp_path_3',
|
|
'-f', str(test_file_2),
|
|
])
|
|
|
|
expected = (
|
|
'this_is_a_module_in_tmp_path_1',
|
|
'this_is_a_module_in_tmp_path_2',
|
|
'this_is_a_module_in_tmp_path_3',
|
|
)
|
|
for module in expected:
|
|
assert module in sys.modules
|
|
|
|
|
|
def test_non_existing_module_raises_exception(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))
|
|
|
|
with pytest.raises(ModuleNotFoundError):
|
|
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):
|
|
|
|
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):
|
|
modules_main([
|
|
'this_is_a_module_in_tmp_path_1',
|
|
])
|
|
|
|
|
|
def test_correct_modules_are_excluded(tmp_path):
|
|
test_module_1 = tmp_path / 'module_in_tmp_path_1.py'
|
|
test_module_2 = tmp_path / 'module_in_tmp_path_2.py'
|
|
test_module_3 = tmp_path / '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))
|
|
test_file_1 = tmp_path / 'a_file_in_tmp_path_1.txt'
|
|
test_file_1.write_text('module_in_tmp_path_1\nmodule_in_tmp_path_2\nmodule_in_tmp_path_3\n')
|
|
|
|
modules_main([
|
|
'-e', 'module_in_tmp_path_2',
|
|
'-f', str(test_file_1),
|
|
'-e', 'module_in_tmp_path_3',
|
|
])
|
|
|
|
assert 'module_in_tmp_path_1' in sys.modules
|
|
assert 'module_in_tmp_path_2' not in sys.modules
|
|
assert 'module_in_tmp_path_3' not in sys.modules
|
|
|
|
|
|
def test_excluding_all_modules_raises_error(tmp_path):
|
|
test_module_1 = tmp_path / 'module_in_tmp_path_1.py'
|
|
test_module_2 = tmp_path / 'module_in_tmp_path_2.py'
|
|
test_module_3 = tmp_path / '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))
|
|
test_file_1 = tmp_path / 'a_file_in_tmp_path_1.txt'
|
|
test_file_1.write_text('module_in_tmp_path_1\nmodule_in_tmp_path_2\nmodule_in_tmp_path_3\n')
|
|
|
|
with pytest.raises(ValueError):
|
|
modules_main([
|
|
'-e', 'module_in_tmp_path*',
|
|
'-f', str(test_file_1),
|
|
])
|
|
|
|
|
|
def test_only_toplevel_modules_found(tmp_path):
|
|
|
|
# Nested structure that is supposed to be importable
|
|
nested_path_1 = tmp_path / 'nested'
|
|
nested_path_2 = nested_path_1 / 'more_nested'
|
|
|
|
for path in (nested_path_1, nested_path_2):
|
|
Path.mkdir(path)
|
|
|
|
test_module_1 = tmp_path / 'this_is_a_module_in_level_0.py'
|
|
test_module_2 = nested_path_1 / 'this_is_a_module_in_level_1.py'
|
|
test_module_3 = nested_path_2 / 'this_is_a_module_in_level_2.py'
|
|
|
|
for module in (test_module_1, test_module_2, test_module_3):
|
|
module.write_text('')
|
|
|
|
sys.path.append(str(tmp_path))
|
|
|
|
modules_main([
|
|
'this_is_a_module_in_level_0',
|
|
'nested.this_is_a_module_in_level_1',
|
|
'nested.more_nested.this_is_a_module_in_level_2',
|
|
'-t'])
|
|
|
|
assert 'nested.this_is_a_module_in_level_1' not in sys.modules
|
|
assert 'nested.more_nested.this_is_a_module_in_level_2' not in sys.modules
|
|
|
|
|
|
def test_only_toplevel_included_modules_found(tmp_path):
|
|
|
|
# Nested structure that is supposed to be importable
|
|
nested_path_1 = tmp_path / 'nested'
|
|
nested_path_2 = nested_path_1 / 'more_nested'
|
|
|
|
for path in (nested_path_1, nested_path_2):
|
|
Path.mkdir(path)
|
|
|
|
test_module_1 = tmp_path / 'this_is_a_module_in_level_0.py'
|
|
test_module_4 = tmp_path / 'this_is_another_module_in_level_0.py'
|
|
|
|
test_module_2 = nested_path_1 / 'this_is_a_module_in_level_1.py'
|
|
test_module_3 = nested_path_2 / 'this_is_a_module_in_level_2.py'
|
|
|
|
for module in (test_module_1, test_module_2, test_module_3, test_module_4):
|
|
module.write_text('')
|
|
|
|
sys.path.append(str(tmp_path))
|
|
|
|
modules_main([
|
|
'this_is_a_module_in_level_0',
|
|
'this_is_another_module_in_level_0',
|
|
'nested.this_is_a_module_in_level_1',
|
|
'nested.more_nested.this_is_a_module_in_level_2',
|
|
'-t',
|
|
'-e', '*another*'
|
|
])
|
|
|
|
assert 'nested.this_is_a_module_in_level_1' not in sys.modules
|
|
assert 'nested.more_nested.this_is_a_module_in_level_2' not in sys.modules
|
|
assert 'this_is_another_module_in_level_0' not in sys.modules
|
|
assert 'this_is_a_module_in_level_0' in sys.modules
|
|
|
|
|
|
def test_module_list_from_relative_path(tmp_path, monkeypatch):
|
|
|
|
monkeypatch.chdir(tmp_path)
|
|
test_file_1 = Path('this_is_a_file_in_cwd.txt')
|
|
test_file_1.write_text('wave')
|
|
|
|
sys.modules.pop('wave', None)
|
|
|
|
modules_main([
|
|
'-f', 'this_is_a_file_in_cwd.txt'
|
|
])
|
|
|
|
assert 'wave' in sys.modules
|
|
|
|
|
|
@pytest.mark.parametrize('arch_in_path', [True, False])
|
|
def test_pth_files_are_read_from__PYTHONSITE(arch_in_path, tmp_path, monkeypatch, capsys):
|
|
sitearch = tmp_path / 'lib64'
|
|
sitearch.mkdir()
|
|
sitelib = tmp_path / 'lib'
|
|
sitelib.mkdir()
|
|
|
|
for where, word in (sitearch, "ARCH"), (sitelib, "LIB"), (sitelib, "MOD"):
|
|
module = where / f'print{word}.py'
|
|
module.write_text(f'print("{word}")')
|
|
|
|
pth_sitearch = sitearch / 'ARCH.pth'
|
|
pth_sitearch.write_text('import printARCH\n')
|
|
|
|
pth_sitelib = sitelib / 'LIB.pth'
|
|
pth_sitelib.write_text('import printLIB\n')
|
|
|
|
if arch_in_path:
|
|
sys.path.append(str(sitearch))
|
|
sys.path.append(str(sitelib))
|
|
|
|
# we always add sitearch to _PYTHONSITE
|
|
# but when not in sys.path, it should not be processed for .pth files
|
|
monkeypatch.setenv('_PYTHONSITE', f'{sitearch}:{sitelib}')
|
|
|
|
modules_main(['printMOD'])
|
|
out, err = capsys.readouterr()
|
|
if arch_in_path:
|
|
assert out == 'ARCH\nLIB\nMOD\n'
|
|
else:
|
|
assert out == 'LIB\nMOD\n'
|
|
|