Source code for validx.py.numbers

import math

from .. import contracts
from .. import exc
from ..compat.types import numbers
from . import abstract


[docs]class Int(abstract.Validator): """ Integer Number Validator :param bool nullable: accept ``None`` as a valid value. :param bool coerce: try to convert non-integer value to ``int``. :param int min: lower limit. :param int max: upper limit. :param iterable options: explicit enumeration of valid values. :raises InvalidTypeError: * if ``value is None`` and ``not self.nullable``; * if ``not isinstance(value, int)`` and ``not self.coerce``; * if ``int(value)`` raises ``ValueError`` or ``TypeError``. :raises MinValueError: if ``value < self.min``. :raises MaxValueError: if ``value > self.max``. :raises OptionsError: if ``value not in self.options``. :note: It implicitly converts ``float`` to ``int``, if ``value.is_integer() is True``. """ __slots__ = ("nullable", "coerce", "min", "max", "options") def __init__( self, nullable=False, coerce=False, min=None, max=None, options=None, alias=None, replace=False, ): nullable = contracts.expect_flag(self, "nullable", nullable) coerce = contracts.expect_flag(self, "coerce", coerce) min = contracts.expect(self, "min", min, nullable=True, types=int) max = contracts.expect(self, "max", max, nullable=True, types=int) options = contracts.expect_container( self, "options", options, nullable=True, item_type=int ) setattr = object.__setattr__ setattr(self, "nullable", nullable) setattr(self, "coerce", coerce) setattr(self, "min", min) setattr(self, "max", max) setattr(self, "options", options) self._register(alias, replace) def __call__(self, value, __context=None): if value is None and self.nullable: return value if not isinstance(value, int): if isinstance(value, float) and value.is_integer(): # Implicitly convert ``float`` to ``int``, # if the value represents integer number value = int(value) elif not self.coerce: raise exc.InvalidTypeError(expected=int, actual=type(value)) else: try: value = int(value) except (TypeError, ValueError): raise exc.InvalidTypeError(expected=int, actual=type(value)) if self.min is not None and value < self.min: raise exc.MinValueError(expected=self.min, actual=value) if self.max is not None and value > self.max: raise exc.MaxValueError(expected=self.max, actual=value) if self.options is not None and value not in self.options: raise exc.OptionsError(expected=self.options, actual=value) return value
[docs]class Float(abstract.Validator): """ Floating Point Number Validator :param bool nullable: accept ``None`` as a valid value. :param bool coerce: try to convert non-float value to ``float``. :param bool nan: accept ``Not-a-Number`` as a valid value. :param bool inf: accept ``Infinity`` as a valid value. :param float min: lower limit. :param float max: upper limit. :raises InvalidTypeError: * if ``value is None`` and ``not self.nullable``; * if ``not isinstance(value, float)`` and ``not self.coerce``; * if ``float(value)`` raises ``ValueError`` or ``TypeError``. :raises FloatValueError: * if ``math.isnan(value)`` and ``not self.nan``; * if ``math.isinf(value)`` and ``not self.inf``. :raises MinValueError: if ``value < self.min``. :raises MaxValueError: if ``value > self.max``. :note: It always converts ``int`` to ``float``. """ __slots__ = ("nullable", "coerce", "nan", "inf", "min", "max") def __init__( self, nullable=False, coerce=False, nan=False, inf=False, min=None, max=None, alias=None, replace=False, ): nullable = contracts.expect_flag(self, "nullable", nullable) coerce = contracts.expect_flag(self, "coerce", coerce) nan = contracts.expect_flag(self, "nan", nan) inf = contracts.expect_flag(self, "inf", inf) min = contracts.expect( self, "min", min, nullable=True, types=numbers, convert_to=float ) max = contracts.expect( self, "max", max, nullable=True, types=numbers, convert_to=float ) setattr = object.__setattr__ setattr(self, "nullable", nullable) setattr(self, "coerce", coerce) setattr(self, "nan", nan) setattr(self, "inf", inf) setattr(self, "min", min) setattr(self, "max", max) self._register(alias, replace) def __call__(self, value, __context=None): if value is None and self.nullable: return value if not isinstance(value, float): if isinstance(value, int): # Always implicitly convert ``int`` to ``float`` value = float(value) elif not self.coerce: raise exc.InvalidTypeError(expected=float, actual=type(value)) else: try: value = float(value) except (TypeError, ValueError): raise exc.InvalidTypeError(expected=float, actual=type(value)) if math.isnan(value): if not self.nan: raise exc.FloatValueError(expected="number", actual=value) # It doesn't make sence to future checks if value is ``Nan`` return value if math.isinf(value) and not self.inf: raise exc.FloatValueError(expected="finite", actual=value) if self.min is not None and value < self.min: raise exc.MinValueError(expected=self.min, actual=value) if self.max is not None and value > self.max: raise exc.MaxValueError(expected=self.max, actual=value) return value