Repository: yt-project/yt

Issue:
Feature request: suggestions for typos in load_sample
### Feature

I think it would be really useful to use the same suggestion mechanism as in https://github.com/yt-project/yt/pull/3184 for load_sample.
Related to #3263
The implementation will be pretty trivial to perform once #3184 is merged


Discussion:
Yeah, this would be super awesome!
So I want to wait for #3336 to go in, because the fix will otherwise create unnecessary conflicts as it will very likely affect the same lines.

Resolving patch:
diff --git a/yt/funcs.py b/yt/funcs.py
--- a/yt/funcs.py
+++ b/yt/funcs.py
@@ -1288,7 +1288,7 @@ def levenshtein_distance(seq1, seq2, max_dist=None):
 
     Returns
     -------
-    The Levensthein distance as an integer.
+    The Levenshtein distance as an integer.
 
     Notes
     -----
diff --git a/yt/loaders.py b/yt/loaders.py
--- a/yt/loaders.py
+++ b/yt/loaders.py
@@ -6,13 +6,14 @@
 import sys
 import tarfile
 from pathlib import Path
-from typing import Optional
+from typing import List, Optional, Tuple
 from urllib.parse import urlsplit
 
 import numpy as np
 from more_itertools import always_iterable
 
 from yt._maintenance.deprecation import issue_deprecation_warning
+from yt.funcs import levenshtein_distance
 from yt.sample_data.api import lookup_on_disk_data
 from yt.utilities.decompose import decompose_array, get_psize
 from yt.utilities.exceptions import (
@@ -1376,6 +1377,21 @@ def load_sample(
     topdir, _, specific_file = fn.partition(os.path.sep)
 
     registry_table = get_data_registry_table()
+
+    known_names: List[str] = registry_table.dropna()["filename"].to_list()
+    if topdir not in known_names:
+        msg = f"'{topdir}' is not an available dataset."
+        lexical_distances: List[Tuple[str, int]] = [
+            (name, levenshtein_distance(name, topdir)) for name in known_names
+        ]
+        suggestions: List[str] = [name for name, dist in lexical_distances if dist < 4]
+        if len(suggestions) == 1:
+            msg += f" Did you mean '{suggestions[0]}' ?"
+        elif suggestions:
+            msg += " Did you mean to type any of the following ?\n\n    "
+            msg += "\n    ".join(f"'{_}'" for _ in suggestions)
+        raise ValueError(msg)
+
     # PR 3089
     # note: in the future the registry table should be reindexed
     # so that the following line can be replaced with
@@ -1384,11 +1400,7 @@ def load_sample(
     #
     # however we don't want to do it right now because the "filename" column is
     # currently incomplete
-
-    try:
-        specs = registry_table.query(f"`filename` == '{topdir}'").iloc[0]
-    except IndexError as err:
-        raise KeyError(f"Could not find '{fn}' in the registry.") from err
+    specs = registry_table.query(f"`filename` == '{topdir}'").iloc[0]
 
     load_name = specific_file or specs["load_name"] or ""
 


Test patch:
diff --git a/yt/tests/test_load_sample.py b/yt/tests/test_load_sample.py
--- a/yt/tests/test_load_sample.py
+++ b/yt/tests/test_load_sample.py
@@ -1,5 +1,6 @@
 import logging
 import os
+import re
 import sys
 import textwrap
 
@@ -133,9 +134,20 @@ def test_registry_byte_size_sign(sound_subreg):
 @requires_module_pytest("pandas", "requests")
 def test_unknown_filename():
     fake_name = "these_are_not_the_files_your_looking_for"
-    with pytest.raises(KeyError) as err:
+    with pytest.raises(ValueError, match=f"'{fake_name}' is not an available dataset."):
         load_sample(fake_name)
-        assert err.exc == f"Could not find '{fake_name}' in the registry."
+
+
+@requires_module_pytest("pandas", "requests")
+def test_typo_filename():
+    wrong_name = "Isolatedgalaxy"
+    with pytest.raises(
+        ValueError,
+        match=re.escape(
+            f"'{wrong_name}' is not an available dataset. Did you mean 'IsolatedGalaxy' ?"
+        ),
+    ):
+        load_sample(wrong_name, timeout=1)
 
 
 @pytest.fixture()