From cffe44d38f66e5ff302743cb0983b8c099f057d9 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Tue, 28 Oct 2025 10:00:38 +0100 Subject: [PATCH 1/2] do not depend on nodejs in tests --- pdoc/search.py | 20 +++++++++++++++----- test/test_search.py | 6 +++--- test/test_snapshot.py | 10 +++------- test/testdata/demopackage_dir.html | 2 +- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/pdoc/search.py b/pdoc/search.py index 63fcff7c..c197310b 100644 --- a/pdoc/search.py +++ b/pdoc/search.py @@ -46,6 +46,7 @@ from collections.abc import Callable from collections.abc import Mapping +import functools import html import json from pathlib import Path @@ -124,6 +125,16 @@ def make_index(mod: pdoc.doc.Namespace, **extra): return documents +@functools.cache +def node_executable() -> str | None: + if shutil.which("nodejs"): + return "nodejs" + elif shutil.which("node"): + return "node" + else: + return None + + def precompile_index(documents: list[dict], compile_js: Path) -> str: """ This method tries to precompile the Elasticlunr.js search index by invoking `nodejs` or `node`. @@ -136,12 +147,11 @@ def precompile_index(documents: list[dict], compile_js: Path) -> str: """ raw = json.dumps(documents) try: - if shutil.which("nodejs"): - executable = "nodejs" - else: - executable = "node" + node = node_executable() + if node is None: + raise FileNotFoundError("No such file or directory: 'node'") out = subprocess.check_output( - [executable, compile_js], + [node, compile_js], input=raw.encode(), cwd=Path(__file__).parent / "templates", stderr=subprocess.STDOUT, diff --git a/test/test_search.py b/test/test_search.py index d2d42ce6..77510b29 100644 --- a/test/test_search.py +++ b/test/test_search.py @@ -26,15 +26,15 @@ def test_precompile_index(monkeypatch, capsys): == '{"foo": 42, "_isPrebuiltIndex": true}' ) - monkeypatch.setattr(shutil, "which", lambda _: "C:\\nodejs.exe") + monkeypatch.setattr(search, "node_executable", lambda: "C:\\nodejs.exe") assert ( search.precompile_index(docs, compile_js) == '{"foo": 42, "_isPrebuiltIndex": true}' ) - monkeypatch.setattr(shutil, "which", lambda _: None) + monkeypatch.setattr(search, "node_executable", lambda: None) assert ( search.precompile_index(docs, compile_js) - == '{"foo": 42, "_isPrebuiltIndex": true}' + == json.dumps(docs) ) def _raise(*_, **__): diff --git a/test/test_snapshot.py b/test/test_snapshot.py index 77482f6c..ca2d620a 100755 --- a/test/test_snapshot.py +++ b/test/test_snapshot.py @@ -4,7 +4,6 @@ from contextlib import ExitStack import os from pathlib import Path -import shutil import sys import tempfile import warnings @@ -12,6 +11,7 @@ import pytest import pdoc.render +import pdoc.search here = Path(__file__).parent.absolute() @@ -175,6 +175,7 @@ def test_snapshots(snapshot: Snapshot, format: str, monkeypatch): Compare pdoc's rendered output against stored snapshots. """ monkeypatch.chdir(snapshot_dir) + monkeypatch.setattr(pdoc.search, "node_executable", lambda: None) if sys.version_info < snapshot.min_version: pytest.skip( f"Snapshot only works on Python {'.'.join(str(x) for x in snapshot.min_version)} and above." @@ -189,12 +190,7 @@ def test_snapshots(snapshot: Snapshot, format: str, monkeypatch): if __name__ == "__main__": warnings.simplefilter("error") - if not shutil.which("nodejs") and not shutil.which("node"): - print( - "Snapshots include precompiled search indices, " - "but this system does not have Node.js installed to render them. Aborting." - ) - sys.exit(1) + pdoc.search.node_executable = lambda: None os.chdir(snapshot_dir) skipped_some = False for snapshot in snapshots: diff --git a/test/testdata/demopackage_dir.html b/test/testdata/demopackage_dir.html index 3b8c0547..c6c3c9f0 100644 --- a/test/testdata/demopackage_dir.html +++ b/test/testdata/demopackage_dir.html @@ -3,7 +3,7 @@

search.js