Files
cnc_wams/wasm-port/tests/native/probe_python_remap_runtime.sh

190 lines
6.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
LINUXCNC_ROOT="${LINUXCNC_ROOT:-$ROOT_DIR/../linuxcnc}"
FIXTURE_FAMILY="axis/remap/stop-lookahead/nc_files"
FIXTURE_DIR="$LINUXCNC_ROOT/configs/sim/axis/remap/stop-lookahead"
FIXTURE_INI="$FIXTURE_DIR/demo.ini"
RUN_DIR="$ROOT_DIR/build/native/python-remap-runtime"
PYTHON_RUNTIME_STDOUT="$RUN_DIR/python_lifecycle.stdout.log"
PYTHON_RUNTIME_STDERR="$RUN_DIR/python_lifecycle.stderr.log"
required_commands=(python3 linuxcnc)
missing_requirements=()
command_path() {
command -v "$1" 2>/dev/null || true
}
csv_join() {
local IFS=,
printf '%s' "$*"
}
print_kv() {
printf '%s=%s\n' "$1" "$2"
}
runtime_requirements=()
for command_name in "${required_commands[@]}"; do
path="$(command_path "$command_name")"
if [[ -n "$path" ]]; then
runtime_requirements+=("$command_name:1")
print_kv "python_remap_runtime_${command_name}_path" "$path"
else
runtime_requirements+=("$command_name:0")
missing_requirements+=("$command_name")
fi
done
source_ready=1
for rel in \
src/emc/rs274ngc/interp_python.cc \
src/emc/pythonplugin/python_plugin.cc \
configs/sim/axis/remap/stop-lookahead/demo.ini \
configs/sim/axis/remap/stop-lookahead/python/remap.py \
configs/sim/axis/remap/stop-lookahead/python/toplevel.py; do
if [[ ! -f "$LINUXCNC_ROOT/$rel" ]]; then
source_ready=0
missing_requirements+=("$rel")
print_kv "python_remap_missing_source_${rel//[^A-Za-z0-9]/_}" "$LINUXCNC_ROOT/$rel"
fi
done
runtime_ready=0
if [[ "${#missing_requirements[@]}" -eq 0 ]]; then
runtime_ready=1
fi
print_kv python_remap_runtime_fixture_family "$FIXTURE_FAMILY"
print_kv python_remap_runtime_fixture_id "stop_lookahead_python_runtime_lifecycle"
print_kv python_remap_runtime_fixture_scope "no_python_callables_no_ngc_only_subpaths"
print_kv python_remap_runtime_phases "initialize_python,apply_ini_python_path,execute_toplevel,callable_lookup,pycall_dispatch,callable_invoke,remap_phase_dispatch,generator_finish,execute_python_runtime,reload_on_change"
print_kv python_remap_runtime_modules "axis/remap/stop-lookahead/python/remap.py,axis/remap/stop-lookahead/python/toplevel.py"
print_kv python_remap_runtime_requirements "$(csv_join "${runtime_requirements[@]}")"
if [[ "${#missing_requirements[@]}" -eq 0 ]]; then
print_kv python_remap_missing_requirements "-"
else
print_kv python_remap_missing_requirements "$(csv_join "${missing_requirements[@]}")"
fi
print_kv python_remap_source_proof_ready "$source_ready"
print_kv python_remap_runtime_ready "$runtime_ready"
print_kv python_remap_execution_enabled 0
print_kv python_remap_promotion_allowed 0
if [[ "$source_ready" != "1" ]]; then
print_kv python_remap_runtime_probe_status "blocked_missing_source"
exit 0
fi
if [[ "$runtime_ready" != "1" ]]; then
print_kv python_remap_runtime_probe_status "skipped_missing_host_runtime"
exit 0
fi
if [[ "${ENABLE_PYTHON_REMAP_RUNTIME_PROBE:-0}" != "1" ]]; then
print_kv python_remap_runtime_probe_status "ready_disabled_by_default"
print_kv python_remap_runtime_probe_note "set_ENABLE_PYTHON_REMAP_RUNTIME_PROBE_1_to_run_python_lifecycle_probe"
exit 0
fi
mkdir -p "$RUN_DIR"
rm -f "$PYTHON_RUNTIME_STDOUT" "$PYTHON_RUNTIME_STDERR"
print_kv python_remap_runtime_probe_fixture "$FIXTURE_DIR"
print_kv python_remap_runtime_probe_ini "$FIXTURE_INI"
print_kv python_remap_runtime_probe_stdout "$PYTHON_RUNTIME_STDOUT"
print_kv python_remap_runtime_probe_stderr "$PYTHON_RUNTIME_STDERR"
if python3 - "$LINUXCNC_ROOT" "$FIXTURE_DIR" "$FIXTURE_INI" "$RUN_DIR" \
>"$PYTHON_RUNTIME_STDOUT" 2>"$PYTHON_RUNTIME_STDERR" <<'PY'
import configparser
import importlib
import inspect
import os
from pathlib import Path
import re
import sys
import tempfile
linuxcnc_root = Path(sys.argv[1])
fixture_dir = Path(sys.argv[2])
fixture_ini = Path(sys.argv[3])
run_dir = Path(sys.argv[4])
def emit(name, value):
print(f"{name}={value}")
def require(condition, message):
if not condition:
raise RuntimeError(message)
interp_return = linuxcnc_root / "src/emc/nml_intf/interp_return.hh"
match = re.search(
r"INTERP_EXECUTE_FINISH\s*=\s*([0-9]+)",
interp_return.read_text(encoding="utf-8"),
)
require(match, "missing_INTERP_EXECUTE_FINISH_source_value")
execute_finish = int(match.group(1))
emit("python_remap_lifecycle_execute_finish_source_value", execute_finish)
stub_dir = Path(tempfile.mkdtemp(prefix="python-remap-interpreter-", dir=run_dir))
(stub_dir / "interpreter.py").write_text(
f"INTERP_EXECUTE_FINISH = {execute_finish}\n",
encoding="utf-8",
)
parser = configparser.ConfigParser(strict=False)
parser.optionxform = str
parser.read(fixture_ini)
require(parser.has_section("PYTHON"), "missing_PYTHON_section")
path_prepend = parser.get("PYTHON", "PATH_PREPEND", fallback="")
toplevel = parser.get("PYTHON", "TOPLEVEL", fallback="")
require(path_prepend == "python", f"PATH_PREPEND_drift:{path_prepend}")
require(toplevel == "python/toplevel.py", f"TOPLEVEL_drift:{toplevel}")
emit("python_remap_lifecycle_ini_path_prepend", path_prepend)
emit("python_remap_lifecycle_ini_toplevel", toplevel)
sys.path.insert(0, str(stub_dir))
sys.path.insert(0, str(fixture_dir / path_prepend))
sys.path.insert(0, str(linuxcnc_root / "lib/python"))
import interpreter # noqa: E402
require(interpreter.INTERP_EXECUTE_FINISH == execute_finish, "interpreter_sentinel_drift")
emit("python_remap_lifecycle_interpreter_sentinel_ok", 1)
toplevel_module = importlib.import_module(Path(toplevel).stem)
emit("python_remap_lifecycle_toplevel_imported", int(toplevel_module.__name__ == "toplevel"))
remap_module = importlib.import_module("remap")
emit("python_remap_lifecycle_remap_imported", int(remap_module.__name__ == "remap"))
require(hasattr(remap_module, "queuebuster"), "missing_queuebuster_callable")
require(callable(remap_module.queuebuster), "queuebuster_not_callable")
emit("python_remap_lifecycle_callable_lookup_ok", 1)
result = remap_module.queuebuster(object())
require(inspect.isgenerator(result), "queuebuster_not_generator")
emit("python_remap_lifecycle_generator_returned", 1)
first_yield = next(result)
require(first_yield == execute_finish, f"queuebuster_yield_drift:{first_yield}")
emit("python_remap_lifecycle_generator_first_yield", first_yield)
try:
next(result)
raise RuntimeError("queuebuster_generator_did_not_finish")
except StopIteration:
emit("python_remap_lifecycle_generator_finish_ok", 1)
emit("python_remap_runtime_lifecycle_probe_ok", 1)
PY
then
cat "$PYTHON_RUNTIME_STDOUT"
print_kv python_remap_runtime_probe_status "runtime_lifecycle_probe_passed"
print_kv python_remap_runtime_probe_note "linuxcnc_stop_lookahead_python_lifecycle_probe_passed_without_promotion"
exit 0
fi
cat "$PYTHON_RUNTIME_STDOUT" || true
print_kv python_remap_runtime_probe_status "runtime_lifecycle_probe_failed"
print_kv python_remap_runtime_probe_note "linuxcnc_stop_lookahead_python_lifecycle_probe_failed"
exit 1