From eb2252a1cb9c345428c41fb214a83ebdb7fb4365 Mon Sep 17 00:00:00 2001 From: Ali jawwad <33836051+jawwad-ali@users.noreply.github.com> Date: Sat, 18 Jul 2026 01:10:05 +0500 Subject: [PATCH] fix(catalogs): 'priority: .inf' yields a clean validation error instead of crashing (#3525) * fix(catalogs): priority: .inf yields a clean validation error, not OverflowError _load_catalog_config coerces a catalog entry's priority with int() inside except (TypeError, ValueError). int(float('inf')) raises OverflowError, which is not in that tuple, so a YAML 'priority: .inf' escaped as an uncaught traceback instead of the intended 'expected integer' validation error (the bool-is-int case is already guarded just above). Add OverflowError to the except tuple. Test mirrors the existing rejects_boolean_priority test with priority: .inf (fails before: OverflowError; passes after: ValidationError naming the config). Co-Authored-By: Claude Opus 4.8 (1M context) * fix(presets): priority: .inf in a preset catalog config yields a clean error The PresetCatalog._load_catalog_config priority parser has its own loader (separate from CatalogStackBase) that caught only TypeError/ValueError, so a YAML 'priority: .inf' escaped as an uncaught OverflowError from int(float('inf')). Add OverflowError to the except tuple (the bool-is-int case is already guarded just above), matching catalogs.py. Test mirrors rejects_boolean_priority with priority: .inf (fails before: OverflowError; passes after: PresetValidationError). Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Claude Opus 4.8 (1M context) --- src/specify_cli/catalogs.py | 5 ++++- src/specify_cli/presets/__init__.py | 5 ++++- tests/test_extensions.py | 30 +++++++++++++++++++++++++++++ tests/test_presets.py | 18 +++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/specify_cli/catalogs.py b/src/specify_cli/catalogs.py index e4df8eae2..774aaa51d 100644 --- a/src/specify_cli/catalogs.py +++ b/src/specify_cli/catalogs.py @@ -149,7 +149,10 @@ class CatalogStackBase: ) try: priority = int(raw_priority) - except (TypeError, ValueError): + except (TypeError, ValueError, OverflowError): + # OverflowError: int(float("inf")) — a YAML ``priority: .inf`` + # would otherwise escape as an uncaught traceback instead of the + # clean validation error. raise self._validation_error( f"Invalid catalog config {config_path}: " f"Invalid priority for catalog '{item.get('name', idx + 1)}': " diff --git a/src/specify_cli/presets/__init__.py b/src/specify_cli/presets/__init__.py index 97e15aa64..fa498b960 100644 --- a/src/specify_cli/presets/__init__.py +++ b/src/specify_cli/presets/__init__.py @@ -2235,7 +2235,10 @@ class PresetCatalog: ) try: priority = int(raw_priority) - except (TypeError, ValueError): + except (TypeError, ValueError, OverflowError): + # OverflowError: int(float("inf")) — a YAML ``priority: .inf`` + # would otherwise escape as an uncaught traceback instead of the + # clean validation error (mirrors catalogs.py). raise PresetValidationError( f"Invalid priority for catalog '{item.get('name', idx + 1)}': " f"expected integer, got {raw_priority!r}" diff --git a/tests/test_extensions.py b/tests/test_extensions.py index d2f4af826..652540d1b 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -4542,6 +4542,36 @@ class TestCatalogStack: catalog.get_active_catalogs() assert str(config_path) in str(exc_info.value) + def test_load_catalog_config_rejects_infinite_priority(self, temp_dir): + """A ``priority: .inf`` yields a clean validation error, not an uncaught + OverflowError from int(float('inf')).""" + import yaml as yaml_module + + project_dir = self._make_project(temp_dir) + config_path = project_dir / ".specify" / "extension-catalogs.yml" + config_path.write_text( + yaml_module.dump( + { + "catalogs": [ + { + "name": "inf-priority", + "url": "https://example.com/catalog.json", + "priority": float("inf"), + } + ] + } + ), + encoding="utf-8", + ) + + catalog = ExtensionCatalog(project_dir) + + with pytest.raises( + ValidationError, match="Invalid priority|expected integer" + ) as exc_info: + catalog.get_active_catalogs() + assert str(config_path) in str(exc_info.value) + def test_load_catalog_config_defaults_blank_names(self, temp_dir): """Blank and null names normalize by valid catalog order.""" import yaml as yaml_module diff --git a/tests/test_presets.py b/tests/test_presets.py index aec335c91..d59836ebf 100644 --- a/tests/test_presets.py +++ b/tests/test_presets.py @@ -2671,6 +2671,24 @@ class TestPresetCatalogMultiCatalog: with pytest.raises(PresetValidationError, match="Invalid priority|expected integer"): catalog._load_catalog_config(config_path) + def test_load_catalog_config_rejects_infinite_priority(self, project_dir): + """A ``priority: .inf`` yields a clean validation error, not an uncaught + OverflowError from int(float('inf')).""" + config_path = project_dir / ".specify" / "preset-catalogs.yml" + config_path.write_text(yaml.dump({ + "catalogs": [ + { + "name": "inf-priority", + "url": "https://example.com/catalog.json", + "priority": float("inf"), + } + ] + })) + + catalog = PresetCatalog(project_dir) + with pytest.raises(PresetValidationError, match="Invalid priority|expected integer"): + catalog._load_catalog_config(config_path) + def test_load_catalog_config_install_allowed_string(self, project_dir): """Test that install_allowed accepts string values.""" config_path = project_dir / ".specify" / "preset-catalogs.yml"