Skip to content

Fix TypeError when stacking two headerless Datasets with stack_cols#651

Open
vineethsaivs wants to merge 1 commit into
jazzband:masterfrom
vineethsaivs:fix/stack-cols-headerless
Open

Fix TypeError when stacking two headerless Datasets with stack_cols#651
vineethsaivs wants to merge 1 commit into
jazzband:masterfrom
vineethsaivs:fix/stack-cols-headerless

Conversation

@vineethsaivs

Copy link
Copy Markdown
Contributor

Problem

Dataset.stack_cols() crashes with an opaque TypeError when both datasets have no headers, even though the method is written to allow that case:

>>> import tablib
>>> a = tablib.Dataset(); a.append(("x", "y")); a.append(("p", "q"))
>>> b = tablib.Dataset(); b.append(("1", "2", "3")); b.append(("4", "5", "6"))
>>> a.stack_cols(b)
TypeError: 'NoneType' object is not iterable

The method already anticipates two headerless datasets:

  • the header guard raises HeadersNeeded only when exactly one side has headers, and
  • the header concatenation catches None + None and sets new_headers = None.

But it then does for column in self.headers: to copy the columns, which iterates None and raises. Row-level stack() handles headerless datasets fine, so the column-level crash is an internal inconsistency rather than intended behavior.

Fix

When neither dataset has headers, copy the columns by index with get_col() instead of by header name. The existing headered path is unchanged (it is just moved under if self.headers:), so headered stacking behaves exactly as before.

Test

Added test_column_stacking_headerless, which stacks two headerless datasets and asserts the combined rows and headers is None. It fails on the current code with the TypeError above and passes with this change; the existing test_column_stacking (headered) still passes.

Note

This touches the same method as the open PR #649 ("Raise TypeError for invalid Dataset instance"), but a different line: #649 changes the isinstance(other, Dataset) guard, while this fixes the headerless column-iteration path. The two do not overlap and either order rebases trivially.

Dataset.stack_cols() explicitly permits two headerless datasets: the
header guard only raises HeadersNeeded when exactly one side has headers,
and the header concatenation catches None + None to set new_headers=None.
But it then did 'for column in self.headers', which iterates None and
raised 'TypeError: NoneType object is not iterable', so column-level
stacking crashed on inputs that row-level stack() handles fine.

When neither dataset has headers, iterate columns by index with get_col()
instead of by header name. The headered path is unchanged.
@codecov

codecov Bot commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.28%. Comparing base (64f0201) to head (862a731).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #651      +/-   ##
==========================================
+ Coverage   93.19%   93.28%   +0.09%     
==========================================
  Files          29       29              
  Lines        3247     3263      +16     
==========================================
+ Hits         3026     3044      +18     
+ Misses        221      219       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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