From 23624669030f4283c1c499e86cfa4b64ad54ea75 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sun, 18 Oct 2026 07:32:46 +0200 Subject: [PATCH] fix: suggest a region for an unknown spelling --- install.ps1 | 2 +- install.py | 11 +++++++++-- install.sh | 2 +- scripts/region.py | 18 +++++++++++++++++- tests/test_region.py | 27 +++++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/install.ps1 b/install.ps1 index 141bc16c..6ad5678a 100644 --- a/install.ps1 +++ b/install.ps1 @@ -8,7 +8,7 @@ param( $ErrorActionPreference = "Stop" $defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -$defaultInstallSha256 = "a7c7a25573c6c96beb63eebbc079956dca4f173b9cc8aa6397935654ddc23f71" +$defaultInstallSha256 = "f2a35f8df5956bb8b7ff1d697c0c204688d911a8942477b6d084882a92349a3b" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index 0f60d924..be342a2c 100644 --- a/install.py +++ b/install.py @@ -1716,7 +1716,14 @@ def canonical_region(raw: str) -> str: key = raw.strip().lower() key = REGION_ALIASES.get(key, key) if key not in REGIONS: - raise ValueError(f"unknown region: {raw.strip()}") + import difflib # noqa: PLC0415 + + close = difflib.get_close_matches(key, [*REGION_ALIASES, *REGIONS], n=3, cutoff=0.5) + hint = f" Did you mean: {', '.join(close)}?" if close else "" + raise ValueError( + f"unknown region: {raw.strip()}.{hint} " + f"Short forms: {', '.join(REGION_ALIASES)}; regions: {', '.join(sorted(REGIONS))}" + ) return key @@ -2945,7 +2952,7 @@ def main() -> None: try: resolve_regions(args.region) except ValueError as exc: - parser.error(f"{exc} (known: {', '.join(sorted(REGIONS))})") + parser.error(str(exc)) for option, values in (("--system", args.system), ("--core", args.core)): if values is not None and not any( token.strip() for value in values for token in value.split(",") diff --git a/install.sh b/install.sh index 9ccaea9f..f4be7c18 100755 --- a/install.sh +++ b/install.sh @@ -18,7 +18,7 @@ esac TEMP_INSTALLER="" TEMP_DIRECTORY="" DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -DEFAULT_INSTALL_SHA256="a7c7a25573c6c96beb63eebbc079956dca4f173b9cc8aa6397935654ddc23f71" +DEFAULT_INSTALL_SHA256="f2a35f8df5956bb8b7ff1d697c0c204688d911a8942477b6d084882a92349a3b" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/scripts/region.py b/scripts/region.py index d70d1979..f2a659aa 100644 --- a/scripts/region.py +++ b/scripts/region.py @@ -70,12 +70,28 @@ ALIASES: dict[str, str] = { } +def unknown_region_message(raw: str, aliases: dict[str, str], regions: frozenset[str]) -> str: + """What to type instead: the closest spellings, then every short form. + + `unknown region: 'uss'` left the user to read the source for `us`. + """ + import difflib # noqa: PLC0415 + + spelled = raw.strip() + close = difflib.get_close_matches(spelled.lower(), [*aliases, *regions], n=3, cutoff=0.5) + hint = f" Did you mean: {', '.join(close)}?" if close else "" + return ( + f"unknown region: {spelled}.{hint} Short forms: {', '.join(aliases)}; " + f"regions: {', '.join(sorted(regions))}" + ) + + def _canonical(raw: str) -> str: """Map one token to a canonical region slug.""" key = raw.strip().lower() key = ALIASES.get(key, key) if key not in REGIONS: - raise ValueError(f"unknown region: {raw!r}") + raise ValueError(unknown_region_message(raw, ALIASES, REGIONS)) return key diff --git a/tests/test_region.py b/tests/test_region.py index c77d4983..d09c488f 100644 --- a/tests/test_region.py +++ b/tests/test_region.py @@ -910,5 +910,32 @@ class EmulatorModeKeepsAHeldFile(unittest.TestCase): ) + +class UnknownRegionSaysWhatToType(unittest.TestCase): + """`unknown region: 'uss'` named nothing to type instead.""" + + def test_the_build_scripts_suggest_and_list_short_forms(self): + import region + + with self.assertRaises(ValueError) as caught: + region.parse_requested("uss") + message = str(caught.exception) + self.assertIn("Did you mean: us", message) + self.assertIn("Short forms: jp", message) + + def test_the_installer_says_the_same(self): + import importlib.util + + spec = importlib.util.spec_from_file_location( + "install_under_test", Path(__file__).resolve().parents[1] / "install.py" + ) + install = importlib.util.module_from_spec(spec) + spec.loader.exec_module(install) + with self.assertRaises(ValueError) as caught: + install.canonical_region("europ") + self.assertIn("Did you mean: europe", str(caught.exception)) + self.assertIn("Short forms:", str(caught.exception)) + + if __name__ == "__main__": unittest.main()