mirror of https://github.com/astral-sh/uv
1038 lines
29 KiB
Python
1038 lines
29 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import textwrap
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
PROJECT_DIR = Path(__file__).parent.parent
|
|
TREE_DIR = PROJECT_DIR / "tests" / "tree"
|
|
|
|
# Snapshot filters
|
|
TIME = (r"(\d+.)?\d+(ms|s)", "[TIME]")
|
|
SHUTDOWN = (
|
|
textwrap.dedent(
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
"""
|
|
).lstrip(),
|
|
"",
|
|
)
|
|
STDOUT = ("STDOUT .*", "STDOUT [PATH]")
|
|
STDERR = ("STDERR .*", "STDERR [PATH]")
|
|
|
|
TREE = (re.escape(str(TREE_DIR)), "[TREE]")
|
|
CWD = (re.escape(os.getcwd()), "[CWD]")
|
|
TRACEBACK = ("TRACEBACK .*", "TRACEBACK [TRACEBACK]")
|
|
DEFAULT_FILTERS = [TIME, STDOUT, STDERR, TRACEBACK, TREE, CWD]
|
|
|
|
|
|
def new(
|
|
extra_backend_paths: list[str] | None = None,
|
|
tree_path: Path | None = TREE_DIR,
|
|
) -> subprocess.Popen:
|
|
extra_backend_paths = extra_backend_paths or []
|
|
|
|
env = os.environ.copy()
|
|
# Add the test backends to the Python path
|
|
env["PYTHONPATH"] = ":".join(
|
|
[str(path) for path in [PROJECT_DIR / "backends"] + extra_backend_paths]
|
|
)
|
|
|
|
return subprocess.Popen(
|
|
[sys.executable, str(PROJECT_DIR / "hookd.py")]
|
|
+ ([str(tree_path)] if tree_path is not None else []),
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
env=env,
|
|
)
|
|
|
|
|
|
def send(process, lines):
|
|
process.stdin.write("\n".join(lines) + "\n")
|
|
|
|
|
|
def assert_snapshot(value, expected, filters=None):
|
|
filters = filters or []
|
|
for pattern, replace in filters:
|
|
value = re.sub(pattern, replace, value)
|
|
print(value)
|
|
assert value == textwrap.dedent(expected).lstrip()
|
|
|
|
|
|
def test_shutdown():
|
|
daemon = new()
|
|
daemon.communicate(input="shutdown\n")
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_sigkill():
|
|
daemon = new()
|
|
daemon.kill()
|
|
daemon.wait()
|
|
assert daemon.returncode == -9
|
|
|
|
|
|
def test_sigterm():
|
|
daemon = new()
|
|
daemon.terminate()
|
|
daemon.wait()
|
|
assert daemon.returncode == -15
|
|
|
|
|
|
def test_run_invalid_backend():
|
|
daemon = new()
|
|
send(
|
|
daemon,
|
|
[
|
|
"run",
|
|
"backend_does_not_exist",
|
|
"",
|
|
"build_wheel",
|
|
"",
|
|
"",
|
|
"",
|
|
],
|
|
)
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG backend_does_not_exist build_wheel wheel_directory=[TREE] config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR MissingBackendModule Failed to import the backend 'backend_does_not_exist'
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_invalid_hook():
|
|
daemon = new()
|
|
send(daemon, ["run", "ok_backend", "", "hook_does_not_exist"])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
ERROR InvalidHookName The name 'hook_does_not_exist' is not valid hook. Expected one of: 'build_wheel', 'build_sdist', 'prepare_metadata_for_build_wheel', 'get_requires_for_build_wheel', 'get_requires_for_build_sdist'
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_build_wheel_ok():
|
|
"""
|
|
Uses a mock backend to test the `build_wheel` hook.
|
|
"""
|
|
daemon = new()
|
|
send(daemon, ["run", "ok_backend", "", "build_wheel", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG ok_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK build_wheel_fake_path
|
|
DEBUG ran hook in [TIME]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_build_sdist_ok():
|
|
"""
|
|
Uses a mock backend to test the `build_sdist` hook.
|
|
"""
|
|
daemon = new()
|
|
send(daemon, ["run", "ok_backend", "", "build_sdist", "foo", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT sdist_directory
|
|
EXPECT config_settings
|
|
DEBUG ok_backend build_sdist sdist_directory=[TREE]/foo config_settings=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK build_sdist_fake_path
|
|
DEBUG ran hook in [TIME]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_get_requires_for_build_wheel_ok():
|
|
"""
|
|
Uses a mock backend to test the `get_requires_for_build_wheel` hook.
|
|
"""
|
|
daemon = new()
|
|
send(daemon, ["run", "ok_backend", "", "get_requires_for_build_wheel", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT config_settings
|
|
DEBUG ok_backend get_requires_for_build_wheel config_settings=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK ['fake', 'build', 'wheel', 'requires']
|
|
DEBUG ran hook in [TIME]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_prepare_metadata_for_build_wheel_ok():
|
|
"""
|
|
Uses a mock backend to test the `prepare_metadata_for_build_wheel` hook.
|
|
"""
|
|
daemon = new()
|
|
send(
|
|
daemon, ["run", "ok_backend", "", "prepare_metadata_for_build_wheel", "foo", ""]
|
|
)
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT metadata_directory
|
|
EXPECT config_settings
|
|
DEBUG ok_backend prepare_metadata_for_build_wheel metadata_directory=[TREE]/foo config_settings=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK prepare_metadata_fake_dist_info_path
|
|
DEBUG ran hook in [TIME]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_invalid_config_settings():
|
|
"""
|
|
Sends invalid JSON for the `config_settings` argument which should result in a non_fatal error.
|
|
"""
|
|
daemon = new()
|
|
send(
|
|
daemon,
|
|
["run", "ok_backend", "", "get_requires_for_build_wheel", "not_valid_json"],
|
|
)
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT config_settings
|
|
ERROR MalformedHookArgument Malformed content for argument 'config_settings': 'not_valid_json'
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_build_wheel_multiple_times():
|
|
"""
|
|
Uses a mock backend to test running a hook repeatedly.
|
|
"""
|
|
daemon = new()
|
|
for _ in range(5):
|
|
send(daemon, ["run", "ok_backend", "", "build_wheel", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
""".rstrip()
|
|
+ """
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG ok_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK build_wheel_fake_path
|
|
DEBUG ran hook in [TIME]"""
|
|
* 5
|
|
+ """
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_build_wheel_error():
|
|
"""
|
|
Uses a mock backend that throws an error to test error reporting.
|
|
"""
|
|
daemon = new()
|
|
send(daemon, ["run", "err_backend", "", "build_wheel", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG err_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR HookRuntimeError Oh no
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_error_not_fatal():
|
|
"""
|
|
Uses a mock backend that throws an error to ensure errors are not fatal and another hook can be run.
|
|
"""
|
|
daemon = new()
|
|
send(daemon, ["run", "err_backend", "", "build_wheel", "foo", "", ""])
|
|
send(daemon, ["run", "err_backend", "", "build_wheel", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG err_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR HookRuntimeError Oh no
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG err_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR HookRuntimeError Oh no
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_base_exception_error_not_fatal(tmp_path: Path):
|
|
"""
|
|
Uses a mock backend that throws an error to ensure errors are not fatal and another hook can be run.
|
|
"""
|
|
(tmp_path / "base_exc_backend.py").write_text(
|
|
textwrap.dedent(
|
|
"""
|
|
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
|
|
raise BaseException("Oh no")
|
|
"""
|
|
)
|
|
)
|
|
|
|
daemon = new(extra_backend_paths=[tmp_path])
|
|
send(daemon, ["run", "base_exc_backend", "", "build_wheel", "foo", "", ""])
|
|
send(daemon, ["run", "base_exc_backend", "", "build_wheel", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG base_exc_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR HookRuntimeError Oh no
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG base_exc_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR HookRuntimeError Oh no
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_error_in_backend_module(tmp_path: Path):
|
|
"""
|
|
Tests a backend that raises an error on import.
|
|
"""
|
|
(tmp_path / "import_err_backend.py").write_text("raise RuntimeError('oh no')")
|
|
daemon = new(extra_backend_paths=[tmp_path])
|
|
send(daemon, ["run", "import_err_backend", "", "build_wheel", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG import_err_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR BackendImportError Backend threw an exception during import: oh no
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_unsupported_hook_empty(tmp_path: Path):
|
|
"""
|
|
Tests a backend without any hooks.
|
|
"""
|
|
(tmp_path / "empty_backend.py").write_text("")
|
|
daemon = new(extra_backend_paths=[tmp_path])
|
|
send(daemon, ["run", "empty_backend", "", "build_wheel", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG empty_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR UnsupportedHook The hook 'build_wheel' is not supported by the backend. The backend does not support any known hooks.
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_unsupported_hook_partial(tmp_path: Path):
|
|
"""
|
|
Tests a backend without the requested hook.
|
|
"""
|
|
(tmp_path / "partial_backend.py").write_text(
|
|
textwrap.dedent(
|
|
"""
|
|
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
|
|
raise BaseException("Oh no")
|
|
|
|
def some_other_utility():
|
|
pass
|
|
"""
|
|
)
|
|
)
|
|
|
|
daemon = new(extra_backend_paths=[tmp_path])
|
|
send(daemon, ["run", "partial_backend", "", "build_sdist", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT sdist_directory
|
|
EXPECT config_settings
|
|
DEBUG partial_backend build_sdist sdist_directory=[TREE]/foo config_settings=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR UnsupportedHook The hook 'build_sdist' is not supported by the backend. The backend supports: 'build_wheel'
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
ERROR InvalidAction Received invalid action ''. Expected one of: 'run', 'shutdown'
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
@pytest.mark.parametrize("separator", [":", "."])
|
|
def test_run_cls_backend(separator):
|
|
"""
|
|
Tests a backend namespaced to a class.
|
|
"""
|
|
daemon = new()
|
|
send(
|
|
daemon,
|
|
["run", f"cls_backend{separator}Class", "", "build_wheel", "foo", "", ""],
|
|
)
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
f"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG cls_backend{separator}Class build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK build_wheel_fake_path
|
|
DEBUG ran hook in [TIME]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
@pytest.mark.parametrize("separator", [":", "."])
|
|
def test_run_obj_backend(separator):
|
|
"""
|
|
Tests a backend namespaced to an object.
|
|
"""
|
|
daemon = new()
|
|
send(
|
|
daemon, ["run", f"obj_backend{separator}obj", "", "build_wheel", "foo", "", ""]
|
|
)
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
f"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG obj_backend{separator}obj build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK build_wheel_fake_path
|
|
DEBUG ran hook in [TIME]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_in_tree_backend():
|
|
"""
|
|
Tests a backend in the source tree
|
|
"""
|
|
daemon = new()
|
|
send(
|
|
daemon,
|
|
[
|
|
"run",
|
|
"in_tree",
|
|
"directory_does_not_exist",
|
|
"in_tree_backend",
|
|
"",
|
|
"build_wheel",
|
|
"foo",
|
|
"",
|
|
"",
|
|
],
|
|
)
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG in_tree build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK build_wheel_fake_path
|
|
DEBUG ran hook in [TIME]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_submodule_backend():
|
|
"""
|
|
Tests a backend namespaced to an submodule.
|
|
"""
|
|
daemon = new()
|
|
send(
|
|
daemon, ["run", "submodule_backend.submodule", "", "build_wheel", "foo", "", ""]
|
|
)
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG submodule_backend.submodule build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK build_wheel_fake_path
|
|
DEBUG ran hook in [TIME]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_submodule_backend_invalid_import():
|
|
"""
|
|
Tests a backend namespaced to an submodule but imported as an attribute
|
|
"""
|
|
daemon = new()
|
|
send(
|
|
daemon,
|
|
[
|
|
"run",
|
|
"submodule_backend:submodule",
|
|
"",
|
|
"build_wheel",
|
|
"",
|
|
"",
|
|
"",
|
|
],
|
|
)
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG submodule_backend:submodule build_wheel wheel_directory=[TREE] config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR MissingBackendAttribute Failed to find attribute 'submodule_backend:submodule' in the backend module 'submodule_backend'
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_stdout_capture():
|
|
"""
|
|
Tests capture of stdout from a backend.
|
|
"""
|
|
daemon = new()
|
|
send(daemon, ["run", "stdout_backend", "", "build_wheel", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG stdout_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK build_wheel_fake_path
|
|
DEBUG ran hook in [TIME]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
stdout_parts = stderr_parts = None
|
|
for line in stdout.splitlines():
|
|
parts = line.split()
|
|
if parts[0] == "STDOUT":
|
|
stdout_parts = parts
|
|
elif parts[0] == "STDERR":
|
|
stderr_parts = parts
|
|
|
|
assert len(stdout_parts) == 2
|
|
assert len(stderr_parts) == 2
|
|
|
|
assert_snapshot(
|
|
Path(stdout_parts[1]).read_text(),
|
|
"""
|
|
hello
|
|
world
|
|
""",
|
|
)
|
|
assert Path(stderr_parts[1]).read_text() == ""
|
|
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_stderr_capture():
|
|
"""
|
|
Tests capture of stderr from a backend.
|
|
"""
|
|
daemon = new()
|
|
send(daemon, ["run", "stderr_backend", "", "build_wheel", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG stderr_backend build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
OK build_wheel_fake_path
|
|
DEBUG ran hook in [TIME]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=DEFAULT_FILTERS,
|
|
)
|
|
stdout_parts = stderr_parts = None
|
|
for line in stdout.splitlines():
|
|
parts = line.split()
|
|
if parts[0] == "STDOUT":
|
|
stdout_parts = parts
|
|
elif parts[0] == "STDERR":
|
|
stderr_parts = parts
|
|
|
|
assert len(stdout_parts) == 2
|
|
assert len(stderr_parts) == 2
|
|
assert Path(stdout_parts[1]).read_text() == ""
|
|
assert_snapshot(
|
|
Path(stderr_parts[1]).read_text(),
|
|
"""
|
|
hello
|
|
world
|
|
""",
|
|
)
|
|
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
def test_run_stdout_capture_multiple_hook_runs():
|
|
"""
|
|
Tests capture of stdout from a backend, each hook run should get a unique file
|
|
"""
|
|
COUNT = 2
|
|
daemon = new()
|
|
for i in range(COUNT):
|
|
send(
|
|
daemon,
|
|
["run", "stdout_backend", "", "build_wheel", "foo", f'{{"run": {i}}}', ""],
|
|
)
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
print(stdout)
|
|
|
|
all_stdout_parts = []
|
|
all_stderr_parts = []
|
|
for line in stdout.splitlines():
|
|
parts = line.split()
|
|
if parts[0] == "STDOUT":
|
|
all_stdout_parts.append(parts)
|
|
elif parts[0] == "STDERR":
|
|
all_stderr_parts.append(parts)
|
|
|
|
# We should have a result for each hook run
|
|
assert len(all_stdout_parts) == COUNT
|
|
assert len(all_stderr_parts) == COUNT
|
|
|
|
# Each run should write unique output to their file
|
|
for i, stdout_parts in enumerate(all_stdout_parts):
|
|
assert_snapshot(
|
|
Path(stdout_parts[1]).read_text(),
|
|
f"""
|
|
writing config_settings
|
|
run = {i}
|
|
""",
|
|
)
|
|
for i, stderr_parts in enumerate(all_stderr_parts):
|
|
assert Path(stderr_parts[1]).read_text() == ""
|
|
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|
|
|
|
|
|
@pytest.mark.parametrize("backend", ["hatchling.build", "poetry.core.masonry.api"])
|
|
def test_run_real_backend_build_wheel_error(backend: str):
|
|
"""
|
|
Sends an path that does not exist to a real "build_wheel" hook.
|
|
"""
|
|
try:
|
|
importlib.import_module(backend)
|
|
except ImportError:
|
|
pytest.skip(f"build backend {backend!r} is not installed")
|
|
|
|
daemon = new()
|
|
send(daemon, ["run", backend, "", "build_wheel", "foo", "", ""])
|
|
stdout, stderr = daemon.communicate(input="shutdown\n")
|
|
assert_snapshot(
|
|
stdout,
|
|
f"""
|
|
DEBUG changed working directory to [TREE]
|
|
READY
|
|
EXPECT action
|
|
EXPECT build_backend
|
|
EXPECT backend_path
|
|
EXPECT hook_name
|
|
EXPECT wheel_directory
|
|
EXPECT config_settings
|
|
EXPECT metadata_directory
|
|
DEBUG {backend} build_wheel wheel_directory=[TREE]/foo config_settings=None metadata_directory=None
|
|
DEBUG parsed hook inputs in [TIME]
|
|
STDOUT [PATH]
|
|
STDERR [PATH]
|
|
ERROR HookRuntimeError [MESSAGE]
|
|
TRACEBACK [TRACEBACK]
|
|
READY
|
|
EXPECT action
|
|
SHUTDOWN
|
|
""",
|
|
filters=(
|
|
DEFAULT_FILTERS + [("HookRuntimeError .*", "HookRuntimeError [MESSAGE]")]
|
|
),
|
|
)
|
|
assert stderr == ""
|
|
assert daemon.returncode == 0
|