@@ -156,6 +156,11 @@ def __init__(
156156
157157 # Build source map (string construction only — fast, no network)
158158 self .SOURCES : dict [str , Source ] = self ._build_sources ()
159+ if self .priority :
160+ # Filter to only the requested sources, in priority order
161+ self .SOURCES = {
162+ k : self .SOURCES [k ] for k in self .priority if k in self .SOURCES
163+ }
159164
160165 # ── Abstract interface ─────────────────────────────────────────────────
161166
@@ -212,10 +217,12 @@ def _resolve_params(self, kwargs: dict) -> dict:
212217 # ── Source ordering ────────────────────────────────────────────────────
213218
214219 def _ordered_sources (self ) -> dict [str , Source ]:
215- """Return sources in priority order (user-supplied or model default)."""
216- if self .priority is None :
217- return self .SOURCES
218- return {k : self .SOURCES [k ] for k in self .priority if k in self .SOURCES }
220+ """Return sources in priority order (user-supplied or model default).
221+
222+ SOURCES is already reordered at construction time, so this is a
223+ simple passthrough kept for internal consistency.
224+ """
225+ return self .SOURCES
219226
220227 # ── Local path helpers ─────────────────────────────────────────────────
221228
@@ -666,9 +673,9 @@ def xarray(
666673
667674 return ds
668675
669- def find (self ) -> dict [ str , tuple ] :
676+ def find (self ) -> "HerbieModel" :
670677 """
671- Resolve the first available GRIB source and index file.
678+ Resolve and display the first available GRIB source and index file.
672679
673680 Unlike ``status()``, which fires parallel HEAD requests to *every*
674681 source, ``find()`` walks sources in priority order and stops at the
@@ -677,11 +684,74 @@ def find(self) -> dict[str, tuple]:
677684 will actually be used before doing any real work.
678685
679686 Results are cached, so repeated calls are free.
687+
688+ Returns
689+ -------
690+ self
691+ The HerbieModel instance, enabling chaining::
692+
693+ ds = H.find().xarray("TMP:2 m above ground")
694+
695+ Examples
696+ --------
697+ >>> H = GFS("2025-01-01", fxx=6)
698+ >>> H.find() # display + return self
699+ >>> H.find().inventory("TMP:500 mb") # chain into inventory
680700 """
681- return {
682- "grib" : self ._found_grib ,
683- "index" : self ._found_index ,
684- }
701+ # Trigger lazy resolution (cached after the first call)
702+ grib_src , grib_url = self ._found_grib
703+ idx_src , idx_url = self ._found_index
704+
705+ # ── Rich display ───────────────────────────────────────────────────
706+ logo = Text ()
707+ logo .append ("▌" , style = "bold red on white" )
708+ logo .append ("▌" , style = "bold blue on #f0ead2" )
709+ logo .append ("Herbie" , style = "bold black on #f0ead2" )
710+ logo .append (f" { self .MODEL_NAME } " , style = "bold cyan" )
711+ logo .append (f" — { self .MODEL_DESCRIPTION } " , style = "dim italic" )
712+
713+ grid = Table .grid (padding = (0 , 2 ))
714+ grid .add_column () # label
715+ grid .add_column () # source badge
716+ grid .add_column () # url / path
717+
718+ # Header row
719+ grid .add_row (
720+ f"[bold]Initialized:[/bold] [green]{ self .date :%Y-%b-%d %H:%M UTC} [/green]"
721+ f" [bold]F{ self .fxx :02d} [/bold]" ,
722+ "" ,
723+ f"[bold]Valid:[/bold] [green]{ self .valid_date :%Y-%b-%d %H:%M UTC} [/green]" ,
724+ )
725+
726+ grid .add_row ("" , "" , "" ) # spacer
727+
728+ # GRIB row
729+ if grib_url :
730+ badge = f"[[bold cyan]{ grib_src } [/bold cyan]]"
731+ link = f"[link={ grib_url } ][dim]{ grib_url } [/dim][/link]"
732+ grid .add_row ("[bold]GRIB [/bold]" , badge , link )
733+ else :
734+ grid .add_row ("[bold]GRIB [/bold]" , "[red]not found[/red]" , "" )
735+
736+ # Index row
737+ if idx_url :
738+ badge = f"[[bold cyan]{ idx_src } [/bold cyan]]"
739+ link = f"[link={ idx_url } ][dim]{ idx_url } [/dim][/link]"
740+ grid .add_row ("[bold]Index[/bold]" , badge , link )
741+ else :
742+ grid .add_row ("[bold]Index[/bold]" , "[red]not found[/red]" , "" )
743+
744+ console .print (
745+ Panel (
746+ grid ,
747+ title = logo ,
748+ title_align = "left" ,
749+ border_style = "cyan" ,
750+ box = box .ROUNDED ,
751+ )
752+ )
753+
754+ return self
685755
686756 def status (self ) -> None :
687757 """
@@ -693,8 +763,8 @@ def status(self) -> None:
693763
694764 Sources are shown in three separate sections depending on type:
695765 Remote GRIB Sources, Remote Directory Sources, Remote Zarr Sources.
696- The GRIB section includes an Index column showing whether the
697- companion index file was also found .
766+ The GRIB section links to the index file (whichever suffix is
767+ found first) appended to the source URL .
698768 """
699769 from rich .console import Group
700770
@@ -713,12 +783,14 @@ def status(self) -> None:
713783 }
714784
715785 # ── Parallel HEAD requests ─────────────────────────────────────────
716- # For each GRIB source check both the GRIB file and its first index
717- # suffix URL. For Zarr sources just check the store URL.
786+ # For each GRIB source check the GRIB file and every index suffix.
787+ # Index keys are (name, "idx:SUFFIX") so we know which suffix hit.
788+ # For Zarr sources just check the store URL.
718789 check_map : dict [tuple [str , str ], str ] = {}
719790 for name , src in grib_srcs .items ():
720791 check_map [(name , "grib" )] = src .url
721- check_map [(name , "idx" )] = src .url + src .index_suffixes [0 ]
792+ for suffix in src .index_suffixes :
793+ check_map [(name , f"idx:{ suffix } " )] = src .url + suffix
722794 for name , src in zarr_srcs .items ():
723795 check_map [(name , "zarr" )] = src .url
724796
@@ -761,20 +833,30 @@ def status(self) -> None:
761833 )
762834 grib_table .add_column ("Source" , style = "bold cyan" )
763835 grib_table .add_column ("GRIB" , justify = "center" )
764- grib_table .add_column ("Index" , justify = "center" )
765836 grib_table .add_column ("Size" , justify = "right" )
766- grib_table .add_column ("URL" , style = "dim" , overflow = "fold" , max_width = 60 )
837+ grib_table .add_column ("URL" , style = "dim" , overflow = "fold" , max_width = 70 )
767838
768839 for name , src in grib_srcs .items ():
769840 g_info = url_results .get (
770841 (name , "grib" ), {"exists" : False , "size" : None }
771842 )
772- ix_info = url_results .get ((name , "idx" ), {"exists" : False })
773843 grib_str = "[green]✓[/green]" if g_info ["exists" ] else "[red]✗[/red]"
774- idx_str = "[green]✓[/green]" if ix_info ["exists" ] else "[red]✗[/red]"
775844 size_str = _fmt_size (g_info ["size" ])
845+ # Find first index suffix that exists
846+ found_idx : tuple [str , str ] | None = next (
847+ (
848+ (suffix , url_results [(name , f"idx:{ suffix } " )]["exists" ])
849+ for suffix in src .index_suffixes
850+ if url_results .get ((name , f"idx:{ suffix } " ), {}).get ("exists" )
851+ ),
852+ None ,
853+ )
854+ idx_url = src .url + found_idx [0 ] if found_idx else None
776855 url_str = f"[link={ src .url } ]{ src .url } [/link]"
777- grib_table .add_row (name , grib_str , idx_str , size_str , url_str )
856+ if idx_url :
857+ suffix_label = found_idx [0 ]
858+ url_str += f" [link={ idx_url } ]{ suffix_label } [/link]"
859+ grib_table .add_row (name , grib_str , size_str , url_str )
778860
779861 renderables .append (grib_table )
780862
0 commit comments