结论:已提交当前 Python-remap proof 链路、runtime/browser/UI/docs 相关变更;保持 L4-PYTHON-REMAP inventory baseline 不直接批量 PASS。
807 lines
27 KiB
JavaScript
807 lines
27 KiB
JavaScript
export const PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION = 1;
|
|
|
|
export const PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE = Object.freeze({
|
|
fixtureFamily: "axis/remap/stop-lookahead/nc_files",
|
|
fixtureId: "stop_lookahead_python_runtime_lifecycle",
|
|
iniPath: "axis/remap/stop-lookahead/demo.ini",
|
|
pythonPathPrepend: "python",
|
|
topLevelPath: "python/toplevel.py",
|
|
modules: [
|
|
"axis/remap/stop-lookahead/python/remap.py",
|
|
"axis/remap/stop-lookahead/python/toplevel.py",
|
|
],
|
|
callableName: "queuebuster",
|
|
expectedFirstYield: 2,
|
|
});
|
|
|
|
export const PYTHON_REMAP_LIFECYCLE_PLAN = [
|
|
{ phase: "initialize_python", method: "initializePython" },
|
|
{ phase: "apply_ini_python_path", method: "applyIniPythonPath" },
|
|
{ phase: "execute_toplevel", method: "executeTopLevel" },
|
|
{ phase: "import_module", method: "importModule", modulePath: "python/remap.py" },
|
|
{ phase: "callable_lookup", method: "lookupCallable", callableName: "queuebuster" },
|
|
{ phase: "callable_invoke", method: "invokeGenerator", callableName: "queuebuster" },
|
|
{ phase: "remap_phase_dispatch", method: "observeFirstYield", expectedYield: 2 },
|
|
{ phase: "generator_finish", method: "finishGenerator" },
|
|
{ phase: "export_interpreter_state", method: "exportInterpreterState" },
|
|
{ phase: "export_diagnostics", method: "exportDiagnostics" },
|
|
];
|
|
|
|
export const PYTHON_REMAP_ROW_RUNTIME_PROOF_PHASES = Object.freeze([
|
|
"initialize_python",
|
|
"apply_ini_python_path",
|
|
"execute_toplevel",
|
|
"import_module",
|
|
"callable_lookup",
|
|
"callable_invoke",
|
|
"remap_phase_dispatch",
|
|
"generator_finish",
|
|
"stage_ngc_remap_asset",
|
|
"reject_ngc_only_standalone",
|
|
"export_interpreter_state",
|
|
"export_canonical_events",
|
|
"export_diagnostics",
|
|
]);
|
|
|
|
function requireString(value, label) {
|
|
if (typeof value !== "string" || value.length === 0) {
|
|
throw new Error(`${label} must be a non-empty string.`);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function normalizeSourceFiles(sourceFiles = []) {
|
|
if (!Array.isArray(sourceFiles)) {
|
|
throw new Error("sourceFiles must be an array.");
|
|
}
|
|
return sourceFiles.map((sourceFile) => requireString(sourceFile, "source file"));
|
|
}
|
|
|
|
function normalizeStringList(values = [], label = "value") {
|
|
if (values === "-" || values === null || values === undefined) {
|
|
return [];
|
|
}
|
|
if (!Array.isArray(values)) {
|
|
throw new Error(`${label} must be an array.`);
|
|
}
|
|
return values
|
|
.filter((value) => value !== "-")
|
|
.map((value) => requireString(value, label));
|
|
}
|
|
|
|
function callableNameFromSpec(callableSpec) {
|
|
return requireString(callableSpec, "callable").split("@")[0];
|
|
}
|
|
|
|
function stableTextHash(text) {
|
|
let hash = 0x811c9dc5;
|
|
for (let index = 0; index < text.length; index += 1) {
|
|
hash ^= text.charCodeAt(index);
|
|
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
}
|
|
return hash.toString(16).padStart(8, "0");
|
|
}
|
|
|
|
function clonePlan(plan) {
|
|
return plan.map((step) => ({ ...step }));
|
|
}
|
|
|
|
function transcriptText(transcript) {
|
|
return transcript
|
|
.map((entry) => `${entry.phase}:${entry.status}:${entry.value ?? ""}`)
|
|
.join("\n");
|
|
}
|
|
|
|
function normalizeLifecycleResult(phase, result = {}) {
|
|
return {
|
|
phase,
|
|
status: result.status ?? "ok",
|
|
value: result.value ?? null,
|
|
ready: result.ready !== false,
|
|
source: result.source ?? "runtime-adapter",
|
|
};
|
|
}
|
|
|
|
function defaultRowRuntimePlan({
|
|
sourceFiles,
|
|
pythonRemapFunctions,
|
|
prologFunctions,
|
|
epilogFunctions,
|
|
ngcRemapFiles,
|
|
ngcOnlySubpaths,
|
|
}) {
|
|
const callables = [
|
|
...pythonRemapFunctions,
|
|
...prologFunctions,
|
|
...epilogFunctions,
|
|
];
|
|
return [
|
|
{ phase: "initialize_python", method: "initializePython" },
|
|
{ phase: "apply_ini_python_path", method: "applyIniPythonPath" },
|
|
{ phase: "execute_toplevel", method: "executeTopLevel" },
|
|
...sourceFiles.map((modulePath) => ({
|
|
phase: "import_module",
|
|
method: "importModule",
|
|
modulePath,
|
|
})),
|
|
...callables.flatMap((callableSpec) => {
|
|
const callableName = callableNameFromSpec(callableSpec);
|
|
return [
|
|
{
|
|
phase: "callable_lookup",
|
|
method: "lookupCallable",
|
|
callableName,
|
|
callableSpec,
|
|
},
|
|
{
|
|
phase: "callable_invoke",
|
|
method: "invokeGenerator",
|
|
callableName,
|
|
callableSpec,
|
|
},
|
|
{
|
|
phase: "remap_phase_dispatch",
|
|
method: "observeFirstYield",
|
|
callableName,
|
|
callableSpec,
|
|
},
|
|
{
|
|
phase: "generator_finish",
|
|
method: "finishGenerator",
|
|
callableName,
|
|
callableSpec,
|
|
},
|
|
];
|
|
}),
|
|
...ngcRemapFiles.map((ngcPath) => ({
|
|
phase: "stage_ngc_remap_asset",
|
|
method: "stageNgcRemapAsset",
|
|
ngcPath,
|
|
})),
|
|
...ngcOnlySubpaths.map((ngcPath) => ({
|
|
phase: "reject_ngc_only_standalone",
|
|
method: "rejectNgcOnlyStandalone",
|
|
ngcPath,
|
|
})),
|
|
{ phase: "export_interpreter_state", method: "exportInterpreterState" },
|
|
{ phase: "export_canonical_events", method: "exportCanonicalEvents" },
|
|
{ phase: "export_diagnostics", method: "exportDiagnostics" },
|
|
];
|
|
}
|
|
|
|
export function createPythonRemapLifecyclePlan() {
|
|
return clonePlan(PYTHON_REMAP_LIFECYCLE_PLAN);
|
|
}
|
|
|
|
export function createPythonRemapRowRuntimePlan({
|
|
sourceFiles = [],
|
|
pythonRemapFunctions = [],
|
|
prologFunctions = [],
|
|
epilogFunctions = [],
|
|
ngcRemapFiles = [],
|
|
ngcOnlySubpaths = [],
|
|
} = {}) {
|
|
return clonePlan(defaultRowRuntimePlan({
|
|
sourceFiles: normalizeStringList(sourceFiles, "source file"),
|
|
pythonRemapFunctions: normalizeStringList(pythonRemapFunctions, "Python remap function"),
|
|
prologFunctions: normalizeStringList(prologFunctions, "prolog function"),
|
|
epilogFunctions: normalizeStringList(epilogFunctions, "epilog function"),
|
|
ngcRemapFiles: normalizeStringList(ngcRemapFiles, "NGC remap file"),
|
|
ngcOnlySubpaths: normalizeStringList(ngcOnlySubpaths, "NGC-only subpath"),
|
|
}));
|
|
}
|
|
|
|
export function validatePythonRemapLifecycleTranscript(transcript) {
|
|
if (!Array.isArray(transcript)) {
|
|
throw new Error("transcript must be an array.");
|
|
}
|
|
const byPhase = new Map(transcript.map((entry) => [entry?.phase, entry]));
|
|
const requiredPhases = PYTHON_REMAP_LIFECYCLE_PLAN.map((step) => step.phase);
|
|
const missingPhases = requiredPhases.filter((phase) => !byPhase.has(phase));
|
|
const failedPhases = transcript
|
|
.filter((entry) => entry?.ready === false || entry?.status === "blocked" || entry?.status === "failed")
|
|
.map((entry) => entry.phase);
|
|
const firstYield = byPhase.get("remap_phase_dispatch")?.value ?? null;
|
|
const expectedValues = {
|
|
apply_ini_python_path: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.pythonPathPrepend,
|
|
execute_toplevel: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.topLevelPath,
|
|
import_module: "python/remap.py",
|
|
callable_lookup: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.callableName,
|
|
};
|
|
const mismatchedValues = Object.entries(expectedValues)
|
|
.filter(([phase, expectedValue]) => byPhase.get(phase)?.value !== expectedValue)
|
|
.map(([phase]) => phase);
|
|
const hasInterpreterState = byPhase.has("export_interpreter_state");
|
|
const hasDiagnostics = byPhase.has("export_diagnostics");
|
|
const ready = (
|
|
missingPhases.length === 0 &&
|
|
failedPhases.length === 0 &&
|
|
mismatchedValues.length === 0 &&
|
|
Number(firstYield) === PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.expectedFirstYield &&
|
|
hasInterpreterState &&
|
|
hasDiagnostics
|
|
);
|
|
|
|
return {
|
|
contractVersion: PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
|
|
fixtureFamily: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureFamily,
|
|
callableName: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.callableName,
|
|
requiredPhases,
|
|
observedPhases: transcript.map((entry) => entry.phase),
|
|
missingPhases,
|
|
failedPhases,
|
|
mismatchedValues,
|
|
firstYield,
|
|
hasInterpreterState,
|
|
hasDiagnostics,
|
|
lifecycleTranscriptReady: ready,
|
|
callableLookupReady: byPhase.get("callable_lookup")?.ready === true,
|
|
generatorLifecycleReady: byPhase.get("generator_finish")?.ready === true,
|
|
interpreterStateBindingReady: hasInterpreterState,
|
|
ready,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
}
|
|
|
|
export function createPythonRemapRuntimeDiagnostics({
|
|
fixtureFamily = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureFamily,
|
|
iniPath = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.iniPath,
|
|
pythonPathPrepend = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.pythonPathPrepend,
|
|
topLevelPath = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.topLevelPath,
|
|
modules = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.modules,
|
|
callableName = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.callableName,
|
|
runtimeMode = "contract-only",
|
|
runtimeExecutionReady = false,
|
|
transcript = [],
|
|
} = {}) {
|
|
const validation = validatePythonRemapLifecycleTranscript(transcript);
|
|
return {
|
|
contractVersion: PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
|
|
runtimeMode,
|
|
runtimeExecutionReady: runtimeExecutionReady === true,
|
|
fixtureFamily,
|
|
fixtureId: PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureId,
|
|
iniPath,
|
|
pythonPathPrepend,
|
|
topLevelPath,
|
|
modules: [...modules],
|
|
callableName,
|
|
lifecycleTranscriptReady: validation.lifecycleTranscriptReady,
|
|
callableLookupReady: validation.callableLookupReady,
|
|
generatorLifecycleReady: validation.generatorLifecycleReady,
|
|
interpreterStateBindingReady: validation.interpreterStateBindingReady,
|
|
transcriptHash: stableTextHash(transcriptText(transcript)),
|
|
transcriptPhaseCount: transcript.length,
|
|
linuxCncOwnedLifecycle: true,
|
|
jsCncSemantics: false,
|
|
ngcOnlySubroutinePromoted: false,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
}
|
|
|
|
export function validatePythonRemapRowRuntimeTranscript({
|
|
rowPath,
|
|
transcript,
|
|
sourceFiles = [],
|
|
pythonRemapFunctions = [],
|
|
prologFunctions = [],
|
|
epilogFunctions = [],
|
|
ngcRemapFiles = [],
|
|
ngcOnlySubpaths = [],
|
|
} = {}) {
|
|
requireString(rowPath, "rowPath");
|
|
if (!Array.isArray(transcript)) {
|
|
throw new Error("transcript must be an array.");
|
|
}
|
|
const expectedPlan = createPythonRemapRowRuntimePlan({
|
|
sourceFiles,
|
|
pythonRemapFunctions,
|
|
prologFunctions,
|
|
epilogFunctions,
|
|
ngcRemapFiles,
|
|
ngcOnlySubpaths,
|
|
});
|
|
const expectedPhases = expectedPlan.map((step) => step.phase);
|
|
const observedPhases = transcript.map((entry) => entry.phase);
|
|
const phaseCounts = new Map();
|
|
for (const phase of observedPhases) {
|
|
phaseCounts.set(phase, (phaseCounts.get(phase) ?? 0) + 1);
|
|
}
|
|
const failedPhases = transcript
|
|
.filter((entry) => entry?.ready === false || entry?.status === "blocked" || entry?.status === "failed")
|
|
.map((entry) => entry.phase);
|
|
const missingPhases = [...new Set(expectedPhases)].filter((phase) => !phaseCounts.has(phase));
|
|
const expectedModuleCount = normalizeStringList(sourceFiles, "source file").length;
|
|
const expectedCallableCount = [
|
|
...normalizeStringList(pythonRemapFunctions, "Python remap function"),
|
|
...normalizeStringList(prologFunctions, "prolog function"),
|
|
...normalizeStringList(epilogFunctions, "epilog function"),
|
|
].length;
|
|
const expectedNgcRemapCount = normalizeStringList(ngcRemapFiles, "NGC remap file").length;
|
|
const expectedNgcOnlyCount = normalizeStringList(ngcOnlySubpaths, "NGC-only subpath").length;
|
|
const countDrift = [
|
|
["import_module", expectedModuleCount],
|
|
["callable_lookup", expectedCallableCount],
|
|
["callable_invoke", expectedCallableCount],
|
|
["remap_phase_dispatch", expectedCallableCount],
|
|
["generator_finish", expectedCallableCount],
|
|
["stage_ngc_remap_asset", expectedNgcRemapCount],
|
|
["reject_ngc_only_standalone", expectedNgcOnlyCount],
|
|
].filter(([phase, expectedCount]) => (phaseCounts.get(phase) ?? 0) !== expectedCount)
|
|
.map(([phase]) => phase);
|
|
const hasInterpreterState = phaseCounts.has("export_interpreter_state");
|
|
const hasCanonicalEvents = phaseCounts.has("export_canonical_events");
|
|
const hasDiagnostics = phaseCounts.has("export_diagnostics");
|
|
const ready = (
|
|
missingPhases.length === 0 &&
|
|
failedPhases.length === 0 &&
|
|
countDrift.length === 0 &&
|
|
hasInterpreterState &&
|
|
hasCanonicalEvents &&
|
|
hasDiagnostics
|
|
);
|
|
|
|
return {
|
|
contractVersion: PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
|
|
rowPath,
|
|
requiredPhases: [...new Set(expectedPhases)],
|
|
observedPhases,
|
|
missingPhases,
|
|
failedPhases,
|
|
countDrift,
|
|
moduleImportReady: (phaseCounts.get("import_module") ?? 0) === expectedModuleCount,
|
|
callableLookupReady: (phaseCounts.get("callable_lookup") ?? 0) === expectedCallableCount,
|
|
generatorLifecycleReady: (
|
|
(phaseCounts.get("callable_invoke") ?? 0) === expectedCallableCount &&
|
|
(phaseCounts.get("remap_phase_dispatch") ?? 0) === expectedCallableCount &&
|
|
(phaseCounts.get("generator_finish") ?? 0) === expectedCallableCount
|
|
),
|
|
ngcRemapAssetsReady: (phaseCounts.get("stage_ngc_remap_asset") ?? 0) === expectedNgcRemapCount,
|
|
ngcOnlySubpathsGuarded: (phaseCounts.get("reject_ngc_only_standalone") ?? 0) === expectedNgcOnlyCount,
|
|
interpreterStateBindingReady: hasInterpreterState,
|
|
canonicalEventsReady: hasCanonicalEvents,
|
|
diagnosticsReady: hasDiagnostics,
|
|
rowRuntimeTranscriptReady: ready,
|
|
ready,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
}
|
|
|
|
export function createLinuxCncPythonRemapRuntimePort({
|
|
fixtureFamily = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.fixtureFamily,
|
|
iniPath = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.iniPath,
|
|
pythonPathPrepend = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.pythonPathPrepend,
|
|
topLevelPath = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.topLevelPath,
|
|
sourceFiles = PYTHON_REMAP_STOP_LOOKAHEAD_FIXTURE.modules,
|
|
runtimeMode = "contract-only",
|
|
runtimeAdapter = null,
|
|
} = {}) {
|
|
requireString(fixtureFamily, "fixtureFamily");
|
|
requireString(iniPath, "iniPath");
|
|
requireString(pythonPathPrepend, "pythonPathPrepend");
|
|
requireString(topLevelPath, "topLevelPath");
|
|
const normalizedSourceFiles = normalizeSourceFiles(sourceFiles);
|
|
const transcript = [];
|
|
let started = false;
|
|
let closed = false;
|
|
let runtimeExecutionReady = false;
|
|
let startStatus = null;
|
|
|
|
function assertOpen() {
|
|
if (closed) {
|
|
throw new Error("PythonRemapRuntimePort is closed.");
|
|
}
|
|
}
|
|
|
|
async function callAdapterMethod(method, payload = {}) {
|
|
if (!runtimeAdapter?.[method]) {
|
|
return normalizeLifecycleResult(payload.phase ?? method, {
|
|
status: "blocked",
|
|
ready: false,
|
|
source: "missing-runtime-adapter-method",
|
|
});
|
|
}
|
|
return normalizeLifecycleResult(payload.phase ?? method, await runtimeAdapter[method](payload));
|
|
}
|
|
|
|
async function runStep(step) {
|
|
const payload = {
|
|
...step,
|
|
fixtureFamily,
|
|
iniPath,
|
|
pythonPathPrepend,
|
|
topLevelPath,
|
|
sourceFiles: normalizedSourceFiles,
|
|
};
|
|
const result = await callAdapterMethod(step.method, payload);
|
|
transcript.push(result);
|
|
return result;
|
|
}
|
|
|
|
return {
|
|
fixtureFamily,
|
|
iniPath,
|
|
pythonPathPrepend,
|
|
topLevelPath,
|
|
sourceFiles: normalizedSourceFiles,
|
|
runtimeMode,
|
|
|
|
async start() {
|
|
assertOpen();
|
|
started = true;
|
|
let adapterStartResult = {};
|
|
if (runtimeAdapter?.start) {
|
|
adapterStartResult = await runtimeAdapter.start({
|
|
fixtureFamily,
|
|
iniPath,
|
|
pythonPathPrepend,
|
|
topLevelPath,
|
|
sourceFiles: normalizedSourceFiles,
|
|
});
|
|
}
|
|
runtimeExecutionReady = adapterStartResult.runtimeExecutionReady === true;
|
|
startStatus = adapterStartResult.status ?? (runtimeExecutionReady ? "python_remap_runtime_adapter_started" : null);
|
|
return {
|
|
runtimeMode,
|
|
runtimeExecutionReady,
|
|
status: startStatus,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
},
|
|
|
|
async initializePython() {
|
|
assertOpen();
|
|
if (!started) {
|
|
throw new Error("PythonRemapRuntimePort must be started before initializePython().");
|
|
}
|
|
return runStep({ phase: "initialize_python", method: "initializePython" });
|
|
},
|
|
|
|
async applyIniPythonPath() {
|
|
assertOpen();
|
|
if (!started) {
|
|
throw new Error("PythonRemapRuntimePort must be started before applyIniPythonPath().");
|
|
}
|
|
return runStep({ phase: "apply_ini_python_path", method: "applyIniPythonPath" });
|
|
},
|
|
|
|
async executeTopLevel() {
|
|
assertOpen();
|
|
if (!started) {
|
|
throw new Error("PythonRemapRuntimePort must be started before executeTopLevel().");
|
|
}
|
|
return runStep({ phase: "execute_toplevel", method: "executeTopLevel" });
|
|
},
|
|
|
|
async importModule(modulePath) {
|
|
assertOpen();
|
|
if (!started) {
|
|
throw new Error("PythonRemapRuntimePort must be started before importModule().");
|
|
}
|
|
return runStep({ phase: "import_module", method: "importModule", modulePath });
|
|
},
|
|
|
|
async lookupCallable(callableName) {
|
|
assertOpen();
|
|
if (!started) {
|
|
throw new Error("PythonRemapRuntimePort must be started before lookupCallable().");
|
|
}
|
|
return runStep({ phase: "callable_lookup", method: "lookupCallable", callableName });
|
|
},
|
|
|
|
async invokeGenerator(callableName, args = []) {
|
|
assertOpen();
|
|
if (!started) {
|
|
throw new Error("PythonRemapRuntimePort must be started before invokeGenerator().");
|
|
}
|
|
return runStep({ phase: "callable_invoke", method: "invokeGenerator", callableName, args });
|
|
},
|
|
|
|
async runLifecyclePlan(plan = createPythonRemapLifecyclePlan()) {
|
|
assertOpen();
|
|
if (!started) {
|
|
throw new Error("PythonRemapRuntimePort must be started before runLifecyclePlan().");
|
|
}
|
|
if (!runtimeAdapter || runtimeExecutionReady !== true) {
|
|
return {
|
|
status: runtimeAdapter ? (startStatus ?? "blocked_runtime_execution_not_ready") : "blocked_runtime_adapter_required",
|
|
plan: clonePlan(plan),
|
|
runtimeExecutionReady: false,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
}
|
|
for (const step of plan) {
|
|
await runStep(step);
|
|
}
|
|
return {
|
|
status: "runtime_adapter_lifecycle_plan_executed",
|
|
plan: clonePlan(plan),
|
|
transcript: this.exportTranscript(),
|
|
validation: validatePythonRemapLifecycleTranscript(transcript),
|
|
runtimeExecutionReady: true,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
},
|
|
|
|
exportTranscript() {
|
|
return transcript.map((entry) => ({ ...entry }));
|
|
},
|
|
|
|
exportDiagnostics() {
|
|
return createPythonRemapRuntimeDiagnostics({
|
|
fixtureFamily,
|
|
iniPath,
|
|
pythonPathPrepend,
|
|
topLevelPath,
|
|
modules: normalizedSourceFiles,
|
|
runtimeMode,
|
|
runtimeExecutionReady,
|
|
transcript,
|
|
});
|
|
},
|
|
|
|
async close() {
|
|
if (runtimeAdapter?.close) {
|
|
await runtimeAdapter.close();
|
|
}
|
|
closed = true;
|
|
return {
|
|
closed,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
export function createLinuxCncPythonRemapRowRuntimePort({
|
|
path,
|
|
iniPath,
|
|
family = null,
|
|
machineRoot = "",
|
|
pythonPathPrepend = "-",
|
|
topLevelPath = "-",
|
|
sourceFiles = [],
|
|
remapDeclarations = [],
|
|
pythonRemapFunctions = [],
|
|
prologFunctions = [],
|
|
epilogFunctions = [],
|
|
ngcRemapFiles = [],
|
|
ngcOnlySubpaths = [],
|
|
runtimeMode = "row-runtime-contract-only",
|
|
runtimeAdapter = null,
|
|
} = {}) {
|
|
requireString(path, "path");
|
|
requireString(iniPath, "iniPath");
|
|
const normalizedFamily = family ?? path.split("/").slice(0, -1).join("/");
|
|
requireString(normalizedFamily, "family");
|
|
const normalizedMachineRoot = machineRoot === "-" ? "" : String(machineRoot ?? "");
|
|
const normalizedPythonPathPrepend = pythonPathPrepend === "-" ? "" : String(pythonPathPrepend ?? "");
|
|
const normalizedTopLevelPath = topLevelPath === "-" ? "" : String(topLevelPath ?? "");
|
|
const normalizedSourceFiles = normalizeStringList(sourceFiles, "source file");
|
|
const normalizedRemapDeclarations = normalizeStringList(remapDeclarations, "remap declaration");
|
|
const normalizedPythonRemapFunctions = normalizeStringList(pythonRemapFunctions, "Python remap function");
|
|
const normalizedPrologFunctions = normalizeStringList(prologFunctions, "prolog function");
|
|
const normalizedEpilogFunctions = normalizeStringList(epilogFunctions, "epilog function");
|
|
const normalizedNgcRemapFiles = normalizeStringList(ngcRemapFiles, "NGC remap file");
|
|
const normalizedNgcOnlySubpaths = normalizeStringList(ngcOnlySubpaths, "NGC-only subpath");
|
|
const transcript = [];
|
|
let started = false;
|
|
let closed = false;
|
|
let runtimeExecutionReady = false;
|
|
let startStatus = null;
|
|
|
|
function assertOpen() {
|
|
if (closed) {
|
|
throw new Error("PythonRemapRowRuntimePort is closed.");
|
|
}
|
|
}
|
|
|
|
async function callAdapterMethod(method, payload = {}) {
|
|
if (!runtimeAdapter?.[method]) {
|
|
return normalizeLifecycleResult(payload.phase ?? method, {
|
|
status: "blocked",
|
|
ready: false,
|
|
source: "missing-runtime-adapter-method",
|
|
});
|
|
}
|
|
return normalizeLifecycleResult(payload.phase ?? method, await runtimeAdapter[method](payload));
|
|
}
|
|
|
|
async function runStep(step) {
|
|
const payload = {
|
|
...step,
|
|
path,
|
|
iniPath,
|
|
family: normalizedFamily,
|
|
machineRoot: normalizedMachineRoot,
|
|
pythonPathPrepend: normalizedPythonPathPrepend,
|
|
topLevelPath: normalizedTopLevelPath,
|
|
sourceFiles: normalizedSourceFiles,
|
|
remapDeclarations: normalizedRemapDeclarations,
|
|
pythonRemapFunctions: normalizedPythonRemapFunctions,
|
|
prologFunctions: normalizedPrologFunctions,
|
|
epilogFunctions: normalizedEpilogFunctions,
|
|
ngcRemapFiles: normalizedNgcRemapFiles,
|
|
ngcOnlySubpaths: normalizedNgcOnlySubpaths,
|
|
};
|
|
const result = await callAdapterMethod(step.method, payload);
|
|
transcript.push(result);
|
|
return result;
|
|
}
|
|
|
|
function rowPlan() {
|
|
return createPythonRemapRowRuntimePlan({
|
|
sourceFiles: normalizedSourceFiles,
|
|
pythonRemapFunctions: normalizedPythonRemapFunctions,
|
|
prologFunctions: normalizedPrologFunctions,
|
|
epilogFunctions: normalizedEpilogFunctions,
|
|
ngcRemapFiles: normalizedNgcRemapFiles,
|
|
ngcOnlySubpaths: normalizedNgcOnlySubpaths,
|
|
});
|
|
}
|
|
|
|
function validation() {
|
|
return validatePythonRemapRowRuntimeTranscript({
|
|
rowPath: path,
|
|
transcript,
|
|
sourceFiles: normalizedSourceFiles,
|
|
pythonRemapFunctions: normalizedPythonRemapFunctions,
|
|
prologFunctions: normalizedPrologFunctions,
|
|
epilogFunctions: normalizedEpilogFunctions,
|
|
ngcRemapFiles: normalizedNgcRemapFiles,
|
|
ngcOnlySubpaths: normalizedNgcOnlySubpaths,
|
|
});
|
|
}
|
|
|
|
return {
|
|
path,
|
|
iniPath,
|
|
family: normalizedFamily,
|
|
machineRoot: normalizedMachineRoot,
|
|
pythonPathPrepend: normalizedPythonPathPrepend,
|
|
topLevelPath: normalizedTopLevelPath,
|
|
sourceFiles: normalizedSourceFiles,
|
|
remapDeclarations: normalizedRemapDeclarations,
|
|
pythonRemapFunctions: normalizedPythonRemapFunctions,
|
|
prologFunctions: normalizedPrologFunctions,
|
|
epilogFunctions: normalizedEpilogFunctions,
|
|
ngcRemapFiles: normalizedNgcRemapFiles,
|
|
ngcOnlySubpaths: normalizedNgcOnlySubpaths,
|
|
runtimeMode,
|
|
|
|
createRowRuntimePlan: rowPlan,
|
|
|
|
async start() {
|
|
assertOpen();
|
|
started = true;
|
|
let adapterStartResult = {};
|
|
if (runtimeAdapter?.start) {
|
|
adapterStartResult = await runtimeAdapter.start({
|
|
path,
|
|
iniPath,
|
|
family: normalizedFamily,
|
|
machineRoot: normalizedMachineRoot,
|
|
pythonPathPrepend: normalizedPythonPathPrepend,
|
|
topLevelPath: normalizedTopLevelPath,
|
|
sourceFiles: normalizedSourceFiles,
|
|
remapDeclarations: normalizedRemapDeclarations,
|
|
pythonRemapFunctions: normalizedPythonRemapFunctions,
|
|
prologFunctions: normalizedPrologFunctions,
|
|
epilogFunctions: normalizedEpilogFunctions,
|
|
ngcRemapFiles: normalizedNgcRemapFiles,
|
|
ngcOnlySubpaths: normalizedNgcOnlySubpaths,
|
|
});
|
|
}
|
|
runtimeExecutionReady = adapterStartResult.runtimeExecutionReady === true;
|
|
startStatus = adapterStartResult.status ?? (runtimeExecutionReady ? "python_remap_row_runtime_adapter_started" : null);
|
|
return {
|
|
runtimeMode,
|
|
runtimeExecutionReady,
|
|
status: startStatus,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
},
|
|
|
|
async runRowRuntimePlan(plan = rowPlan()) {
|
|
assertOpen();
|
|
if (!started) {
|
|
throw new Error("PythonRemapRowRuntimePort must be started before runRowRuntimePlan().");
|
|
}
|
|
if (!runtimeAdapter || runtimeExecutionReady !== true) {
|
|
return {
|
|
status: runtimeAdapter ? (startStatus ?? "blocked_row_runtime_execution_not_ready") : "blocked_row_runtime_adapter_required",
|
|
plan: clonePlan(plan),
|
|
runtimeExecutionReady: false,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
}
|
|
for (const step of plan) {
|
|
await runStep(step);
|
|
}
|
|
return {
|
|
status: "row_runtime_adapter_plan_executed",
|
|
plan: clonePlan(plan),
|
|
transcript: this.exportTranscript(),
|
|
validation: validation(),
|
|
runtimeExecutionReady: true,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
},
|
|
|
|
exportTranscript() {
|
|
return transcript.map((entry) => ({ ...entry }));
|
|
},
|
|
|
|
exportDiagnostics() {
|
|
const rowValidation = validation();
|
|
return {
|
|
contractVersion: PYTHON_REMAP_RUNTIME_PORT_CONTRACT_VERSION,
|
|
runtimeMode,
|
|
runtimeExecutionReady,
|
|
path,
|
|
iniPath,
|
|
family: normalizedFamily,
|
|
machineRoot: normalizedMachineRoot,
|
|
pythonPathPrepend: normalizedPythonPathPrepend,
|
|
topLevelPath: normalizedTopLevelPath,
|
|
sourceFiles: [...normalizedSourceFiles],
|
|
remapDeclarations: [...normalizedRemapDeclarations],
|
|
pythonRemapFunctions: [...normalizedPythonRemapFunctions],
|
|
prologFunctions: [...normalizedPrologFunctions],
|
|
epilogFunctions: [...normalizedEpilogFunctions],
|
|
ngcRemapFiles: [...normalizedNgcRemapFiles],
|
|
ngcOnlySubpaths: [...normalizedNgcOnlySubpaths],
|
|
rowRuntimeTranscriptReady: rowValidation.rowRuntimeTranscriptReady,
|
|
moduleImportReady: rowValidation.moduleImportReady,
|
|
callableLookupReady: rowValidation.callableLookupReady,
|
|
generatorLifecycleReady: rowValidation.generatorLifecycleReady,
|
|
ngcRemapAssetsReady: rowValidation.ngcRemapAssetsReady,
|
|
ngcOnlySubpathsGuarded: rowValidation.ngcOnlySubpathsGuarded,
|
|
interpreterStateBindingReady: rowValidation.interpreterStateBindingReady,
|
|
canonicalEventsReady: rowValidation.canonicalEventsReady,
|
|
transcriptHash: stableTextHash(transcriptText(transcript)),
|
|
transcriptPhaseCount: transcript.length,
|
|
linuxCncOwnedLifecycle: true,
|
|
jsCncSemantics: false,
|
|
ngcOnlySubroutinePromoted: false,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
},
|
|
|
|
async close() {
|
|
if (runtimeAdapter?.close) {
|
|
await runtimeAdapter.close();
|
|
}
|
|
closed = true;
|
|
return {
|
|
closed,
|
|
executionEnabled: false,
|
|
promotionAllowed: false,
|
|
bulkPromotionAllowed: false,
|
|
};
|
|
},
|
|
};
|
|
}
|