feat(filesystem): add #key=value reader-hint channel to source URIs#183
Conversation
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #183 +/- ##
==========================================
+ Coverage 52.84% 53.17% +0.32%
==========================================
Files 202 202
Lines 9323 9347 +24
==========================================
+ Hits 4927 4970 +43
+ Misses 4396 4377 -19 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| The named-hint grammar: | ||
|
|
||
| - Values are percent-decoded, so `#sheet=My%20Sheet` gives the value `My Sheet` | ||
| and `#sheet=R%26D` gives `R&D`. | ||
| - Only the first `=` splits key from value, so a value may itself contain `=` | ||
| (`#range=A1=B2` gives `range` = `A1=B2`). | ||
| - An empty value is preserved (`#sheet=` gives `sheet` = `""`); a reader decides | ||
| whether that means "unset". | ||
| - Duplicate keys take the last value (`#sheet=a&sheet=b` gives `b`). | ||
| - If any segment of the fragment is neither a `key=value` pair nor a single | ||
| known format, the whole `#...` is treated as a literal part of the path, so a | ||
| real `#` in a filename keeps working. Percent-encode a literal `#` as `%23` | ||
| when a trailing `path#key=value` would otherwise be read as a fragment. |
883847b to
4146c81
Compare
Give every filesystem source a uniform, self-documenting reader-option channel carried in the URI fragment (file://book.xlsx#sheet=quotes) instead of overloading --source-table. A format hint and named hints coexist in one fragment (feed.dat#csv&sheet=quotes). - router.parse_fragment() splits the trailing #fragment into (path, format_hint, hints): a known-format token stays the format hint, key=value pairs (parse_qsl: percent-decoded, =-partitioned, blank values kept, duplicate keys last-wins) become hints. It is all-or-nothing: a fragment with any segment that is neither a pair nor a single known format is left literal, so a real # in a path (/feeds/vendor#1/data.csv) keeps working; percent-encode a # as %23 to force a fragment-looking tail literal. - split_format_hint() becomes a thin (path, format_hint) wrapper over parse_fragment, so determine_endpoint and every existing caller keep their contract unchanged. - FilesystemReference.page (previously set but read nowhere) is replaced by hints: dict[str, str], threaded through the local, GCS, S3 and SFTP sources. blob_hints() mirrors parse_uri's carrier choice so hints track the file actually loaded across both the --source-table and the deprecated URI-path forms. No built-in reader consumes hints yet: this lands the channel and the page->hints migration with zero read-behavior change. Source-only; the file:// destination parser is untouched. Closes panodata#149
4146c81 to
17b4e3b
Compare
Summary
file://book.xlsx#sheet=quotes) instead of overloading--source-table, which Blob XLSX sources: --source-table value reused as both object path and sheet_name #148 flagged as ambiguous for XLSX. A format hint and named hints coexist in one fragment (feed.dat#csv&sheet=quotes).FilesystemReference.pagefield in favour of a free-formhints: dict[str, str], as specced on Filesystem: Named hint parameters for URIs #149.pagewas set by all four sources but read nowhere, so this is a clean swap with no consumer to migrate.router.parse_fragment()centralizes fragment parsing for every scheme: a bare known-format token stays the format hint;#key=valuepairs are parsed withparse_qsl(percent-decoded,=-partitioned, blank values kept, duplicate keys last-wins). It is all-or-nothing: a fragment with any segment that is neither akey=valuepair nor a single known format is treated as a literal path, so a real#in a path (/feeds/vendor#1/data.csv) keeps working; percent-encode a#as%23to force a fragment-looking tail literal.split_format_hint()is now a thin(path, format_hint)wrapper overparse_fragment, sodetermine_endpointand every existing caller keep their contract unchanged.blob_hints()mirrorsparse_uri's file-selection precedence so hints always track the file actually loaded, across both the--source-tableform and the deprecateds3://bucket/path#fragURI-path form.Scope: source-only, and no built-in reader consumes hints yet, so this lands the channel and the
pagetohintsmigration with zero read-behavior change. Thefile://destination parser is untouched. Wiring a hint into a reader is a follow-up.Fragment, not query params: query params already carry credentials/connection options (
?access_key_id=...); putting reader hints there risks key collisions and leaking credential params into reader kwargs. The example in #149 uses the fragment.Coordination with #123: this can merge independently (channel + migration, no consumer). When the XLSX source (#123) lands, it can read
hints["sheet"]for sheet selection with no second migration. Happy to align on the exact key name (sheetvspage) on either PR.Test plan
parse_fragmentunit tests across the full grammar (no hint, bare format, single/multiple named hints, format+named coexist, empty value,=in value, percent-decode, duplicate last-wins, mixed valid/invalid to literal, duplicate format to literal, literal#,%23escape)FilesystemReference.hintsfor all four sources (local/GCS/S3/SFTP), plus the deprecated URI-path form and the contradictory both-forms casesplit_format_hint, literal-#-in-path) unchanged and greenruff+validate-pyproject+tyclean-W; grammar documented on the source pagesCloses #149