Skip to content

Fix filldown RuntimeError on header-only tables#698

Open
sarathfrancis90 wants to merge 1 commit into
petl-developers:masterfrom
sarathfrancis90:fix-filldown-header-only
Open

Fix filldown RuntimeError on header-only tables#698
sarathfrancis90 wants to merge 1 commit into
petl-developers:masterfrom
sarathfrancis90:fix-filldown-header-only

Conversation

@sarathfrancis90

Copy link
Copy Markdown

filldown reads the first data row up front to seed its fill values, so a table that has a header but no data rows blows up with RuntimeError: generator raised StopIteration instead of just passing the header through. fillright and fillleft don't have this problem — they return the header unchanged — so it's really an inconsistency between the three fill transforms.

It's easy to hit, e.g. after a select that filters everything out:

>>> import petl as etl
>>> empty = etl.select([['foo', 'bar'], [1, 2]], lambda r: r.foo > 100)
>>> list(etl.filldown(empty))
RuntimeError: generator raised StopIteration

Same PEP 479 class as the iterrowslice fix in #613: the StopIteration from next() escapes the generator. I wrapped the seed read in the same try/except StopIteration: return the function already uses for the header read just above it, so a header-only table now yields just the header like the other fill transforms.

Added a regression test next to the existing *_headerless ones and a changelog note. Verified with pytest --cov=petl petl and pytest --doctest-modules petl (all green) plus the sphinx-build -W docs build.

filldown eagerly reads the first data row to seed its fill values, so a
table with a header but no data rows raised
"RuntimeError: generator raised StopIteration" (the StopIteration from
next() escaping the generator under PEP 479). fillright and fillleft
already handle that input by returning the header unchanged; guard the
seed read so filldown behaves the same.
@sarathfrancis90 sarathfrancis90 force-pushed the fix-filldown-header-only branch from 64219c7 to 49511d9 Compare June 29, 2026 07:57
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Fix filldown StopIteration crash on header-only tables
🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 10-20 Minutes

Grey Divider

Description

• Guard filldown’s initial seed-row read so header-only tables don’t raise RuntimeError.
• Add regression test to ensure header-only input passes through unchanged.
• Note the behavioral fix in the 1.7.17 changelog.
Diagram

graph TD
  A["Client code"] --> B(["filldown() API"]) --> C["FillDownView"] --> D(["iterfilldown()"]) --> E[("Input table iterator")] --> F[("Output rows")]
  T["test_fills.py"] --> B
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Lazy seed initialization inside the main loop
  • ➕ Avoids a separate next(it) call and its StopIteration handling
  • ➕ Keeps all row processing in one loop, potentially simpler to reason about
  • ➖ Requires additional branching to detect and handle the first data row
  • ➖ Slightly larger refactor for a small behavioral fix
2. Use itertools.tee (peek) to avoid consuming the iterator
  • ➕ Can “peek” at the first data row without advancing the original iterator
  • ➖ Extra memory/overhead and complexity for minimal benefit
  • ➖ Still needs explicit handling for the no-data case

Recommendation: The current approach (catching StopIteration around the seed-row next(it)) is the best fit: it is minimal, consistent with the existing header read guard, aligns filldown behavior with fillright/fillleft on header-only input, and avoids a larger refactor.

Files changed (3) +22 / -1

Bug fix (1) +4 / -1
fills.pyHandle missing seed row in iterfilldown to avoid PEP 479 RuntimeError +4/-1

Handle missing seed row in iterfilldown to avoid PEP 479 RuntimeError

• Wraps the initial seed-row read (next(it)) in a try/except StopIteration. For header-only tables, the generator now exits cleanly after yielding the header instead of leaking StopIteration and triggering a RuntimeError under PEP 479.

petl/transform/fills.py

Tests (1) +13 / -0
test_fills.pyAdd regression test for filldown on header-only tables +13/-0

Add regression test for filldown on header-only tables

• Introduces test_filldown_header_only to ensure filldown returns the header unchanged when there are no data rows. Verifies behavior both with default fill fields and with an explicit field selection.

petl/test/transform/test_fills.py

Documentation (1) +5 / -0
changes.rstDocument filldown header-only behavior fix in changelog +5/-0

Document filldown header-only behavior fix in changelog

• Adds a 1.7.17 changelog entry noting that filldown no longer raises RuntimeError for header-only tables. Clarifies behavior consistency with fillright/fillleft.

docs/changes.rst

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Qodo Logo

@sarathfrancis90

Copy link
Copy Markdown
Author

Heads up that the red on the Python 3.8/3.9 jobs is AttributeError: ignore_egg_info_in_manifest during pip install -e . (egg-info generation) — a recent setuptools release triggers it on those legacy interpreters via the vcs-versioning backend. It happens before the tests run and hits every PR's 3.8/3.9 matrix entries, not this change; the fix is green on 3.10–3.14 (and locally: 572 tests + 725 doctests). Glad to send a separate PR pinning setuptools for the 3.8/3.9 jobs if that'd help.

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