Skip to content

Commit 7a71736

Browse files
committed
feat: add messages config for failure/success
Signed-off-by: Henry Schreiner <[email protected]>
1 parent d977a08 commit 7a71736

File tree

10 files changed

+127
-14
lines changed

10 files changed

+127
-14
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,12 @@ generate[].template-path = ""
299299
# files & remember to gitignore the file.
300300
generate[].location = "install"
301301

302+
# A message to print after a build failure.
303+
messages.after-failure = ""
304+
305+
# A message to print after a successful build.
306+
messages.after-success = ""
307+
302308
# List dynamic metadata fields and hook locations in this table.
303309
metadata = {}
304310

src/scikit_build_core/build/__init__.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ def build_wheel(
3737
except FailedLiveProcessError as err:
3838
sys.stdout.flush()
3939
rich_print(f"\n[red bold]*** {' '.join(err.args)}", file=sys.stderr)
40+
if err.msg:
41+
rich_print(err.msg)
4042
raise SystemExit(1) from None
4143

4244

@@ -59,6 +61,8 @@ def build_editable(
5961
except FailedLiveProcessError as err:
6062
sys.stdout.flush()
6163
rich_print(f"\n[red bold]*** {' '.join(err.args)}", file=sys.stderr)
64+
if err.msg:
65+
rich_print(err.msg)
6266
raise SystemExit(1) from None
6367

6468

@@ -91,9 +95,18 @@ def build_sdist(
9195
sdist_directory: str,
9296
config_settings: dict[str, list[str] | str] | None = None,
9397
) -> str:
98+
from .._logging import rich_print
99+
from ..errors import FailedLiveProcessError
94100
from .sdist import build_sdist as skbuild_build_sdist
95101

96-
return skbuild_build_sdist(sdist_directory, config_settings)
102+
try:
103+
return skbuild_build_sdist(sdist_directory, config_settings)
104+
except FailedLiveProcessError as err:
105+
sys.stdout.flush()
106+
rich_print(f"\n[red bold]*** {' '.join(err.args)}", file=sys.stderr)
107+
if err.msg:
108+
rich_print(err.msg)
109+
raise SystemExit(1) from None
97110

98111

99112
def get_requires_for_build_sdist(

src/scikit_build_core/build/wheel.py

+25-12
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ def _get_packages(
111111
@dataclasses.dataclass
112112
class WheelImplReturn:
113113
wheel_filename: str
114+
settings: ScikitBuildSettings
114115
mapping: dict[str, str] = dataclasses.field(default_factory=dict)
115116

116117

@@ -146,6 +147,9 @@ def _build_wheel_impl(
146147
settings_reader.validate_may_exit()
147148

148149
if settings_reader.settings.fail:
150+
if settings_reader.settings.messages.after_failure:
151+
rich_print(settings_reader.settings.messages.after_failure.format())
152+
raise SystemExit(7)
149153
rich_error("scikit-build-core's fail setting was enabled. Exiting immediately.")
150154

151155
# Warn if cmake or ninja is in build-system.requires
@@ -177,6 +181,7 @@ def _build_wheel_impl(
177181
pyproject, config_settings or {}, state=state, retry=True
178182
)
179183
if "failed" not in settings_reader.overrides:
184+
err.msg = settings_reader.settings.messages.after_failure.format()
180185
raise
181186

182187
rich_print(
@@ -187,15 +192,19 @@ def _build_wheel_impl(
187192

188193
settings_reader.validate_may_exit()
189194

190-
return _build_wheel_impl_impl(
191-
wheel_directory,
192-
metadata_directory,
193-
exit_after_config=exit_after_config,
194-
editable=editable,
195-
state=state,
196-
settings=settings_reader.settings,
197-
pyproject=pyproject,
198-
)
195+
try:
196+
return _build_wheel_impl_impl(
197+
wheel_directory,
198+
metadata_directory,
199+
exit_after_config=exit_after_config,
200+
editable=editable,
201+
state=state,
202+
settings=settings_reader.settings,
203+
pyproject=pyproject,
204+
)
205+
except FailedLiveProcessError as err:
206+
err.msg = settings_reader.settings.messages.after_failure.format()
207+
raise
199208

200209

201210
def _build_wheel_impl_impl(
@@ -343,7 +352,7 @@ def _build_wheel_impl_impl(
343352
if not path.parent.is_dir():
344353
path.parent.mkdir(exist_ok=True, parents=True)
345354
path.write_bytes(data)
346-
return WheelImplReturn(wheel_filename=dist_info.name)
355+
return WheelImplReturn(wheel_filename=dist_info.name, settings=settings)
347356

348357
for gen in settings.generate:
349358
contents = generate_file_contents(gen, metadata)
@@ -390,7 +399,7 @@ def _build_wheel_impl_impl(
390399
)
391400

392401
if exit_after_config:
393-
return WheelImplReturn("")
402+
return WheelImplReturn("", settings=settings)
394403

395404
default_gen = (
396405
"MSVC"
@@ -486,4 +495,8 @@ def _build_wheel_impl_impl(
486495

487496
wheel_filename: str = wheel.wheelpath.name
488497
rich_print(f"[green]***[/green] [bold]Created[/bold] {wheel_filename}...")
489-
return WheelImplReturn(wheel_filename=wheel_filename, mapping=mapping)
498+
if settings.messages.after_success:
499+
rich_print(settings.messages.after_success.format())
500+
return WheelImplReturn(
501+
wheel_filename=wheel_filename, mapping=mapping, settings=settings
502+
)

src/scikit_build_core/errors.py

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ class FailedLiveProcessError(Exception):
7676
Exception for when output was not being redirected.
7777
"""
7878

79+
def __init__(self, *args: object, msg: str = "") -> None:
80+
super().__init__(*args)
81+
self.msg = msg
82+
7983

8084
class CMakeAccessError(FailedProcessError):
8185
"""

src/scikit_build_core/resources/scikit-build.schema.json

+19
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,22 @@
360360
]
361361
}
362362
},
363+
"messages": {
364+
"type": "object",
365+
"additionalProperties": false,
366+
"properties": {
367+
"after-failure": {
368+
"type": "string",
369+
"default": "",
370+
"description": "A message to print after a build failure."
371+
},
372+
"after-success": {
373+
"type": "string",
374+
"default": "",
375+
"description": "A message to print after a successful build."
376+
}
377+
}
378+
},
363379
"metadata": {
364380
"type": "object",
365381
"description": "List dynamic metadata fields and hook locations in this table.",
@@ -565,6 +581,9 @@
565581
"generate": {
566582
"$ref": "#/properties/generate"
567583
},
584+
"messages": {
585+
"$ref": "#/properties/messages"
586+
},
568587
"metadata": {
569588
"$ref": "#/properties/metadata"
570589
},

src/scikit_build_core/settings/skbuild_model.py

+18
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,23 @@ class GenerateSettings:
293293
"""
294294

295295

296+
@dataclasses.dataclass
297+
class MessagesSettings:
298+
"""
299+
Settings for messages.
300+
"""
301+
302+
after_failure: str = ""
303+
"""
304+
A message to print after a build failure.
305+
"""
306+
307+
after_success: str = ""
308+
"""
309+
A message to print after a successful build.
310+
"""
311+
312+
296313
@dataclasses.dataclass
297314
class ScikitBuildSettings:
298315
cmake: CMakeSettings = dataclasses.field(default_factory=CMakeSettings)
@@ -305,6 +322,7 @@ class ScikitBuildSettings:
305322
build: BuildSettings = dataclasses.field(default_factory=BuildSettings)
306323
install: InstallSettings = dataclasses.field(default_factory=InstallSettings)
307324
generate: List[GenerateSettings] = dataclasses.field(default_factory=list)
325+
messages: MessagesSettings = dataclasses.field(default_factory=MessagesSettings)
308326

309327
metadata: Dict[str, Dict[str, Any]] = dataclasses.field(default_factory=dict)
310328
"""

tests/packages/abi3_pyproject_ext/pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ version = "0.0.1"
88

99
[tool.scikit-build]
1010
wheel.py-api = "cp37"
11+
messages.after-success = "This is a message after success"

tests/test_broken_fallback.py

+19
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,22 @@ def test_fail_setting(
5050
assert exc.value.code == 7
5151
out, _ = capsys.readouterr()
5252
assert "fail setting was enabled" in out
53+
54+
55+
@pytest.mark.usefixtures("broken_fallback")
56+
def test_fail_setting_msg(
57+
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
58+
):
59+
monkeypatch.setenv("FAIL_NOW", "1")
60+
monkeypatch.setenv(
61+
"SKBUILD_MESSAGES_AFTER_FAILURE", "This is a test failure message"
62+
)
63+
64+
assert get_requires_for_build_wheel({}) == []
65+
with pytest.raises(SystemExit) as exc:
66+
build_wheel("dist")
67+
68+
assert exc.value.code == 7
69+
out, _ = capsys.readouterr()
70+
assert "This is a test failure message" in out
71+
assert "fail setting was enabled" not in out

tests/test_pyproject_abi3.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
sysconfig.get_platform().startswith(("msys", "mingw")),
2020
reason="abi3 FindPython on MSYS/MinGW reports not found",
2121
)
22-
def test_abi3_wheel(tmp_path, monkeypatch, virtualenv):
22+
def test_abi3_wheel(tmp_path, monkeypatch, virtualenv, capsys):
2323
dist = tmp_path / "dist"
2424
dist.mkdir()
2525
monkeypatch.chdir(ABI_PKG)
@@ -29,6 +29,8 @@ def test_abi3_wheel(tmp_path, monkeypatch, virtualenv):
2929
shutil.rmtree("build")
3030

3131
out = build_wheel(str(dist))
32+
stdout, stderr = capsys.readouterr()
33+
assert "This is a message after success" in stdout
3234
(wheel,) = dist.glob("abi3_example-0.0.1-*.whl")
3335
assert wheel == dist / out
3436
abi3 = sys.implementation.name == "cpython" and not sysconfig.get_config_var(

tests/test_skbuild_settings.py

+18
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ def test_skbuild_settings_default(tmp_path: Path):
6868
assert settings.install.strip
6969
assert settings.generate == []
7070
assert not settings.fail
71+
assert settings.messages.after_failure == ""
72+
assert settings.messages.after_success == ""
7173

7274

7375
def test_skbuild_settings_envvar(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
@@ -106,6 +108,12 @@ def test_skbuild_settings_envvar(tmp_path: Path, monkeypatch: pytest.MonkeyPatch
106108
monkeypatch.setenv("SKBUILD_INSTALL_COMPONENTS", "a;b;c")
107109
monkeypatch.setenv("SKBUILD_INSTALL_STRIP", "False")
108110
monkeypatch.setenv("SKBUILD_FAIL", "1")
111+
monkeypatch.setenv(
112+
"SKBUILD_MESSAGES_AFTER_FAILURE", "This is a test failure message"
113+
)
114+
monkeypatch.setenv(
115+
"SKBUILD_MESSAGES_AFTER_SUCCESS", "This is a test success message"
116+
)
109117

110118
pyproject_toml = tmp_path / "pyproject.toml"
111119
pyproject_toml.write_text("", encoding="utf-8")
@@ -149,6 +157,8 @@ def test_skbuild_settings_envvar(tmp_path: Path, monkeypatch: pytest.MonkeyPatch
149157
assert settings.install.components == ["a", "b", "c"]
150158
assert not settings.install.strip
151159
assert settings.fail
160+
assert settings.messages.after_failure == "This is a test failure message"
161+
assert settings.messages.after_success == "This is a test success message"
152162

153163

154164
@pytest.mark.parametrize("prefix", [True, False], ids=["skbuild", "noprefix"])
@@ -196,6 +206,8 @@ def test_skbuild_settings_config_settings(
196206
"install.components": ["a", "b", "c"],
197207
"install.strip": "True",
198208
"fail": "1",
209+
"messages.after-failure": "This is a test failure message",
210+
"messages.after-success": "This is a test success message",
199211
}
200212

201213
if prefix:
@@ -238,6 +250,8 @@ def test_skbuild_settings_config_settings(
238250
assert settings.install.components == ["a", "b", "c"]
239251
assert settings.install.strip
240252
assert settings.fail
253+
assert settings.messages.after_failure == "This is a test failure message"
254+
assert settings.messages.after_success == "This is a test success message"
241255

242256

243257
def test_skbuild_settings_pyproject_toml(
@@ -284,6 +298,8 @@ def test_skbuild_settings_pyproject_toml(
284298
install.components = ["a", "b", "c"]
285299
install.strip = true
286300
fail = true
301+
messages.after-failure = "This is a test failure message"
302+
messages.after-success = "This is a test success message"
287303
[[tool.scikit-build.generate]]
288304
path = "a/b/c"
289305
template = "hello"
@@ -341,6 +357,8 @@ def test_skbuild_settings_pyproject_toml(
341357
),
342358
]
343359
assert settings.fail
360+
assert settings.messages.after_failure == "This is a test failure message"
361+
assert settings.messages.after_success == "This is a test success message"
344362

345363

346364
def test_skbuild_settings_pyproject_toml_broken(

0 commit comments

Comments
 (0)