Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace deprecated load_module with exec_module #498

Merged
merged 4 commits into from Oct 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 40 additions & 4 deletions nox/tasks.py
Expand Up @@ -13,10 +13,11 @@
# limitations under the License.

import ast
import importlib.machinery
import importlib.util
import io
import json
import os
import sys
import types
from argparse import Namespace
from typing import List, Union
Expand All @@ -31,6 +32,42 @@
from nox.sessions import Result


def _load_and_exec_nox_module(global_config: Namespace) -> types.ModuleType:
"""
Loads, executes, then returns the global_config nox module.

Args:
global_config (Namespace): The global config.

Raises:
IOError: If the nox module cannot be loaded. This
exception is chosen such that it will be caught
by load_nox_module and logged appropriately.

Returns:
types.ModuleType: The initialised nox module.
"""
spec = importlib.util.spec_from_file_location(
"user_nox_module", global_config.noxfile
)
if not spec:
raise IOError(f"Could not get module spec from {global_config.noxfile}")

module = importlib.util.module_from_spec(spec)
if not module:
raise IOError(f"Noxfile {global_config.noxfile} is not a valid python module.")

sys.modules["user_nox_module"] = module

loader = spec.loader
if not loader: # pragma: no cover
raise IOError(f"Could not get module loader for {global_config.noxfile}")
# See https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
# unsure why mypy doesn't like this
loader.exec_module(module) # type: ignore
return module


def load_nox_module(global_config: Namespace) -> Union[types.ModuleType, int]:
"""Load the user's noxfile and return the module object for it.

Expand Down Expand Up @@ -64,9 +101,8 @@ def load_nox_module(global_config: Namespace) -> Union[types.ModuleType, int]:
# guess. The original working directory (the directory that Nox was
# invoked from) gets stored by the .invoke_from "option" in _options.
os.chdir(noxfile_parent_dir)
return importlib.machinery.SourceFileLoader(
"user_nox_module", global_config.noxfile
).load_module()

return _load_and_exec_nox_module(global_config)

except (VersionCheckFailed, InvalidVersionSpecifier) as error:
logger.error(str(error))
Expand Down
30 changes: 24 additions & 6 deletions tests/test_tasks.py
Expand Up @@ -95,9 +95,7 @@ def test_load_nox_module_IOError(caplog):
our_noxfile = Path(__file__).parent.parent.joinpath("noxfile.py")
config = _options.options.namespace(noxfile=str(our_noxfile))

with mock.patch(
"nox.tasks.importlib.machinery.SourceFileLoader.load_module"
) as mock_load:
with mock.patch("nox.tasks.importlib.util.module_from_spec") as mock_load:
mock_load.side_effect = IOError

assert tasks.load_nox_module(config) == 2
Expand All @@ -112,15 +110,35 @@ def test_load_nox_module_OSError(caplog):
our_noxfile = Path(__file__).parent.parent.joinpath("noxfile.py")
config = _options.options.namespace(noxfile=str(our_noxfile))

with mock.patch(
"nox.tasks.importlib.machinery.SourceFileLoader.load_module"
) as mock_load:
with mock.patch("nox.tasks.importlib.util.module_from_spec") as mock_load:
mock_load.side_effect = OSError

assert tasks.load_nox_module(config) == 2
assert "Failed to load Noxfile" in caplog.text


def test_load_nox_module_invalid_spec():
our_noxfile = Path(__file__).parent.parent.joinpath("noxfile.py")
config = _options.options.namespace(noxfile=str(our_noxfile))

with mock.patch("nox.tasks.importlib.util.spec_from_file_location") as mock_spec:
mock_spec.return_value = None

with pytest.raises(IOError):
tasks._load_and_exec_nox_module(config)


def test_load_nox_module_invalid_module():
our_noxfile = Path(__file__).parent.parent.joinpath("noxfile.py")
config = _options.options.namespace(noxfile=str(our_noxfile))

with mock.patch("nox.tasks.importlib.util.module_from_spec") as mock_spec:
mock_spec.return_value = None

with pytest.raises(IOError):
tasks._load_and_exec_nox_module(config)


@pytest.fixture
def reset_needs_version():
"""Do not leak ``nox.needs_version`` between tests."""
Expand Down