Skip to content

Commit

Permalink
Forward warnings from build-backend to build-frontend
Browse files Browse the repository at this point in the history
This makes it possible for build-frontends to surface this information
to end users.
  • Loading branch information
pradyunsg committed Jun 19, 2023
1 parent a3456dd commit 8952b32
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 16 deletions.
2 changes: 2 additions & 0 deletions src/pyproject_hooks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""

from ._impl import (
BuildBackendWarning,
BackendInvalid,
BackendUnavailable,
BuildBackendHookCaller,
Expand All @@ -13,6 +14,7 @@

__version__ = "1.0.0"
__all__ = [
"BuildBackendWarning",
"BackendUnavailable",
"BackendInvalid",
"HookMissing",
Expand Down
13 changes: 13 additions & 0 deletions src/pyproject_hooks/_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from os.path import abspath
from os.path import join as pjoin
from subprocess import STDOUT, check_call, check_output
import warnings

from ._in_process import _in_proc_script_path

Expand All @@ -20,6 +21,10 @@ def read_json(path):
return json.load(f)


class BuildBackendWarning(UserWarning):
"""Will be emitted for every UserWarning emitted by the hook process."""


class BackendUnavailable(Exception):
"""Will be raised if the backend cannot be imported in the hook process."""

Expand Down Expand Up @@ -343,4 +348,12 @@ def _call_hook(self, hook_name, kwargs):
)
if data.get("hook_missing"):
raise HookMissing(data.get("missing_hook_name") or hook_name)

for w in data.get("warnings", []):
warnings.warn_explicit(
message=w["message"],
category=BuildBackendWarning,
filename=w["filename"],
lineno=w["lineno"],
)
return data["return_val"]
44 changes: 28 additions & 16 deletions src/pyproject_hooks/_in_process/_in_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from glob import glob
from importlib import import_module
from os.path import join as pjoin
import warnings

# This file is run as a script, and `import wrappers` is not zip-safe, so we
# include write_json() and read_json() from wrappers.py.
Expand Down Expand Up @@ -336,22 +337,33 @@ def main():

hook_input = read_json(pjoin(control_dir, "input.json"))

json_out = {"unsupported": False, "return_val": None}
try:
json_out["return_val"] = hook(**hook_input["kwargs"])
except BackendUnavailable as e:
json_out["no_backend"] = True
json_out["traceback"] = e.traceback
except BackendInvalid as e:
json_out["backend_invalid"] = True
json_out["backend_error"] = e.message
except GotUnsupportedOperation as e:
json_out["unsupported"] = True
json_out["traceback"] = e.traceback
except HookMissing as e:
json_out["hook_missing"] = True
json_out["missing_hook_name"] = e.hook_name or hook_name

with warnings.catch_warnings(record=True) as captured_warnings:
json_out = {"unsupported": False, "return_val": None}
try:
json_out["return_val"] = hook(**hook_input["kwargs"])
except BackendUnavailable as e:
json_out["no_backend"] = True
json_out["traceback"] = e.traceback
except BackendInvalid as e:
json_out["backend_invalid"] = True
json_out["backend_error"] = e.message
except GotUnsupportedOperation as e:
json_out["unsupported"] = True
json_out["traceback"] = e.traceback
except HookMissing as e:
json_out["hook_missing"] = True
json_out["missing_hook_name"] = e.hook_name or hook_name

json_out["warnings"] = [
{
"message": str(w.message),
"filename": w.filename,
"lineno": w.lineno,
}
for w in captured_warnings
if isinstance(w.category, type) and issubclass(w.category, UserWarning)
]
print(json_out)
write_json(json_out, pjoin(control_dir, "output.json"), indent=2)


Expand Down
9 changes: 9 additions & 0 deletions tests/samples/buildsys_pkgs/buildsys_warnings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Test "backend" defining nothing other than a hook that logs a warning.
"""

import warnings


def get_requires_for_build_wheel(config_settings):
warnings.warn("this is my example warning")
return []
3 changes: 3 additions & 0 deletions tests/samples/pkg-with-warnings/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["eg_buildsys"]
build-backend = "buildsys_warnings"
9 changes: 9 additions & 0 deletions tests/test_call_hooks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import os
import tarfile
import warnings
import zipfile
from os.path import abspath, dirname
from os.path import join as pjoin
Expand All @@ -12,6 +13,7 @@

from pyproject_hooks import (
BackendUnavailable,
BuildBackendWarning,
BuildBackendHookCaller,
UnsupportedOperation,
default_subprocess_runner,
Expand Down Expand Up @@ -226,3 +228,10 @@ def test__supported_features(pkg, expected):
with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
res = hooks._supported_features()
assert res == expected


def test_warnings():
hooks = get_hooks("pkg-with-warnings")
with modified_env({"PYTHONPATH": BUILDSYS_PKGS}):
with pytest.warns(BuildBackendWarning, match="this is my example warning"):
hooks.get_requires_for_build_wheel({})

0 comments on commit 8952b32

Please sign in to comment.