Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ jobs:
run: |
bash -c "find . -type f -name '*.gcno' -exec gcov -pb --all-blocks {} +" || true
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unittests
Expand Down
4 changes: 4 additions & 0 deletions atom/scalars.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ def __init__(self, low=None, high=None, value=None, *, strict=False):
if strict:
self.set_validate_mode(Validate.FloatRange, (low, high))
else:
if low is not None:
low = float(low)
if high is not None:
high = float(high)
self.set_validate_mode(Validate.FloatRangePromote, (low, high))


Expand Down
40 changes: 32 additions & 8 deletions atom/src/validatebehavior.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ Member::check_context( Validate::Mode mode, PyObject* context )
}
break;
case Validate::FloatRange:
case Validate::FloatRangePromote:
{
if( !PyTuple_Check( context ) )
{
Expand Down Expand Up @@ -804,7 +805,12 @@ enum_handler( Member* member, CAtom* atom, PyObject* oldvalue, PyObject* newvalu
return 0;
if( res == 1 )
return cppy::incref( newvalue );
return cppy::value_error( "invalid enum value" );
return PyErr_Format(
PyExc_ValueError,
"invalid enum value for '%s' of '%s'",
PyUnicode_AsUTF8( member->name ),
Py_TYPE( pyobject_cast( atom ) )->tp_name
);
}


Expand All @@ -830,12 +836,22 @@ float_range_handler( Member* member, CAtom* atom, PyObject* oldvalue, PyObject*
if( low != Py_None )
{
if( PyFloat_AS_DOUBLE( low ) > value )
return cppy::type_error( "range value too small" );
return PyErr_Format(
PyExc_ValueError,
"range value for '%s' of '%s' too small",
PyUnicode_AsUTF8( member->name ),
Py_TYPE( pyobject_cast( atom ) )->tp_name
);
}
if( high != Py_None )
{
if( PyFloat_AS_DOUBLE( high ) < value )
return cppy::type_error( "range value too large" );
return PyErr_Format(
PyExc_ValueError,
"range value for '%s' of '%s' too large",
PyUnicode_AsUTF8( member->name ),
Py_TYPE( pyobject_cast( atom ) )->tp_name
);
}
return cppy::incref( newvalue );
}
Expand All @@ -844,8 +860,6 @@ float_range_handler( Member* member, CAtom* atom, PyObject* oldvalue, PyObject*
PyObject*
float_range_promote_handler( Member* member, CAtom* atom, PyObject* oldvalue, PyObject* newvalue )
{
if( PyFloat_Check( newvalue ) )
return float_range_handler( member, atom, oldvalue, newvalue );
if( PyLong_Check( newvalue ) )
{
double value = PyLong_AsDouble( newvalue );
Expand All @@ -854,7 +868,7 @@ float_range_promote_handler( Member* member, CAtom* atom, PyObject* oldvalue, Py
cppy::ptr convertedvalue( PyFloat_FromDouble( value ) );
return float_range_handler( member, atom, oldvalue, convertedvalue.get() );
}
return validate_type_fail( member, atom, newvalue, "float" );
return float_range_handler( member, atom, oldvalue, newvalue );
}


Expand All @@ -868,12 +882,22 @@ range_handler( Member* member, CAtom* atom, PyObject* oldvalue, PyObject* newval
if( low != Py_None )
{
if( PyObject_RichCompareBool( low , newvalue, Py_GT ) )
return cppy::type_error( "range value too small" );
return PyErr_Format(
PyExc_ValueError,
"range value for '%s' of '%s' too small",
PyUnicode_AsUTF8( member->name ),
Py_TYPE( pyobject_cast( atom ) )->tp_name
);
}
if( high != Py_None )
{
if( PyObject_RichCompareBool( high , newvalue, Py_LT ) )
return cppy::type_error( "range value too large" );
return PyErr_Format(
PyExc_ValueError,
"range value for '%s' of '%s' too large",
PyUnicode_AsUTF8( member->name ),
Py_TYPE( pyobject_cast( atom ) )->tp_name
);
}
return cppy::incref( newvalue );
}
Expand Down
20 changes: 12 additions & 8 deletions tests/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ def c(x: object) -> int:
(FloatRange(0.0), [0.0, 0.6], [0.0, 0.6], [-0.1, ""]),
(FloatRange(high=0.5), [-0.3, 0.5], [-0.3, 0.5], [0.6]),
(FloatRange(1.0, 10.0, strict=True), [1.0, 3.7], [1.0, 3.7], [2, 4, 0, -11]),
(FloatRange(low=0, strict=False), [1, 25], [1.0, 25.0], [-1, -10.0]),
(Bytes(strict=False), [b"a", "a"], [b"a"] * 2, [1]),
(Bytes(), [b"a"], [b"a"], ["a"]),
(Str(strict=False), [b"a", "a"], ["a"] * 2, [1]),
Expand Down Expand Up @@ -224,13 +225,15 @@ class MemberTest(Atom):
assert tester.m == v

for rv in raising_values:
with pytest.raises(
OverflowError
if (isinstance(member, Int) and isinstance(rv, float) and rv > 2**32)
else ValueError
if isinstance(member, Enum)
else TypeError
):
if isinstance(member, Int) and isinstance(rv, float) and rv > 2**32:
error_type = OverflowError
elif isinstance(member, Enum):
error_type = ValueError
elif isinstance(member, (Range, FloatRange)):
error_type = (ValueError, TypeError)
else:
error_type = TypeError
with pytest.raises(error_type):
tester.m = rv


Expand Down Expand Up @@ -271,7 +274,8 @@ class MemberTest(Atom):
(FloatRange(), "FloatRange", 1, "2-tuple of float or None"),
(FloatRange(), "FloatRange", (), "2-tuple of float or None"),
(FloatRange(), "FloatRange", ("", None), "2-tuple of float or None"),
(FloatRange(), "FloatRange", (None, ""), "2-tuple of float or None"),
(FloatRange(), "FloatRange", (0, 0), "2-tuple of float or None"),
(FloatRange(), "FloatRangePromote", (0.0, ""), "2-tuple of float or None"),
(Range(), "Range", 1, "2-tuple of int or None"),
(Range(), "Range", (), "2-tuple of int or None"),
(Range(), "Range", ("", None), "2-tuple of int or None"),
Expand Down
Loading