From c003fb32840b34d4c8288b5418e20011d771e165 Mon Sep 17 00:00:00 2001 From: Jeff Dairiki Date: Tue, 31 Oct 2023 16:11:12 -0700 Subject: [PATCH] fix: do not use shutil.which to find npm binary This works around a bug in python 3.12.0's `shutil.which` when running on Windows. See python/cpython#109590 --- build_frontend.py | 15 +++++++++++---- tests/test_build_frontend.py | 6 ++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/build_frontend.py b/build_frontend.py index 7c701426c..178ae767c 100644 --- a/build_frontend.py +++ b/build_frontend.py @@ -26,6 +26,9 @@ # path to compiled JS entry point APP_JS = "lektor/admin/static/app.js" +# name of NPM program (patched by tests) +NPM = "npm" + class FrontendBuildHook(BuildHookInterface): """Hatching build hook to compile our frontend JS.""" @@ -65,16 +68,20 @@ def initialize(self, version, build_data): app.display_info(f"{APP_JS} exists, skipping frontend build") return - npm = shutil.which("npm") - if npm is None: + try: + proc = subprocess.run( + (NPM, "-v"), capture_output=True, text=True, check=True + ) + except FileNotFoundError: app.abort("npm is not available. can not build frontend") + app.display_info(f"found npm version {proc.stdout.strip()}") frontend = Path(root, FRONTEND) if not frontend.is_dir(): app.abort("frontend source is missing. can not build frontend") app.display_info("npm install") - subprocess.run((npm, "install"), cwd=frontend, check=True) + subprocess.run((NPM, "install"), cwd=frontend, check=True) app.display_info("npm run build") - subprocess.run((npm, "run", "build"), cwd=frontend, check=True) + subprocess.run((NPM, "run", "build"), cwd=frontend, check=True) app.display_success("built frontend static files") diff --git a/tests/test_build_frontend.py b/tests/test_build_frontend.py index d0a2b1dfd..7824561cb 100644 --- a/tests/test_build_frontend.py +++ b/tests/test_build_frontend.py @@ -81,8 +81,10 @@ def test_initialize_skips_build_if_output_exists( @pytest.mark.usefixtures("frontend_src") -def test_initialize_aborts_if_no_npm(frontend_build_hook, mocker): - mocker.patch("shutil.which").return_value = None +def test_initialize_aborts_if_no_npm( + frontend_build_hook, frontend_build_module, monkeypatch +): + monkeypatch.setattr(frontend_build_module, "NPM", "NPM_IS_MISSING") with pytest.raises(SystemExit) as exc_info: frontend_build_hook.initialize("standard", build_data={}) assert exc_info.value.args[0] == 1