diff --git a/CHANGELOG.md b/CHANGELOG.md index 19d97b9..aad10a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ **Fixes**: * [#539](https://github.com/alecthomas/voluptuous/pull/539): Raise `Invalid` instead of leaking `TypeError`/`ValueError` for non-numeric input to the `Number` validator +* [#542](https://github.com/alecthomas/voluptuous/pull/542): Raise `Invalid` instead of leaking a raw `TypeError` for `Infinity`/`NaN` input to the `Number` validator ## [0.16.0] diff --git a/voluptuous/tests/tests.py b/voluptuous/tests/tests.py index 32a5b7a..0608548 100644 --- a/voluptuous/tests/tests.py +++ b/voluptuous/tests/tests.py @@ -1145,6 +1145,21 @@ def test_number_validation_with_non_numeric(): assert False, "Did not raise Invalid for %r" % (value,) +def test_number_validation_with_infinity_and_nan(): + """Non-finite input raises Invalid, not a raw TypeError.""" + schema = Schema({"number": Number(precision=6, scale=2)}) + for value in ('Infinity', '-Infinity', 'NaN'): + try: + schema({"number": value}) + except MultipleInvalid as e: + assert ( + str(e) + == "Value must be a number enclosed with string for dictionary value @ data['number']" + ) + else: + assert False, "Did not raise Invalid for %s" % value + + def test_number_validation_with_invalid_precision_invalid_scale(): """Test with Number with invalid precision and scale""" schema = Schema({"number": Number(precision=6, scale=2)}) diff --git a/voluptuous/validators.py b/voluptuous/validators.py index ddf1639..a69bb8a 100644 --- a/voluptuous/validators.py +++ b/voluptuous/validators.py @@ -1186,9 +1186,8 @@ def _get_precision_scale(self, number) -> typing.Tuple[int, int, Decimal]: if isinstance(exp, int): return (len(decimal_num.as_tuple().digits), -exp, decimal_num) else: - # TODO: handle infinity and NaN - # raise Invalid(self.msg or 'Value has no precision') - raise TypeError("infinity and NaN have no precision") + # Infinity/NaN have no precision; report as Invalid, not a raw TypeError. + raise Invalid(self.msg or 'Value must be a number enclosed with string') class SomeOf(_WithSubValidators):