Engine: python.output switches a Python cell between Python Object and Excel Value

A Python cell's return type had no command of its own: python.set takes
it, but only together with the code. python.output {cell?, mode?} sets
it on its own; its command entry lists Excel's shortcut,
Ctrl+Alt+Shift+M. Without a mode it switches to the other one, so the
shortcut needs no parameters; a cell that isn't a Python cell is an
error. A cached result belongs to the return type it was made for, so
a switch treats it as python.set treats a new return type: the result
goes, the cell shows #BLOCKED! until it runs, and its old spill goes
with what read it recalculated. Both commands write the cell through
one path, so python.output refuses a table header and a locked cell on
a protected sheet as python.set does. Each switch is one undo step, and
setting the mode a cell already has adds none.

python.set now refuses an output that isn't text instead of ignoring it.
The command-sequence fuzzer runs python.output too. A macro recording
keeps the return type a switch without a mode set, as its mode (as Bold
records on), also for a switch inside a recorded script run, so playing
it back sets that type rather than switching again.

Tests: switching without a mode drops the cached result, its spill and
the value its reader saw, the same as python.set with the other return
type; switching back brings no result back; the code is kept; one undo
step per switch, undo brings the result back, and none for the mode a
cell already has; a cell without a result stays #BLOCKED!; the
refusals (no Python cell, a bad or non-text mode, a bad cell, a table
header, a locked cell, a non-text output for python.set), an unlocked
Hidden cell on a protected sheet switches, and a recorded switch played
back on a cell that already has the new type keeps it.
This commit is contained in:
prxnvt
2026-11-01 20:13:08 -05:00
parent c1cc8264fe
commit 8b96d30c2b
6 changed files with 235 additions and 25 deletions
@@ -793,6 +793,7 @@ const CURATED: &[(&str, usize)] = &[
("formulas.deleteName", 1),
("edit.replace", 1),
("python.set", 3),
("python.output", 2),
];
fn generate(rng: &mut Rng, s: &Session) -> Step {
@@ -845,6 +846,14 @@ fn curated_params(rng: &mut Rng, wb: &Workbook, id: &str) -> Json {
}
p
}
"python.output" => {
// Mostly the cells python.set writes to; a missing mode switches.
let mut p = json!({"cell": near_cell(rng).a1()});
if rng.chance(50) {
p["mode"] = json!(if rng.chance(50) { "object" } else { "value" });
}
p
}
"range.fill" => json!({"range": near_range(rng).a1(), "input": input(rng, wb)}),
"selection.set" => {
let mut p = json!({"range": range(rng)});