Skip to content

Reject NaN against a declared float bound - #280

Open
dylanpulver wants to merge 1 commit into
DiffSK:5.0.xfrom
dylanpulver:fix-float-bounds-nan
Open

Reject NaN against a declared float bound#280
dylanpulver wants to merge 1 commit into
DiffSK:5.0.xfrom
dylanpulver:fix-float-bounds-nan

Conversation

@dylanpulver

Copy link
Copy Markdown

is_float bounds-checks with value < min_val / value > max_val
(src/configobj/validate.py:843-846). Every comparison against NaN is False, so
a NaN violates neither test and is returned as valid — while inf and -inf
are correctly rejected by those same bounds:

float(0, 10)   'nan'   -> nan                      accepted
float(0, 10)   'inf'   -> VdtValueTooBigError      rejected
float(max=10)  'nan'   -> nan                      accepted
float(min=0)   'nan'   -> nan                      accepted

docs/validate.rst describes these parameters as integer(3, 9) # any value from 3 to 9, with float having "the same parameters as the integer check".
NaN is not a value from 3 to 9.

The fix phrases each check as must satisfy the bound rather than must not
violate it
, so NaN fails. Scope is deliberately narrow:

  • an unbounded float still accepts NaN — unchanged, and pinned by a test
  • is_integer is unaffected: it converts through int(), which rejects NaN
    before any bound is compared (verified)

Remedy choice worth a second opinion: with a bound declared, NaN now raises
VdtValueTooSmallError (or VdtValueTooBigError for a max-only spec). That is
the natural consequence of the ordering rather than a considered claim that NaN
is "too small". If you'd rather it raised VdtTypeError, say so and I'll change
it — I avoided that as the default because the obvious form of it (reject NaN
whenever it appears) also breaks unbounded float, which nothing asked for.

Test runs (same command and environment each time)

python -m pytest src/tests/ -q, based on 5.0.x:

result
clean 5.0.x 76 passed
5.0.x + the new tests 2 failed, 78 passed
this PR 80 passed

I also built the blanket version (if value != value: raise VdtTypeError
regardless of bounds): 3 failed, 77 passed — it fails
test_unbounded_float_is_unchanged, which is why that test is there.

Based on 5.0.x rather than the default release branch, since #275, #276 and
#278 all merged there. Prior art: no open PR touches is_float (checked the
changed files of all 14 open PRs; #267 and #127 touch validate.py, for type
hints and strict-spec validation).


AI disclosure: found and drafted with Claude Code (model Claude Opus 5,
claude-opus-5) — probing the numeric validators with non-comparable values
after noticing inf was caught and NaN was not. Reviewed before submission.

is_float compared with `value < min_val` / `value > max_val`. Every
comparison against NaN is False, so a NaN satisfied neither test and was
returned as valid: `float(0, 10)` accepts the string 'nan', while 'inf' and
'-inf' are correctly rejected by those same bounds.

docs/validate.rst describes these parameters as "any value from 3 to 9"
(integer, and float "has the same parameters"), which NaN is not.

Phrase the checks as "must satisfy the bound" instead, so NaN fails them. An
unbounded `float` still accepts NaN, unchanged. is_integer is unaffected: it
converts through int() first, which rejects NaN before any bound is compared.
@jelmer

jelmer commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Please keep PR descriptions brief and to the point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants