Cover unit tests with mypy (#84084)

* Added support for testing unit tests with mypy.
* Added support for ignoring individual mypy error codes.
* Added missing assert on unit tests and marked xfail.
* Added type hints for some unit tests.
* Added ignores for unit tests not passing mypy.
* Fixed incorrect autouse argument in unit test fixtures.
* Fixed minor issues causing problems with mypy in unit tests.
pull/84087/head
Matt Clay 2 months ago committed by GitHub
parent 18c6b40e19
commit 955e310b4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -3,11 +3,13 @@
"lib/ansible/",
"test/lib/ansible_test/_internal/",
"packaging/",
"test/units",
"test/lib/ansible_test/_util/target/sanity/import/"
],
"extensions": [
".py"
],
"multi_version": "controller",
"error_code": "ansible-test",
"output": "path-line-column-code-message"
}

@ -36,6 +36,8 @@ def main() -> None:
MyPyContext('ansible-core', ['lib/ansible/'], controller_python_versions),
MyPyContext('modules', ['lib/ansible/modules/', 'lib/ansible/module_utils/'], remote_only_python_versions),
MyPyContext('packaging', ['packaging/'], controller_python_versions),
MyPyContext('modules', ['test/units/modules/', 'test/units/module_utils/'], remote_only_python_versions),
MyPyContext('ansible-core', ['test/units/'], controller_python_versions),
)
unfiltered_messages: list[SanityMessage] = []

@ -2,6 +2,7 @@ mypy
cryptography # type stubs not published separately
jinja2 # type stubs not published separately
packaging # type stubs not published separately
pytest # type stubs not published separately
tomli # type stubs not published separately, required for toml inventory plugin
types-backports
types-paramiko

@ -1,12 +1,15 @@
# edit "mypy.requirements.in" and generate with: hacking/update-sanity-requirements.py --test mypy
cffi==1.17.1
cryptography==43.0.1
iniconfig==2.0.0
Jinja2==3.1.4
MarkupSafe==2.1.5
mypy==1.11.2
mypy-extensions==1.0.0
packaging==24.1
pluggy==1.5.0
pycparser==2.22
pytest==8.3.3
tomli==2.0.2
types-backports==0.1.3
types-paramiko==3.5.0.20240928

@ -11,6 +11,16 @@ strict_optional = False
# The safe-super rule is disabled because it reports false positives on methods which return None.
disable_error_code = attr-defined,safe-super
# Some controller unit tests use ansible_collections imports, both real and test-specific.
# The real imports are not currently visible to mypy.
# There's little point in exposing the test-specific imports.
[mypy-ansible_collections.*]
ignore_missing_imports = True
# Some unit tests for ansible-test use ansible_test imports, which are not currently visible to mypy.
[mypy-ansible_test.*]
ignore_missing_imports = True
[mypy-ansible.module_utils.six.moves.*]
ignore_missing_imports = True

@ -164,3 +164,72 @@ lib/ansible/module_utils/pycompat24.py pylint:ansible-deprecated-version
lib/ansible/plugins/connection/__init__.py pylint:ansible-deprecated-version
lib/ansible/plugins/filter/core.py pylint:ansible-deprecated-version
lib/ansible/vars/manager.py pylint:ansible-deprecated-version
test/units/module_utils/basic/test_exit_json.py mypy-3.13:assignment
test/units/module_utils/basic/test_exit_json.py mypy-3.13:misc
test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.13:abstract
test/units/module_utils/facts/other/test_facter.py mypy-3.13:assignment
test/units/module_utils/facts/other/test_ohai.py mypy-3.13:assignment
test/units/module_utils/facts/system/test_lsb.py mypy-3.13:assignment
test/units/module_utils/facts/test_collectors.py mypy-3.13:assignment
test/units/module_utils/facts/test_facts.py mypy-3.13:assignment
test/units/modules/mount_facts_data.py mypy-3.13:arg-type
test/units/modules/test_apt.py mypy-3.13:name-match
test/units/modules/test_mount_facts.py mypy-3.13:index
test/units/playbook/test_base.py mypy-3.13:assignment
test/units/module_utils/basic/test_exit_json.py mypy-3.12:assignment
test/units/module_utils/basic/test_exit_json.py mypy-3.12:misc
test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.12:abstract
test/units/module_utils/facts/other/test_facter.py mypy-3.12:assignment
test/units/module_utils/facts/other/test_ohai.py mypy-3.12:assignment
test/units/module_utils/facts/system/test_lsb.py mypy-3.12:assignment
test/units/module_utils/facts/test_collectors.py mypy-3.12:assignment
test/units/module_utils/facts/test_facts.py mypy-3.12:assignment
test/units/modules/mount_facts_data.py mypy-3.12:arg-type
test/units/modules/test_apt.py mypy-3.12:name-match
test/units/modules/test_mount_facts.py mypy-3.12:index
test/units/playbook/test_base.py mypy-3.12:assignment
test/units/module_utils/basic/test_exit_json.py mypy-3.11:assignment
test/units/module_utils/basic/test_exit_json.py mypy-3.11:misc
test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.11:abstract
test/units/module_utils/facts/other/test_facter.py mypy-3.11:assignment
test/units/module_utils/facts/other/test_ohai.py mypy-3.11:assignment
test/units/module_utils/facts/system/test_lsb.py mypy-3.11:assignment
test/units/module_utils/facts/test_collectors.py mypy-3.11:assignment
test/units/module_utils/facts/test_facts.py mypy-3.11:assignment
test/units/modules/mount_facts_data.py mypy-3.11:arg-type
test/units/modules/test_apt.py mypy-3.11:name-match
test/units/modules/test_mount_facts.py mypy-3.11:index
test/units/playbook/test_base.py mypy-3.11:assignment
test/units/module_utils/basic/test_exit_json.py mypy-3.10:assignment
test/units/module_utils/basic/test_exit_json.py mypy-3.10:misc
test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.10:abstract
test/units/module_utils/facts/other/test_facter.py mypy-3.10:assignment
test/units/module_utils/facts/other/test_ohai.py mypy-3.10:assignment
test/units/module_utils/facts/system/test_lsb.py mypy-3.10:assignment
test/units/module_utils/facts/test_collectors.py mypy-3.10:assignment
test/units/module_utils/facts/test_facts.py mypy-3.10:assignment
test/units/modules/mount_facts_data.py mypy-3.10:arg-type
test/units/modules/test_apt.py mypy-3.10:name-match
test/units/modules/test_mount_facts.py mypy-3.10:index
test/units/module_utils/basic/test_exit_json.py mypy-3.9:assignment
test/units/module_utils/basic/test_exit_json.py mypy-3.9:misc
test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.9:abstract
test/units/module_utils/facts/other/test_facter.py mypy-3.9:assignment
test/units/module_utils/facts/other/test_ohai.py mypy-3.9:assignment
test/units/module_utils/facts/system/test_lsb.py mypy-3.9:assignment
test/units/module_utils/facts/test_collectors.py mypy-3.9:assignment
test/units/module_utils/facts/test_facts.py mypy-3.9:assignment
test/units/modules/mount_facts_data.py mypy-3.9:arg-type
test/units/modules/test_apt.py mypy-3.9:name-match
test/units/modules/test_mount_facts.py mypy-3.9:index
test/units/module_utils/basic/test_exit_json.py mypy-3.8:assignment
test/units/module_utils/basic/test_exit_json.py mypy-3.8:misc
test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.8:abstract
test/units/module_utils/facts/other/test_facter.py mypy-3.8:assignment
test/units/module_utils/facts/other/test_ohai.py mypy-3.8:assignment
test/units/module_utils/facts/system/test_lsb.py mypy-3.8:assignment
test/units/module_utils/facts/test_collectors.py mypy-3.8:assignment
test/units/module_utils/facts/test_facts.py mypy-3.8:assignment
test/units/modules/mount_facts_data.py mypy-3.8:arg-type
test/units/modules/test_apt.py mypy-3.8:name-match
test/units/modules/test_mount_facts.py mypy-3.8:index

@ -45,7 +45,7 @@ import unittest
from unittest.mock import patch, MagicMock
@pytest.fixture(autouse='function')
@pytest.fixture(autouse=True)
def reset_cli_args():
co.GlobalCLIArgs._Singleton__instance = None
yield

@ -35,7 +35,7 @@ from ansible.utils import context_objects as co
# mock calls
@pytest.fixture(autouse='function')
@pytest.fixture(autouse=True)
def reset_cli_args():
co.GlobalCLIArgs._Singleton__instance = None
yield

@ -29,7 +29,7 @@ from ansible.utils import context_objects as co
from ansible.utils.display import Display
@pytest.fixture(autouse='function')
@pytest.fixture(autouse=True)
def reset_cli_args():
co.GlobalCLIArgs._Singleton__instance = None
# Required to initialise the GalaxyAPI object

@ -31,7 +31,7 @@ from ansible.utils.display import Display
from ansible.utils.hashing import secure_hash_s
@pytest.fixture(autouse='function')
@pytest.fixture(autouse=True)
def reset_cli_args():
co.GlobalCLIArgs._Singleton__instance = None
yield

@ -53,7 +53,7 @@ def call_galaxy_cli(args):
co.GlobalCLIArgs._Singleton__instance = orig
@pytest.fixture(autouse='function')
@pytest.fixture(autouse=True)
def reset_cli_args():
co.GlobalCLIArgs._Singleton__instance = None
yield
@ -992,9 +992,7 @@ def test_install_collection_with_no_dependency(collection_artifact, monkeypatch)
(["good_signature", "good_signature"], '2', [], True),
]
)
def test_verify_file_signatures(signatures, required_successful_count, ignore_errors, expected_success):
# type: (List[bool], int, bool, bool) -> None
def test_verify_file_signatures(signatures: list[str], required_successful_count: str, ignore_errors: list[str], expected_success: bool) -> None:
def gpg_error_generator(results):
for result in results:
if isinstance(result, collection.gpg.GpgBaseError):

@ -28,7 +28,7 @@ def call_galaxy_cli(args):
co.GlobalCLIArgs._Singleton__instance = orig
@pytest.fixture(autouse='function')
@pytest.fixture(autouse=True)
def reset_cli_args():
co.GlobalCLIArgs._Singleton__instance = None
yield

@ -8,6 +8,7 @@ from __future__ import annotations
import json
import os
import typing as t
import pytest
@ -69,7 +70,7 @@ VALID_SPECS = (
({'arg': {'type': 'int'}}, {'arg': 1, 'invalid': True, '_ansible_ignore_unknown_opts': True}, 1),
)
INVALID_SPECS = (
INVALID_SPECS: tuple[tuple[dict[str, t.Any], dict[str, t.Any], str], ...] = (
# Type is int; unable to convert this string
({'arg': {'type': 'int'}}, {'arg': "wolf"}, f"is of type {type('wolf')} and we were unable to convert to int:"),
# Type is list elements is int; unable to convert this string
@ -495,7 +496,7 @@ class TestComplexOptions:
)
# (Parameters, failure message)
FAILING_PARAMS_LIST = (
FAILING_PARAMS_LIST: tuple[tuple[dict[str, list[dict[str, t.Any]]], str], ...] = (
# Missing required option
({'foobar': [{}]}, 'missing required arguments: foo found in foobar'),
# Invalid option
@ -518,7 +519,7 @@ class TestComplexOptions:
)
# (Parameters, failure message)
FAILING_PARAMS_DICT = (
FAILING_PARAMS_DICT: tuple[tuple[dict[str, dict[str, t.Any]], str], ...] = (
# Missing required option
({'foobar': {}}, 'missing required arguments: foo found in foobar'),
# Invalid option

@ -7,12 +7,13 @@ from __future__ import annotations
import json
import sys
import datetime
import typing as t
import pytest
from ansible.module_utils.common import warnings
EMPTY_INVOCATION = {u'module_args': {}}
EMPTY_INVOCATION: dict[str, dict[str, t.Any]] = {u'module_args': {}}
DATETIME = datetime.datetime.strptime('2020-07-13 12:50:00', '%Y-%m-%d %H:%M:%S')
@ -20,7 +21,7 @@ class TestAnsibleModuleExitJson:
"""
Test that various means of calling exitJson and FailJson return the messages they've been given
"""
DATA = (
DATA: tuple[tuple[dict[str, t.Any]], ...] = (
({}, {'invocation': EMPTY_INVOCATION}),
({'msg': 'message'}, {'msg': 'message', 'invocation': EMPTY_INVOCATION}),
({'msg': 'success', 'changed': True},

@ -5,6 +5,7 @@
from __future__ import annotations
import typing as t
import unittest
from ansible.module_utils.basic import remove_values
@ -12,7 +13,7 @@ from ansible.module_utils.common.parameters import _return_datastructure_name
class TestReturnValues(unittest.TestCase):
dataset = (
dataset: tuple[tuple[t.Any, frozenset[str]], ...] = (
('string', frozenset(['string'])),
('', frozenset()),
(1, frozenset(['1'])),

@ -5,11 +5,14 @@
from __future__ import annotations
from itertools import chain
import typing as t
import pytest
# Strings that should be converted into a typed value
VALID_STRINGS = (
VALID_STRINGS: tuple[tuple[str, t.Any], ...] = (
("'a'", 'a'),
("'1'", '1'),
("1", 1),

@ -67,7 +67,7 @@ ALIAS_TEST_CASES = [
# id, argument spec, parameters, expected parameters, error
ALIAS_TEST_CASES_INVALID = [
ALIAS_TEST_CASES_INVALID: list[tuple[str, dict, dict, dict, str]] = [
(
"alias-invalid",
{'path': {'aliases': 'bad'}},

@ -39,16 +39,14 @@ class FakeAnsibleVaultEncryptedUnicode(Sequence):
TEST_STRINGS = u'he', u'Україна', u'Česká republika'
TEST_STRINGS = TEST_STRINGS + tuple(s.encode('utf-8') for s in TEST_STRINGS) + (FakeAnsibleVaultEncryptedUnicode(u'foo'),)
TEST_ITEMS_NON_SEQUENCES = (
TEST_ITEMS_NON_SEQUENCES: tuple = (
{}, object(), frozenset(),
4, 0.,
) + TEST_STRINGS
TEST_ITEMS_SEQUENCES = (
TEST_ITEMS_SEQUENCES: tuple = (
[], (),
SeqStub(),
)
TEST_ITEMS_SEQUENCES = TEST_ITEMS_SEQUENCES + (
# Iterable effectively containing nested random data:
TEST_ITEMS_NON_SEQUENCES,
)

@ -29,8 +29,6 @@ from . linux_data import LSBLK_OUTPUT, LSBLK_OUTPUT_2, LSBLK_UUIDS, MTAB, MTAB_E
with open(os.path.join(os.path.dirname(__file__), '../fixtures/findmount_output.txt')) as f:
FINDMNT_OUTPUT = f.read()
GET_MOUNT_SIZE = {}
def mock_get_mount_size(mountpoint):
return STATVFS_INFO.get(mountpoint, {})

@ -200,7 +200,7 @@ class TestCollectedFacts(unittest.TestCase):
'env']
not_expected_facts = ['facter', 'ohai']
collected_facts = {}
collected_facts: dict[str, str] = {}
def _mock_module(self, gather_subset=None):
return mock_module(gather_subset=self.gather_subset)

@ -11,102 +11,109 @@ from ansible.modules.copy import AnsibleModuleError, split_pre_existing_dir
from ansible.module_utils.basic import AnsibleModule
THREE_DIRS_DATA: tuple[tuple[str, tuple[str, list[str]] | None, tuple[str, list[str]], tuple[str, list[str]], tuple[str, list[str]]], ...] = (
('/dir1/dir2',
# 0 existing dirs: error (because / should always exist)
None,
# 1 existing dir:
('/', ['dir1', 'dir2']),
# 2 existing dirs:
('/dir1', ['dir2']),
# 3 existing dirs:
('/dir1/dir2', [])
),
('/dir1/dir2/',
# 0 existing dirs: error (because / should always exist)
None,
# 1 existing dir:
('/', ['dir1', 'dir2']),
# 2 existing dirs:
('/dir1', ['dir2']),
# 3 existing dirs:
('/dir1/dir2', [])
),
)
THREE_DIRS_DATA = (('/dir1/dir2',
# 0 existing dirs: error (because / should always exist)
None,
# 1 existing dir:
('/', ['dir1', 'dir2']),
# 2 existing dirs:
('/dir1', ['dir2']),
# 3 existing dirs:
('/dir1/dir2', [])
),
('/dir1/dir2/',
# 0 existing dirs: error (because / should always exist)
None,
# 1 existing dir:
('/', ['dir1', 'dir2']),
# 2 existing dirs:
('/dir1', ['dir2']),
# 3 existing dirs:
('/dir1/dir2', [])
),
)
TWO_DIRS_DATA = (('dir1/dir2',
# 0 existing dirs:
('.', ['dir1', 'dir2']),
# 1 existing dir:
('dir1', ['dir2']),
# 2 existing dirs:
('dir1/dir2', []),
# 3 existing dirs: Same as 2 because we never get to the third
),
('dir1/dir2/',
# 0 existing dirs:
('.', ['dir1', 'dir2']),
# 1 existing dir:
('dir1', ['dir2']),
# 2 existing dirs:
('dir1/dir2', []),
# 3 existing dirs: Same as 2 because we never get to the third
),
('/dir1',
# 0 existing dirs: error (because / should always exist)
None,
# 1 existing dir:
('/', ['dir1']),
# 2 existing dirs:
('/dir1', []),
# 3 existing dirs: Same as 2 because we never get to the third
),
('/dir1/',
# 0 existing dirs: error (because / should always exist)
None,
# 1 existing dir:
('/', ['dir1']),
# 2 existing dirs:
('/dir1', []),
# 3 existing dirs: Same as 2 because we never get to the third
),
) + THREE_DIRS_DATA
ONE_DIR_DATA = (('dir1',
# 0 existing dirs:
('.', ['dir1']),
# 1 existing dir:
('dir1', []),
# 2 existing dirs: Same as 1 because we never get to the third
),
('dir1/',
# 0 existing dirs:
('.', ['dir1']),
# 1 existing dir:
('dir1', []),
# 2 existing dirs: Same as 1 because we never get to the third
),
) + TWO_DIRS_DATA
TWO_DIRS_DATA: tuple[tuple[str, tuple[str, list[str]] | None, tuple[str, list[str]], tuple[str, list[str]]], ...] = (
('dir1/dir2',
# 0 existing dirs:
('.', ['dir1', 'dir2']),
# 1 existing dir:
('dir1', ['dir2']),
# 2 existing dirs:
('dir1/dir2', []),
# 3 existing dirs: Same as 2 because we never get to the third
),
('dir1/dir2/',
# 0 existing dirs:
('.', ['dir1', 'dir2']),
# 1 existing dir:
('dir1', ['dir2']),
# 2 existing dirs:
('dir1/dir2', []),
# 3 existing dirs: Same as 2 because we never get to the third
),
('/dir1',
# 0 existing dirs: error (because / should always exist)
None,
# 1 existing dir:
('/', ['dir1']),
# 2 existing dirs:
('/dir1', []),
# 3 existing dirs: Same as 2 because we never get to the third
),
('/dir1/',
# 0 existing dirs: error (because / should always exist)
None,
# 1 existing dir:
('/', ['dir1']),
# 2 existing dirs:
('/dir1', []),
# 3 existing dirs: Same as 2 because we never get to the third
),
)
TWO_DIRS_DATA += tuple(item[:4] for item in THREE_DIRS_DATA)
ONE_DIR_DATA: tuple[tuple[str, tuple[str, list[str]] | None, tuple[str, list[str]]], ...] = (
('dir1',
# 0 existing dirs:
('.', ['dir1']),
# 1 existing dir:
('dir1', []),
# 2 existing dirs: Same as 1 because we never get to the third
),
('dir1/',
# 0 existing dirs:
('.', ['dir1']),
# 1 existing dir:
('dir1', []),
# 2 existing dirs: Same as 1 because we never get to the third
),
)
ONE_DIR_DATA += tuple(item[:3] for item in TWO_DIRS_DATA)
@pytest.mark.parametrize('directory, expected', ((d[0], d[4]) for d in THREE_DIRS_DATA))
@pytest.mark.xfail(reason='broken test and/or code, original test missing assert', strict=False)
def test_split_pre_existing_dir_three_levels_exist(directory, expected, mocker):
mocker.patch('os.path.exists', side_effect=[True, True, True])
split_pre_existing_dir(directory) == expected
assert split_pre_existing_dir(directory) == expected
@pytest.mark.parametrize('directory, expected', ((d[0], d[3]) for d in TWO_DIRS_DATA))
@pytest.mark.xfail(reason='broken test and/or code, original test missing assert', strict=False)
def test_split_pre_existing_dir_two_levels_exist(directory, expected, mocker):
mocker.patch('os.path.exists', side_effect=[True, True, False])
split_pre_existing_dir(directory) == expected
assert split_pre_existing_dir(directory) == expected
@pytest.mark.parametrize('directory, expected', ((d[0], d[2]) for d in ONE_DIR_DATA))
@pytest.mark.xfail(reason='broken test and/or code, original test missing assert', strict=False)
def test_split_pre_existing_dir_one_level_exists(directory, expected, mocker):
mocker.patch('os.path.exists', side_effect=[True, False, False])
split_pre_existing_dir(directory) == expected
assert split_pre_existing_dir(directory) == expected
@pytest.mark.parametrize('directory', (d[0] for d in ONE_DIR_DATA if d[1] is None))
@ -118,9 +125,10 @@ def test_split_pre_existing_dir_root_does_not_exist(directory, mocker):
@pytest.mark.parametrize('directory, expected', ((d[0], d[1]) for d in ONE_DIR_DATA if not d[0].startswith('/')))
@pytest.mark.xfail(reason='broken test and/or code, original test missing assert', strict=False)
def test_split_pre_existing_dir_working_dir_exists(directory, expected, mocker):
mocker.patch('os.path.exists', return_value=False)
split_pre_existing_dir(directory) == expected
assert split_pre_existing_dir(directory) == expected
#

@ -23,7 +23,7 @@ from ansible.errors import AnsibleParserError
import pytest
SPLIT_DATA = (
SPLIT_DATA: tuple[tuple[str | None, list[str], dict[str, str]], ...] = (
(None,
[],
{}),

@ -6,6 +6,7 @@ from __future__ import annotations
import pytest
import sys
import typing as t
from io import StringIO
from unittest.mock import MagicMock
@ -46,7 +47,7 @@ def psrp_connection():
class TestConnectionPSRP(object):
OPTIONS_DATA = (
OPTIONS_DATA: tuple[tuple[dict[str, t.Any], dict[str, t.Any]], ...] = (
# default options
(
{},

@ -5,6 +5,7 @@
from __future__ import annotations
import os
import typing as t
import pytest
@ -22,7 +23,7 @@ pytest.importorskip("winrm")
class TestConnectionWinRM(object):
OPTIONS_DATA = (
OPTIONS_DATA: tuple[tuple[dict[str, t.Any], dict[str, t.Any], dict[str, t.Any], bool], ...] = (
# default options
(
{'_extras': {}},

@ -11,7 +11,7 @@ import ansible.plugins.filter.mathstuff as ms
from ansible.errors import AnsibleFilterError, AnsibleFilterTypeError
UNIQUE_DATA = [
UNIQUE_DATA: list[tuple[list, list]] = [
([], []),
([1, 3, 4, 2], [1, 3, 4, 2]),
([1, 3, 2, 4, 2, 3], [1, 3, 2, 4]),
@ -19,7 +19,7 @@ UNIQUE_DATA = [
([1, 1, 4, 2, 1, 4, 3, 2], [1, 4, 2, 3]),
]
TWO_SETS_DATA = [
TWO_SETS_DATA: list[tuple[list, list, tuple]] = [
([], [], ([], [], [])),
([1, 2], [1, 2], ([1, 2], [], [])),
([1, 2], [3, 4], ([], [1, 2], [1, 2, 3, 4])),

@ -387,8 +387,11 @@ class TestWritePasswordFile(unittest.TestCase):
def setUp(self):
self.makedirs_safe = password.makedirs_safe
self.os_chmod = password.os.chmod
password.makedirs_safe = lambda path, mode: None
password.os.chmod = lambda path, mode: None
password.makedirs_safe = self.noop
password.os.chmod = self.noop
def noop(self, *args, **kwargs):
pass
def tearDown(self):
password.makedirs_safe = self.makedirs_safe
@ -410,11 +413,14 @@ class BaseTestLookupModule(unittest.TestCase):
self.password_lookup._loader = self.fake_loader
self.os_path_exists = password.os.path.exists
self.os_open = password.os.open
password.os.open = lambda path, flag: None
password.os.open = self.noop
self.os_close = password.os.close
password.os.close = lambda fd: None
password.os.close = self.noop
self.makedirs_safe = password.makedirs_safe
password.makedirs_safe = lambda path, mode: None
password.makedirs_safe = self.noop
def noop(self, *args, **kwargs):
pass
def tearDown(self):
password.os.path.exists = self.os_path_exists

Loading…
Cancel
Save