Skip to content

Fix Col.prev()/next() losing cross-frame reference, value_color() chaining, Neg type hint#1

Closed
staging-devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1774297734-fix-col-prev-next-from-param
Closed

Fix Col.prev()/next() losing cross-frame reference, value_color() chaining, Neg type hint#1
staging-devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1774297734-fix-col-prev-next-from-param

Conversation

@staging-devin-ai-integration

@staging-devin-ai-integration staging-devin-ai-integration Bot commented Mar 23, 2026

Copy link
Copy Markdown

Summary

Fixes three bugs found during code audit:

  1. Col.prev() and Col.next() silently drop from_ parameter (logic bug, highest impact): When a Col is created with from_=other_frame to reference a different ExcelFrame, calling .prev(1) or .next(1) would create a new Col without passing from_ through. This caused cross-frame references to silently reference the wrong ExcelFrame, producing incorrect formulas. Introduced in commit ab6d4f0 when from_ was added to Col.

  2. TableStyler.value_color() missing return self: All other fmt_* methods return Self for method chaining, but value_color() returned None, breaking chains like styler.value_color(...).fmt_number(...).

  3. Neg.compute() return type annotation -> None: Should be -> Any like all other compute() methods. The function actually returns a numeric value.

Review & Testing Checklist for Human

  • Verify cross-frame prev()/next() works in a real multi-table scenario (e.g., the DCF or mortgage example with cross-table references using .prev())
  • Verify value_color() chaining works: styler.value_color(columns=["x"]).fmt_number(columns=["y"])
  • Run pytest test/ and pyright — both pass cleanly

Notes

  • All 14 tests pass (8 existing + 6 new)
  • Pyright reports 0 errors
  • Pre-commit hooks pass

Link to Devin session: https://staging.itsdev.in/sessions/bffcb71e53fc4b468c099a4abebd2377
Requested by: @yjhan96


Staging: Open in Devin

…eturn, Neg.compute() type hint

- Col.prev() and Col.next() now preserve the from_ parameter for cross-frame
  references. Previously, calling .prev(1) or .next(1) on a Col with from_=
  some_other_frame would silently drop the cross-frame reference, causing
  formulas to reference the wrong ExcelFrame.
- TableStyler.value_color() now returns Self to enable method chaining,
  consistent with all other fmt_* methods.
- Fixed Neg.compute() return type annotation from None to Any.
- Added tests for cross-frame prev/next and value_color chaining.

Co-Authored-By: albert.han+coggittest@windsurf.com <yjhan96@gmail.com>
@staging-devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Comment thread src/excelify/_expr.py

def prev(self, offset: int) -> "Col":
return Col(self._col_name, offset=-offset)
return Col(self._col_name, from_=self.from_, offset=-offset)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we adding this param here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this fix, prev() and next() were creating a new Col without forwarding self.from_. This means if you had a cross-frame reference like:

el.col("price", from_=other_frame).prev(1)

The .prev(1) call would produce Col("price", offset=-1) — note from_ defaults to None. So instead of looking up the previous row in other_frame, it would look up the previous row in the current frame, which either references the wrong data or fails silently (e.g., if the current frame doesn't even have a "price" column).

Adding from_=self.from_ ensures the cross-frame reference is preserved through prev()/next() calls. Without from_, these methods are only correct for same-frame references.

@staging-devin-ai-integration staging-devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Staging: Open in Devin
Debug

Playground

@staging-devin-ai-integration

Copy link
Copy Markdown
Author

Review Summary

All three fixes are correct, well-scoped, and well-tested. CI is green. LGTM — ready to merge.

Fix 1: Col.prev()/Col.next() losing from_ (highest impact)

The bug is real. get_cell_expr uses self.from_ to resolve the source frame, but prev()/next() created a new Col without forwarding it — silently reverting cross-frame references back to the current frame. The fix (from_=self.from_) is the correct one-line change in both methods.

Fix 2: TableStyler.value_color() missing return self

Confirmed: every other mutating method on TableStyler (fmt_number, fmt_currency, fmt_percent, rename_columns, etc.) returns Self for chaining. value_color() was the only outlier. Fix is straightforward.

Fix 3: Neg.compute() type hint -> None-> Any

All 17 other compute() implementations annotate -> Any. Neg.compute() was the sole -> None, which is incorrect since it returns -value. Pyright would flag callers that rely on the return value.

Tests

The 6 new tests in test_cross_frame_ref.py provide good coverage:

  • Unit tests for from_ preservation through prev()/next()
  • End-to-end cross-frame formula evaluation tests (verifying computed values)
  • Regression test that from_=None (same-frame) still works
  • value_color() chaining test

Minor observation (not blocking)

prev()/next() don't accumulate offsets — Col("x", offset=2).prev(1) produces offset=-1, not offset=1. This is pre-existing behavior and out of scope for this PR, but worth noting for a potential follow-up if offset chaining is ever desired.

@staging-devin-ai-integration

Copy link
Copy Markdown
Author

Closing due to inactivity for more than 7 days. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant