Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"command": "uv run python -m pytest tests/e2e -x -q",
"ran_at": 1788565082,
"exit_code": 0,
"commit": "4be6d991b581c4940685c9a5009037fd36e6f93f",
"branch": "claude/tackle-1042-b1-extload"
}
23 changes: 23 additions & 0 deletions internals/checks/src/checks/npm_binary_extension_load/diagnose.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""The human-readable diagnosis for a failed npm bundled-cli extension-load probe (#762)."""

from __future__ import annotations

STATIC_MARKER = "Dynamic loading not supported"


def diagnose(result) -> str:
output = f"stdout={result.stdout!r}\nstderr={result.stderr!r}"
if STATIC_MARKER in result.stderr:
return (
f"the npm bundled-cli `dirsql` binary cannot load SQLite "
f"extensions ({STATIC_MARKER!r}): it is statically linked "
"(static-pie), and a static binary cannot dlopen. The release "
"pipeline (putitoutthere `_matrix.yml`) compiles npm bundle_cli "
"Linux binaries via cargo-zigbuild against the declared gnu "
"triple at a pinned glibc floor since putitoutthere#605; a "
"static binary here means that lane regressed (or the `@v0` "
"tag resolves to a pre-#605 revision). Fix the pipeline, not "
"this probe. See dirsql#762.\n"
f"{output}"
)
return f"`dirsql query` against the bundled binary failed.\n{output}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Colocated unit tests for the npm bundled-cli probe's failure diagnosis (#762)."""

from unittest import mock

from checks.npm_binary_extension_load.diagnose import STATIC_MARKER, diagnose


def _result(returncode=0, stdout="", stderr=""):
return mock.Mock(returncode=returncode, stdout=stdout, stderr=stderr)


def describe_diagnose():
def static_binary_gets_the_dlopen_diagnosis():
message = diagnose(_result(1, stdout="", stderr=f"boom: {STATIC_MARKER}"))
assert "statically linked" in message
assert "dirsql#762" in message
assert "putitoutthere#605" in message
assert STATIC_MARKER in message

def the_marker_matches_sqlites_literal_wording():
message = diagnose(_result(1, stderr="Error: Dynamic loading not supported"))
assert "statically linked" in message

def other_failures_get_a_generic_message():
message = diagnose(_result(1, stdout="out", stderr="bad flag"))
assert message.startswith("`dirsql query` against the bundled binary failed.")
assert "'bad flag'" in message
assert "'out'" in message
21 changes: 2 additions & 19 deletions internals/checks/src/checks/npm_binary_extension_load/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@
write_text,
)

from .diagnose import diagnose

PROBE_SQL = "SELECT vec_version() AS v"
ENTRYPOINT = "sqlite3_vec_init"
STATIC_MARKER = "Dynamic loading not supported"
BIN_NAME = "dirsql"


Expand All @@ -57,24 +58,6 @@ def find_binaries(dist_dir: str, walker=os.walk) -> list[str]:
)


def diagnose(result) -> str:
output = f"stdout={result.stdout!r}\nstderr={result.stderr!r}"
if STATIC_MARKER in result.stderr:
return (
f"the npm bundled-cli `dirsql` binary cannot load SQLite "
f"extensions ({STATIC_MARKER!r}): it is statically linked "
"(static-pie), and a static binary cannot dlopen. The release "
"pipeline (putitoutthere `_matrix.yml`) compiles npm bundle_cli "
"Linux binaries via cargo-zigbuild against the declared gnu "
"triple at a pinned glibc floor since putitoutthere#605; a "
"static binary here means that lane regressed (or the `@v0` "
"tag resolves to a pre-#605 revision). Fix the pipeline, not "
"this probe. See dirsql#762.\n"
f"{output}"
)
return f"`dirsql query` against the bundled binary failed.\n{output}"


def run(
dist_dir: str,
runner=subprocess.run,
Expand Down
36 changes: 11 additions & 25 deletions internals/checks/src/checks/npm_binary_extension_load/gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
BIN_NAME,
ENTRYPOINT,
PROBE_SQL,
STATIC_MARKER,
ProbeError,
config_for,
diagnose,
find_binaries,
run,
)
from checks.wheel_extension_load.gate import bin_subdir

DIAGNOSE = "checks.npm_binary_extension_load.gate.diagnose"


def _result(returncode=0, stdout="", stderr=""):
return mock.Mock(returncode=returncode, stdout=stdout, stderr=stderr)
Expand Down Expand Up @@ -63,21 +63,6 @@ def declares_the_library_and_entrypoint():
)


def describe_diagnose():
def static_binary_gets_the_dlopen_diagnosis():
message = diagnose(_result(1, stdout="", stderr=f"boom: {STATIC_MARKER}"))
assert "statically linked" in message
assert "dirsql#762" in message
assert "putitoutthere#605" in message
assert STATIC_MARKER in message

def other_failures_get_a_generic_message():
message = diagnose(_result(1, stdout="out", stderr="bad flag"))
assert message.startswith("`dirsql query` against the bundled binary failed.")
assert "'bad flag'" in message
assert "'out'" in message


def describe_run():
def skips_cleanly_when_no_binary(capsys):
runner = mock.Mock()
Expand Down Expand Up @@ -204,25 +189,26 @@ def failing_probe_raises_the_diagnosis():
_result(0),
_result(0),
_result(0, stdout="/v/vec0\n"),
_result(1, stderr=f"failed to load extension: {STATIC_MARKER}"),
_result(1, stderr="load failed"),
]
)
with pytest.raises(ProbeError) as exc_info:
_run_with(runner)
assert "statically linked" in str(exc_info.value)
with mock.patch(DIAGNOSE, return_value="the diagnosis") as diagnose:
with pytest.raises(ProbeError, match="the diagnosis"):
_run_with(runner)
assert diagnose.call_args.args[0].stderr == "load failed"

def signal_killed_probe_raises_the_diagnosis():
runner = mock.Mock(
side_effect=[
_result(0),
_result(0),
_result(0, stdout="/v/vec0\n"),
_result(-6, stderr=f"failed to load extension: {STATIC_MARKER}"),
_result(-6, stderr="load failed"),
]
)
with pytest.raises(ProbeError) as exc_info:
_run_with(runner)
assert "statically linked" in str(exc_info.value)
with mock.patch(DIAGNOSE, return_value="the diagnosis"):
with pytest.raises(ProbeError, match="the diagnosis"):
_run_with(runner)

def rowless_probe_output_raises():
runner = mock.Mock(
Expand Down
22 changes: 22 additions & 0 deletions internals/checks/src/checks/wheel_extension_load/diagnose.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""The human-readable diagnosis for a failed wheel extension-load probe (#755)."""

from __future__ import annotations

STATIC_MARKER = "Dynamic loading not supported"


def diagnose(result) -> str:
output = f"stdout={result.stdout!r}\nstderr={result.stderr!r}"
if STATIC_MARKER in result.stderr:
return (
f"the bundled `dirsql` binary in this wheel cannot load SQLite "
f"extensions ({STATIC_MARKER!r}): it is statically linked "
"(static-pie), and a static binary cannot dlopen. The release "
"pipeline (putitoutthere `_matrix.yml`, #381) compiles the pypi "
"bundle_cli binary against the musl-mapped triple; the fix is to "
"build the wheel's bundled binary against the declared "
"*-linux-gnu triple (the wheel's manylinux tag already gates the "
"glibc floor), not to change this probe. See dirsql#755.\n"
f"{output}"
)
return f"`dirsql query` against the installed wheel failed.\n{output}"
27 changes: 27 additions & 0 deletions internals/checks/src/checks/wheel_extension_load/diagnose_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Colocated unit tests for the wheel probe's failure diagnosis (#755)."""

from unittest import mock

from checks.wheel_extension_load.diagnose import STATIC_MARKER, diagnose


def _result(returncode=0, stdout="", stderr=""):
return mock.Mock(returncode=returncode, stdout=stdout, stderr=stderr)


def describe_diagnose():
def static_binary_gets_the_dlopen_diagnosis():
message = diagnose(_result(1, stdout="", stderr=f"boom: {STATIC_MARKER}"))
assert "statically linked" in message
assert "dirsql#755" in message
assert STATIC_MARKER in message

def the_marker_matches_sqlites_literal_wording():
message = diagnose(_result(1, stderr="Error: Dynamic loading not supported"))
assert "statically linked" in message

def other_failures_get_a_generic_message():
message = diagnose(_result(1, stdout="out", stderr="config missing"))
assert message.startswith("`dirsql query` against the installed wheel failed.")
assert "'config missing'" in message
assert "'out'" in message
20 changes: 2 additions & 18 deletions internals/checks/src/checks/wheel_extension_load/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
import sys
import tempfile

from .diagnose import diagnose

PROBE_SQL = "SELECT vec_version() AS v"
CONFIG = '[[dirsql.extension]]\npath = "sqlite_vec"\nentrypoint = "sqlite3_vec_init"\n'
STATIC_MARKER = "Dynamic loading not supported"


class ProbeError(RuntimeError):
Expand Down Expand Up @@ -54,23 +55,6 @@ def write_text(path: str, content: str) -> None:
handle.write(content)


def diagnose(result) -> str:
output = f"stdout={result.stdout!r}\nstderr={result.stderr!r}"
if STATIC_MARKER in result.stderr:
return (
f"the bundled `dirsql` binary in this wheel cannot load SQLite "
f"extensions ({STATIC_MARKER!r}): it is statically linked "
"(static-pie), and a static binary cannot dlopen. The release "
"pipeline (putitoutthere `_matrix.yml`, #381) compiles the pypi "
"bundle_cli binary against the musl-mapped triple; the fix is to "
"build the wheel's bundled binary against the declared "
"*-linux-gnu triple (the wheel's manylinux tag already gates the "
"glibc floor), not to change this probe. See dirsql#755.\n"
f"{output}"
)
return f"`dirsql query` against the installed wheel failed.\n{output}"


def run(
dist_dir: str,
runner=subprocess.run,
Expand Down
43 changes: 11 additions & 32 deletions internals/checks/src/checks/wheel_extension_load/gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@
from checks.wheel_extension_load.gate import (
CONFIG,
PROBE_SQL,
STATIC_MARKER,
ProbeError,
_require_zero,
bin_subdir,
diagnose,
list_names,
run,
wheel_names,
write_text,
)

DIAGNOSE = "checks.wheel_extension_load.gate.diagnose"


def _result(returncode=0, stdout="", stderr=""):
return mock.Mock(returncode=returncode, stdout=stdout, stderr=stderr)
Expand Down Expand Up @@ -71,20 +71,6 @@ def writes_the_content(tmp_path):
assert handle.read() == "content"


def describe_diagnose():
def static_binary_gets_the_dlopen_diagnosis():
message = diagnose(_result(1, stdout="", stderr=f"boom: {STATIC_MARKER}"))
assert "statically linked" in message
assert "dirsql#755" in message
assert STATIC_MARKER in message

def other_failures_get_a_generic_message():
message = diagnose(_result(1, stdout="out", stderr="config missing"))
assert message.startswith("`dirsql query` against the installed wheel failed.")
assert "'config missing'" in message
assert "'out'" in message


def describe_run():
def skips_cleanly_when_no_wheel(capsys):
runner = mock.Mock()
Expand Down Expand Up @@ -182,27 +168,20 @@ def install_failure_raises():

def failing_probe_raises_the_diagnosis():
runner = mock.Mock(
side_effect=[
_result(0),
_result(0),
_result(1, stderr=f"failed to load extension: {STATIC_MARKER}"),
]
side_effect=[_result(0), _result(0), _result(1, stderr="load failed")]
)
with pytest.raises(ProbeError) as exc_info:
_run_with(runner)
assert "statically linked" in str(exc_info.value)
with mock.patch(DIAGNOSE, return_value="the diagnosis") as diagnose:
with pytest.raises(ProbeError, match="the diagnosis"):
_run_with(runner)
assert diagnose.call_args.args[0].stderr == "load failed"

def signal_killed_probe_raises_the_diagnosis():
runner = mock.Mock(
side_effect=[
_result(0),
_result(0),
_result(-6, stderr=f"failed to load extension: {STATIC_MARKER}"),
]
side_effect=[_result(0), _result(0), _result(-6, stderr="load failed")]
)
with pytest.raises(ProbeError) as exc_info:
_run_with(runner)
assert "statically linked" in str(exc_info.value)
with mock.patch(DIAGNOSE, return_value="the diagnosis"):
with pytest.raises(ProbeError, match="the diagnosis"):
_run_with(runner)

def rowless_probe_output_raises():
runner = mock.Mock(side_effect=[_result(0), _result(0), _result(0, stdout="[]")])
Expand Down