Skip to content

Commit fbd0c5d

Browse files
committed
Give config-parsing errors an unique exception class
1 parent c2fbd82 commit fbd0c5d

File tree

2 files changed

+24
-13
lines changed

2 files changed

+24
-13
lines changed

Diff for: babel/messages/frontend.py

+23-12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ class SetupError(BaseError):
5454
pass
5555

5656

57+
class ConfigurationError(BaseError):
58+
"""
59+
Raised for errors in configuration files.
60+
"""
61+
62+
5763
def listify_value(arg, split=None):
5864
"""
5965
Make a list out of an argument.
@@ -1041,39 +1047,40 @@ def _parse_config_object(config: dict, *, filename="(unknown)"):
10411047

10421048
extractors_read = config.get("extractors", {})
10431049
if not isinstance(extractors_read, dict):
1044-
raise ValueError(f"{filename}: extractors: Expected a dictionary, got {type(extractors_read)!r}")
1050+
raise ConfigurationError(f"{filename}: extractors: Expected a dictionary, got {type(extractors_read)!r}")
10451051
for method, callable_spec in extractors_read.items():
10461052
if not isinstance(method, str):
1047-
raise ValueError(f"{filename}: extractors: Extraction method must be a string, got {method!r}")
1053+
# Impossible via TOML, but could happen with a custom object.
1054+
raise ConfigurationError(f"{filename}: extractors: Extraction method must be a string, got {method!r}")
10481055
if not isinstance(callable_spec, str):
1049-
raise ValueError(f"{filename}: extractors: Callable specification must be a string, got {callable_spec!r}")
1056+
raise ConfigurationError(f"{filename}: extractors: Callable specification must be a string, got {callable_spec!r}")
10501057
extractors[method] = callable_spec
10511058

10521059
if "mapping" in config:
1053-
raise ValueError(f"{filename}: 'mapping' is not a valid key, did you mean 'mappings'?")
1060+
raise ConfigurationError(f"{filename}: 'mapping' is not a valid key, did you mean 'mappings'?")
10541061

10551062
mappings_read = config.get("mappings", [])
10561063
if not isinstance(mappings_read, list):
1057-
raise ValueError(f"{filename}: mappings: Expected a list, got {type(mappings_read)!r}")
1064+
raise ConfigurationError(f"{filename}: mappings: Expected a list, got {type(mappings_read)!r}")
10581065
for idx, entry in enumerate(mappings_read):
10591066
if not isinstance(entry, dict):
1060-
raise ValueError(f"{filename}: mappings[{idx}]: Expected a dictionary, got {type(entry)!r}")
1067+
raise ConfigurationError(f"{filename}: mappings[{idx}]: Expected a dictionary, got {type(entry)!r}")
10611068
entry = entry.copy()
10621069

10631070
method = entry.pop("method", None)
10641071
if not isinstance(method, str):
1065-
raise ValueError(f"{filename}: mappings[{idx}]: 'method' must be a string, got {method!r}")
1072+
raise ConfigurationError(f"{filename}: mappings[{idx}]: 'method' must be a string, got {method!r}")
10661073
method = extractors.get(method, method) # Map the extractor name to the callable now
10671074

10681075
pattern = entry.pop("pattern", None)
10691076
if not isinstance(pattern, (list, str)):
1070-
raise ValueError(f"{filename}: mappings[{idx}]: 'pattern' must be a list or a string, got {pattern!r}")
1077+
raise ConfigurationError(f"{filename}: mappings[{idx}]: 'pattern' must be a list or a string, got {pattern!r}")
10711078
if not isinstance(pattern, list):
10721079
pattern = [pattern]
10731080

10741081
for pat in pattern:
10751082
if not isinstance(pat, str):
1076-
raise ValueError(f"{filename}: mappings[{idx}]: 'pattern' elements must be strings, got {pat!r}")
1083+
raise ConfigurationError(f"{filename}: mappings[{idx}]: 'pattern' elements must be strings, got {pat!r}")
10771084
method_map.append((pat, method))
10781085
options_map[pat] = entry
10791086

@@ -1100,16 +1107,20 @@ def parse_mapping_toml(
11001107
except ImportError as ie:
11011108
raise ImportError("tomli or tomllib is required to parse TOML files") from ie
11021109

1103-
parsed_data = tomllib.load(fileobj)
1110+
try:
1111+
parsed_data = tomllib.load(fileobj)
1112+
except tomllib.TOMLDecodeError as e:
1113+
raise ConfigurationError(f"{filename}: Error parsing TOML file: {e}") from e
1114+
11041115
if style == "pyproject.toml":
11051116
try:
11061117
babel_data = parsed_data["tool"]["babel"]
11071118
except (TypeError, KeyError) as e:
1108-
raise ValueError(f"{filename}: No 'tool.babel' section found in file") from e
1119+
raise ConfigurationError(f"{filename}: No 'tool.babel' section found in file") from e
11091120
elif style == "standalone":
11101121
babel_data = parsed_data
11111122
if "babel" in babel_data:
1112-
raise ValueError(f"{filename}: 'babel' should not be present in a stand-alone configuration file")
1123+
raise ConfigurationError(f"{filename}: 'babel' should not be present in a stand-alone configuration file")
11131124
else: # pragma: no cover
11141125
raise ValueError(f"Unknown TOML style {style!r}")
11151126

Diff for: tests/messages/test_toml_config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ def test_bad_toml_test_case(test_case: pathlib.Path):
2929
"""
3030
Test that bad TOML files raise a ValueError.
3131
"""
32-
with pytest.raises(ValueError):
32+
with pytest.raises(frontend.ConfigurationError):
3333
with test_case.open("rb") as f:
3434
frontend.parse_mapping_toml(f, filename=test_case.name)

0 commit comments

Comments
 (0)