diff --git a/crates/engine/src/cmd/python.rs b/crates/engine/src/cmd/python.rs index dc27e2f82..8e7007ea1 100644 --- a/crates/engine/src/cmd/python.rs +++ b/crates/engine/src/cmd/python.rs @@ -68,8 +68,10 @@ fn set(s: &mut Session, p: &Json) -> Result { if header { return Err(EngineError::Other(format!("{} is a table header; Python cells can't go in a table's header row", at.a1()))); } - // Unchanged code keeps its result (and its value); new code has none until it runs. - let keep = old.filter(|o| o.code == py.code); + // Unchanged code with the same return type keeps its result (and its value); new code, or a + // new return type, has none until it runs: a result of the other type would show a value and + // preview that don't belong to the cell. + let keep = old.filter(|o| o.code == py.code && o.object == py.object); let value = match &keep { Some(o) => { py.output = o.output.clone(); diff --git a/crates/engine/src/tests/python_cells.rs b/crates/engine/src/tests/python_cells.rs index d0976b7a0..92efdad29 100644 --- a/crates/engine/src/tests/python_cells.rs +++ b/crates/engine/src/tests/python_cells.rs @@ -373,7 +373,7 @@ fn save_model_asks_before_it_writes_over_a_python_cell_that_shows_nothing() { } /// A1 is a Python cell whose kept result `[5, 6]` spills over A1:A2, and B1 reads A2. -fn spill_with_a_reader() -> crate::Session { +pub(super) fn spill_with_a_reader() -> crate::Session { let mut s = s(); s.execute("python.set", json!({"cell": "A1", "code": "[5, 6]"})).unwrap(); { diff --git a/crates/engine/src/tests/python_files.rs b/crates/engine/src/tests/python_files.rs index 4317bbb6b..e04d6a7da 100644 --- a/crates/engine/src/tests/python_files.rs +++ b/crates/engine/src/tests/python_files.rs @@ -622,3 +622,27 @@ fn a_formula_entered_in_a_python_spill_takes_the_area_as_a_formula_would() { assert_eq!(wb.active().unwrap().value(c), fresh.active().unwrap().value(c), "{c}"); } } + +#[test] +fn a_new_return_type_drops_the_kept_result() { + // Same code, the other return type: the result belongs to the old type, so it goes. + let mut s = super::python_cells::spill_with_a_reader(); + s.execute("python.set", json!({"cell": "A1", "code": "[5, 6]", "output": "object"})).unwrap(); + let a1 = code(&s, "A1").unwrap(); + assert!(a1.object && a1.output.is_none(), "no result of the other type is kept"); + assert_eq!(v(&s, "A1"), Value::Error(CellError::Blocked)); + assert_eq!(v(&s, "A2"), Value::Empty, "no spill"); + assert!(s.doc().unwrap().wb.active().unwrap().spill_ranges.is_empty()); + assert_eq!(v(&s, "B1"), Value::Number(0.0), "the reader of the old spill is recalculated"); + let xml = sheet_xml(&save(&mut s, "xlsx")); + assert!(xml.contains("_xlfn._xlws.PY(0,1)#VALUE!") && !xml.contains("r=\"A2\""), "no old cached value is written: {xml}"); + // Undo brings the result back. + s.execute("edit.undo", json!({})).unwrap(); + let a1 = code(&s, "A1").unwrap(); + assert!(!a1.object && a1.output.is_some()); + assert_eq!((v(&s, "A1"), v(&s, "A2"), v(&s, "B1")), (Value::Number(5.0), Value::Number(6.0), Value::Number(6.0))); + // Same code, same return type: the result is kept as before. + s.execute("python.set", json!({"cell": "A1", "code": "[5, 6]", "output": "value"})).unwrap(); + assert!(code(&s, "A1").unwrap().output.is_some()); + assert_eq!((v(&s, "A1"), v(&s, "A2"), v(&s, "B1")), (Value::Number(5.0), Value::Number(6.0), Value::Number(6.0))); +}