Skip to content

Connectors: Add source adapter for the XLSX file format#123

Draft
amotl wants to merge 1 commit into
mainfrom
excel
Draft

Connectors: Add source adapter for the XLSX file format#123
amotl wants to merge 1 commit into
mainfrom
excel

Conversation

@amotl

@amotl amotl commented Jun 28, 2026

Copy link
Copy Markdown
Member

About

A connector for reading spreadsheets from Excel XLSX files was dearly missing.

References

@codecov

codecov Bot commented Jun 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.50000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.88%. Comparing base (eab8b72) to head (a3e29d9).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
omniload/source/filesystem/adapter.py 77.77% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #123      +/-   ##
==========================================
+ Coverage   52.84%   53.88%   +1.03%     
==========================================
  Files         202      202              
  Lines        9323     9463     +140     
==========================================
+ Hits         4927     5099     +172     
+ Misses       4396     4364      -32     

☔ 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.

Comment thread omniload/core/registry.py Outdated
Comment thread tests/warehouse/filesystem/test_excel.py
Comment thread tests/warehouse/filesystem/test_excel.py
Comment thread omniload/source/filesystem/adapter.py Outdated
Comment thread omniload/source/filesystem/format/readers.py Outdated
Comment thread omniload/util/endpoint.py Outdated
Comment thread omniload/source/filesystem/adapter.py Outdated
Comment thread omniload/source/filesystem/api.py Outdated
Comment thread tests/main/filesystem/test_local.py
Comment thread tests/warehouse/filesystem/test_excel.py
@amotl

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 49f53f91-c293-4912-a9d6-97daffd58c46

📥 Commits

Reviewing files that changed from the base of the PR and between f38a61c and a3e29d9.

⛔ Files ignored due to path filters (1)
  • tests/assets/create_replace.xlsx is excluded by !**/*.xlsx
📒 Files selected for processing (9)
  • docs/changelog.md
  • docs/supported-sources/file.md
  • omniload/source/filesystem/adapter.py
  • omniload/source/filesystem/format/readers.py
  • omniload/source/filesystem/format/registry.py
  • omniload/source/filesystem/impl/remote.py
  • pyproject.toml
  • tests/main/filesystem/test_remote.py
  • tests/warehouse/filesystem/test_excel.py
✅ Files skipped from review due to trivial changes (1)
  • docs/changelog.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • omniload/source/filesystem/format/registry.py
  • pyproject.toml
  • tests/warehouse/filesystem/test_excel.py
  • tests/main/filesystem/test_remote.py
  • omniload/source/filesystem/adapter.py

Summary by CodeRabbit

  • New Features
    • Added XLSX spreadsheet ingestion support for local file sources.
    • Enabled XLSX auto-detection and worksheet selection via sheet_name.
    • Updated supported-format documentation and examples to include XLSX.
  • Bug Fixes
    • Improved handling of invalid or missing worksheet selection during spreadsheet ingestion.
    • Refined remote Azure filesystem reference construction for more consistent hint handling.
  • Tests
    • Added integration tests covering XLSX ingestion success (with and without sheet_name) and failure for unknown worksheets.

Walkthrough

This PR adds XLSX support to the filesystem connector, wires the new reader through adapter resolution, registers xlsx in format mapping, adds the runtime dependency, and updates tests and documentation.

Changes

XLSX filesystem support

Layer / File(s) Summary
Reader and adapter wiring
omniload/source/filesystem/format/readers.py, omniload/source/filesystem/adapter.py, omniload/source/filesystem/impl/remote.py
Adds the Excel reader, registers it in the filesystem adapter, selects readers via with_resources, binds sheet_name or column_names as needed, and changes Azure filesystem references to pass hints instead of page.
Format registration and dependency
omniload/source/filesystem/format/registry.py, pyproject.toml
Maps xlsx to read_excel and adds fastexcel<1 to runtime dependencies.
Tests and documentation
tests/warehouse/filesystem/test_excel.py, tests/main/filesystem/test_remote.py, docs/changelog.md, docs/supported-sources/file.md
Adds Excel ingestion tests, loosens the supported-format message assertion, and updates changelog and file-source docs for XLSX support.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AzureSource
  participant Adapter
  participant ReadExcel
  participant Polars

  AzureSource->>Adapter: FilesystemReference(hints=blob_hints(...))
  Adapter->>Adapter: readers(...).with_resources("read_excel")
  Adapter->>ReadExcel: bind sheet_name from ref.hints
  ReadExcel->>Polars: read_excel(file bytes, sheet_name)
  Polars-->>ReadExcel: parsed rows
  ReadExcel-->>Adapter: yield named records
Loading

Possibly related PRs

  • panodata/omniload#160: Shares filesystem reader wiring and format-to-reader mapping changes around adapter.py and FORMAT_TO_READER.
  • panodata/omniload#169: Alters the filesystem reference and reader-binding path that this PR extends for read_excel and sheet_name.
  • panodata/omniload#174: Touches the same supported-format message test adjusted here after adding a new filesystem format.

Suggested reviewers: hampsterx

Poem

A rabbit found a spreadsheet nest,
With XLSX now put to test.
I bound the sheet and hopped along,
Through reader paths both swift and strong,
🐇 Rows now tumble, neat and blessed.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding XLSX source adapter support.
Description check ✅ Passed The description is directly related to the XLSX spreadsheet reader changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch excel

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

coderabbitai[bot]

This comment was marked as resolved.

@amotl

This comment was marked as resolved.

@coderabbitai

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as low quality.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Binary files shouldn't be added to this repository. Let's start a dedicated one to collect data samples for testing purposes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@omniload/source/filesystem/format/readers.py`:
- Around line 88-107: The Excel sheet hint is being read under the wrong hint
key, so user-provided `#sheet=...` never reaches `read_excel`. Update the reader
wiring in `resource_for_reader()` to pass `sheet_name=ref.hints.get("sheet")`
(or normalize the hint key before dispatch), keeping `read_excel()` unchanged
except for consuming the correct value.

In `@omniload/source/filesystem/impl/remote.py`:
- Around line 215-216: The UnsupportedEndpointError handling in remote.py should
suppress exception chaining when converting it to the ValueError from
supported_file_format_message("Azure"). Update the except
UnsupportedEndpointError block so the re-raise mirrors the SFTP path and uses an
explicit from None on the ValueError, keeping the behavior consistent and
satisfying Ruff’s B904 check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 49f53f91-c293-4912-a9d6-97daffd58c46

📥 Commits

Reviewing files that changed from the base of the PR and between f38a61c and a3e29d9.

⛔ Files ignored due to path filters (1)
  • tests/assets/create_replace.xlsx is excluded by !**/*.xlsx
📒 Files selected for processing (9)
  • docs/changelog.md
  • docs/supported-sources/file.md
  • omniload/source/filesystem/adapter.py
  • omniload/source/filesystem/format/readers.py
  • omniload/source/filesystem/format/registry.py
  • omniload/source/filesystem/impl/remote.py
  • pyproject.toml
  • tests/main/filesystem/test_remote.py
  • tests/warehouse/filesystem/test_excel.py
✅ Files skipped from review due to trivial changes (1)
  • docs/changelog.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • omniload/source/filesystem/format/registry.py
  • pyproject.toml
  • tests/warehouse/filesystem/test_excel.py
  • tests/main/filesystem/test_remote.py
  • omniload/source/filesystem/adapter.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@omniload/source/filesystem/format/readers.py`:
- Around line 88-107: The Excel sheet hint is being read under the wrong hint
key, so user-provided `#sheet=...` never reaches `read_excel`. Update the reader
wiring in `resource_for_reader()` to pass `sheet_name=ref.hints.get("sheet")`
(or normalize the hint key before dispatch), keeping `read_excel()` unchanged
except for consuming the correct value.

In `@omniload/source/filesystem/impl/remote.py`:
- Around line 215-216: The UnsupportedEndpointError handling in remote.py should
suppress exception chaining when converting it to the ValueError from
supported_file_format_message("Azure"). Update the except
UnsupportedEndpointError block so the re-raise mirrors the SFTP path and uses an
explicit from None on the ValueError, keeping the behavior consistent and
satisfying Ruff’s B904 check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 49f53f91-c293-4912-a9d6-97daffd58c46

📥 Commits

Reviewing files that changed from the base of the PR and between f38a61c and a3e29d9.

⛔ Files ignored due to path filters (1)
  • tests/assets/create_replace.xlsx is excluded by !**/*.xlsx
📒 Files selected for processing (9)
  • docs/changelog.md
  • docs/supported-sources/file.md
  • omniload/source/filesystem/adapter.py
  • omniload/source/filesystem/format/readers.py
  • omniload/source/filesystem/format/registry.py
  • omniload/source/filesystem/impl/remote.py
  • pyproject.toml
  • tests/main/filesystem/test_remote.py
  • tests/warehouse/filesystem/test_excel.py
✅ Files skipped from review due to trivial changes (1)
  • docs/changelog.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • omniload/source/filesystem/format/registry.py
  • pyproject.toml
  • tests/warehouse/filesystem/test_excel.py
  • tests/main/filesystem/test_remote.py
  • omniload/source/filesystem/adapter.py
🛑 Comments failed to post (2)
omniload/source/filesystem/format/readers.py (1)

88-107: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the hint key used by the parser/model vs. the adapter binding.
rg -nP '\bsheet(_name)?\b' \
  omniload/source/filesystem/adapter.py \
  omniload/source/filesystem/router.py \
  omniload/source/filesystem/model.py

Repository: panodata/omniload

Length of output: 1088


🌐 Web query:

polars read_excel accept bytes input and sheet_name None default sheet

💡 Result:

Polars' polars.read_excel function supports bytes input directly [1][2]. While earlier versions experienced a regression that briefly restricted this [1][3], current versions accept raw bytes as the source parameter [1][2]. Regarding sheet selection, the function does not use None to default to the first sheet. Instead: 1. Default Behavior: If neither sheet_name nor sheet_id is specified, Polars defaults to loading sheet number 1 (the first sheet) [4][5]. 2. Loading All Sheets: To load all sheets at once, you should set sheet_id=0 [4][5]. This returns a dictionary mapping sheet names to DataFrames [4][5]. Passing bytes directly as the source is standard, but if you encounter issues in older environments, you can wrap the bytes in a io.BytesIO object, which is treated as a file-like object by the library [6][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== adapter ==\n'
sed -n '130,155p' omniload/source/filesystem/adapter.py

printf '\n== router ==\n'
sed -n '80,110p' omniload/source/filesystem/router.py

printf '\n== model ==\n'
sed -n '30,50p' omniload/source/filesystem/model.py

printf '\n== read_excel ==\n'
sed -n '80,115p' omniload/source/filesystem/format/readers.py

Repository: panodata/omniload

Length of output: 4787


Use sheet when binding Excel hints.

#sheet=... is parsed into ref.hints["sheet"], but resource_for_reader() looks up ref.hints.get("sheet_name"), so user-supplied sheet selection never reaches read_excel. Normalize the key at the boundary or bind sheet_name=ref.hints.get("sheet").

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@omniload/source/filesystem/format/readers.py` around lines 88 - 107, The
Excel sheet hint is being read under the wrong hint key, so user-provided
`#sheet=...` never reaches `read_excel`. Update the reader wiring in
`resource_for_reader()` to pass `sheet_name=ref.hints.get("sheet")` (or
normalize the hint key before dispatch), keeping `read_excel()` unchanged except
for consuming the correct value.
omniload/source/filesystem/impl/remote.py (1)

215-216: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Chain the re-raise with from None (B904).

Ruff flags this re-raise inside the except clause. The SFTP path already uses from None for the same mapping; mirror it here to silence the expected UnsupportedEndpointError chain and keep the lint gate green.

🔧 Proposed fix
         except UnsupportedEndpointError:
-            raise ValueError(supported_file_format_message("Azure"))
+            raise ValueError(supported_file_format_message("Azure")) from None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        except UnsupportedEndpointError:
            raise ValueError(supported_file_format_message("Azure")) from None
🧰 Tools
🪛 Ruff (0.15.20)

[warning] 216-216: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@omniload/source/filesystem/impl/remote.py` around lines 215 - 216, The
UnsupportedEndpointError handling in remote.py should suppress exception
chaining when converting it to the ValueError from
supported_file_format_message("Azure"). Update the except
UnsupportedEndpointError block so the re-raise mirrors the SFTP path and uses an
explicit from None on the ValueError, keeping the behavior consistent and
satisfying Ruff’s B904 check.

Source: Linters/SAST tools

@amotl amotl mentioned this pull request Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

coderabbit-review Tell CodeRabbit to review the patch.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant