Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Unreleased

* raise ``InterpolationError`` instead of leaking a raw ``TypeError`` when a
value interpolates a reference to an option whose value is a list
* reject ``NaN`` against a declared ``float`` bound; every comparison against
``NaN`` is False, so it previously satisfied neither the ``min`` nor the
``max`` check and was accepted as valid

Release 5.0.9
"""""""""""""
Expand Down
7 changes: 5 additions & 2 deletions src/configobj/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -840,9 +840,12 @@ def is_float(value, min=None, max=None):
value = float(value)
except ValueError:
raise VdtTypeError(value)
if (min_val is not None) and (value < min_val):
# Phrased as "must satisfy the bound" rather than "must not violate it",
# so that a NaN -- for which every comparison is False -- fails a declared
# bound instead of slipping past both checks.
if (min_val is not None) and not (value >= min_val):
raise VdtValueTooSmallError(value)
if (max_val is not None) and (value > max_val):
if (max_val is not None) and not (value <= max_val):
raise VdtValueTooBigError(value)
return value

Expand Down
24 changes: 23 additions & 1 deletion src/tests/test_validate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import math

from configobj import ConfigObj
import pytest
from configobj.validate import VdtValueTooSmallError
from configobj.validate import VdtValueTooBigError, VdtValueTooSmallError


class TestImporting:
Expand Down Expand Up @@ -173,3 +175,23 @@ def change(section, key):
'test3': 3,
'test4': 6.0
}}}


class TestFloatBounds:
"""A declared bound must reject NaN, for which every comparison is False."""

def test_nan_fails_min(self, val):
with pytest.raises(VdtValueTooSmallError):
val.check('float(0, 10)', 'nan')

def test_nan_fails_max_only(self, val):
with pytest.raises(VdtValueTooBigError):
val.check('float(max=10)', 'nan')

def test_bounded_floats_still_pass(self, val):
assert val.check('float(0, 10)', '5.0') == 5.0
assert val.check('float(0, 10)', '0') == 0.0
assert val.check('float(0, 10)', '10') == 10.0

def test_unbounded_float_is_unchanged(self, val):
assert math.isnan(val.check('float', 'nan'))