11# Copyright 2026 Marimo. All rights reserved.
2- """Map DuckDB remote files to readers."""
2+ """Resolve DuckDB remote file reads into fetch and DataFrame reader steps.
3+
4+ DuckDB's native network scanner is unavailable in Pyodide. This module is the
5+ compatibility layer that recognizes supported URL shapes, validates the reader
6+ options we can reproduce, fetches bytes through WASM fetch shim, and
7+ dispatches to a local DataFrame reader. Unsupported readers or option
8+ combinations return ``None`` so callers can use the unpatched DuckDB path
9+ and surface the underlying error.
10+ """
311
412from __future__ import annotations
513
@@ -40,6 +48,7 @@ class RemoteFile:
4048 fetcher_name : str
4149
4250 def fetch (self ) -> _FetchedBytes :
51+ """Fetch through a named strategy so sources stay hashable."""
4352 return _fetcher_by_name (self .fetcher_name ).fetch (self .url )
4453
4554
@@ -265,9 +274,11 @@ class RemoteFileSource:
265274 options : tuple [tuple [str , Any ], ...] = ()
266275
267276 def read_options (self ) -> dict [str , Any ]:
277+ """Expose sorted, hashable options as regular reader kwargs."""
268278 return dict (self .options )
269279
270280 def read_dataframe (self ) -> pd .DataFrame :
281+ """Read one or more remote files using DuckDB-compatible concat rules."""
271282 frames = [self ._read_file_dataframe (file ) for file in self .files ]
272283 if len (frames ) == 1 :
273284 return frames [0 ]
@@ -287,6 +298,7 @@ def read_dataframe(self) -> pd.DataFrame:
287298 return pd .concat (frames , ignore_index = True )
288299
289300 def _read_file_dataframe (self , file : RemoteFile ) -> pd .DataFrame :
301+ """Fetch bytes, decode them, then apply DuckDB's filename option."""
290302 fetched = file .fetch ()
291303 options = self .read_options ()
292304 reader = _reader_by_name (self .reader_name )
@@ -300,6 +312,7 @@ def _read_file_dataframe(self, file: RemoteFile) -> pd.DataFrame:
300312
301313
302314def remote_file_from_url (url : str ) -> RemoteFile | None :
315+ """Return a fetchable remote file only for URL schemes marimo supports."""
303316 fetcher = _fetcher_for_url (url )
304317 if fetcher is None :
305318 return None
@@ -311,6 +324,7 @@ def remote_file_source_from_reader_args(
311324 source : Any ,
312325 raw_options : Mapping [str , Any ],
313326) -> RemoteFileSource | None :
327+ """Map a DuckDB reader call to a reproducible remote DataFrame source."""
314328 reader = reader_for_function (function_name )
315329 if reader is None :
316330 return None
@@ -328,6 +342,7 @@ def remote_file_source_from_reader_args(
328342def _remote_files_from_source_arg (
329343 source : Any ,
330344) -> tuple [RemoteFile , ...] | None :
345+ """Accept DuckDB source shapes that are static URL strings or URL lists."""
331346 if isinstance (source , str ):
332347 file = remote_file_from_url (source )
333348 return (file ,) if file is not None else None
@@ -372,6 +387,7 @@ def _reader_by_name(
372387
373388
374389def reader_for_url (url : str ) -> _DataFrameReader | None :
390+ """Infer a reader from direct URL table syntax such as ``FROM 'x.csv'``."""
375391 path = urlparse (url ).path .lower ()
376392 return next (
377393 (
@@ -386,6 +402,7 @@ def reader_for_url(url: str) -> _DataFrameReader | None:
386402def reader_for_function (
387403 function_name : str ,
388404) -> _DataFrameReader | None :
405+ """Resolve DuckDB table-function names to marimo's fallback readers."""
389406 return next (
390407 (
391408 reader
@@ -397,6 +414,7 @@ def reader_for_function(
397414
398415
399416def _csv_reader_options (options : Mapping [str , Any ]) -> dict [str , Any ]:
417+ """Drop options implemented outside DuckDB's CSV reader call."""
400418 return {
401419 key : value
402420 for key , value in options .items ()
@@ -405,6 +423,7 @@ def _csv_reader_options(options: Mapping[str, Any]) -> dict[str, Any]:
405423
406424
407425def _json_reader_options (options : Mapping [str , Any ]) -> dict [str , Any ]:
426+ """Translate DuckDB JSON option spelling to DuckDB Python API spelling."""
408427 return {
409428 key : _normalize_json_reader_option (key , value )
410429 for key , value in options .items ()
@@ -413,12 +432,14 @@ def _json_reader_options(options: Mapping[str, Any]) -> dict[str, Any]:
413432
414433
415434def _normalize_delimiter (value : Any ) -> Any :
435+ """Convert SQL's escaped tab literal to the byte delimiter DuckDB expects."""
416436 if value == r"\t" :
417437 return "\t "
418438 return value
419439
420440
421441def _normalize_json_reader_option (key : str , value : Any ) -> Any :
442+ """Normalize JSON values whose SQL names differ from Python reader values."""
422443 if key == "compression" :
423444 return _normalize_json_compression (value )
424445 if key == "format" :
@@ -427,6 +448,7 @@ def _normalize_json_reader_option(key: str, value: Any) -> Any:
427448
428449
429450def _normalize_json_compression (value : Any ) -> str :
451+ """Map SQL compression aliases to DuckDB Python JSON reader values."""
430452 compression = str (value ).lower ()
431453 if compression == "auto" :
432454 return "auto_detect"
@@ -436,6 +458,7 @@ def _normalize_json_compression(value: Any) -> str:
436458
437459
438460def _normalize_json_format (value : Any ) -> str :
461+ """Map DuckDB SQL JSON format aliases to Python reader values."""
439462 fmt = str (value ).lower ()
440463 if fmt == "ndjson" :
441464 return "newline_delimited"
@@ -445,6 +468,7 @@ def _normalize_json_format(value: Any) -> str:
445468
446469
447470def _apply_json_option (options : dict [str , Any ], key : str , value : Any ) -> bool :
471+ """Keep JSON options only when the fallback can safely pass them through."""
448472 if key == "format" :
449473 options ["format" ] = _normalize_json_format (value )
450474 return True
@@ -457,12 +481,14 @@ def _apply_json_option(options: dict[str, Any], key: str, value: Any) -> bool:
457481
458482
459483def _is_safe_reader_option_name (key : str ) -> bool :
484+ """Reject option names that cannot be passed as Python reader kwargs."""
460485 return key .isidentifier ()
461486
462487
463488def _apply_common_table_option (
464489 options : dict [str , Any ], key : str , value : Any
465490) -> bool :
491+ """Handle options marimo applies after per-file reads are decoded."""
466492 if key == "filename" and isinstance (value , bool | str ):
467493 options ["filename" ] = value
468494 return True
@@ -475,6 +501,7 @@ def _apply_common_table_option(
475501def _apply_compression_option (
476502 options : dict [str , Any ], key : str , value : Any
477503) -> bool :
504+ """Accept only compression modes supported by the byte-fetch fallback."""
478505 if key == "compression" and _is_supported_compression (value ):
479506 options ["compression" ] = str (value ).lower ()
480507 return True
@@ -484,10 +511,12 @@ def _apply_compression_option(
484511def _apply_shared_source_option (
485512 options : dict [str , Any ], key : str , value : Any
486513) -> bool :
514+ """Apply source options shared by CSV, parquet, and JSON fallbacks."""
487515 return _apply_compression_option (
488516 options , key , value
489517 ) or _apply_common_table_option (options , key , value )
490518
491519
492520def _is_supported_compression (value : Any ) -> bool :
521+ """Limit compression to modes the fallback knows how to preserve."""
493522 return str (value ).lower () in {"auto" , "none" , "gzip" }
0 commit comments