add `ConstrainedFloat, confloat, PositiveFloat and NegativeFloat` types #166 (#166)

This commit is contained in:
Nikita Grishko
2018-04-28 20:40:28 +03:00
committed by Samuel Colvin
parent f88e5925b5
commit dc07277017
4 changed files with 65 additions and 4 deletions
+1
View File
@@ -8,6 +8,7 @@ v0.8.1 (2018-XX-XX)
* tweak email-validator import error message #145
* fix parse error of parse_date() and parse_datetime() when input is 0 #144
* add ``Config.anystr_strip_whitespace`` and ``strip_whitespace`` kwarg to ``constr``, by default values is `False` #163
* add ``ConstrainedFloat``, ``confloat``, ``PositiveFloat`` and ``NegativeFloat`` types #166
v0.8.0 (2018-03-25)
...................
+12 -2
View File
@@ -1,8 +1,8 @@
from pathlib import Path
from uuid import UUID
from pydantic import (DSN, BaseModel, EmailStr, NameEmail, PyObject, conint,
constr, PositiveInt, NegativeInt)
from pydantic import (DSN, BaseModel, EmailStr, NameEmail, NegativeFloat, NegativeInt, PositiveFloat, PositiveInt,
PyObject, confloat, conint, constr)
class Model(BaseModel):
@@ -17,6 +17,10 @@ class Model(BaseModel):
pos_int: PositiveInt = None
neg_int: NegativeInt = None
big_float: confloat(gt=1000, lt=1024) = None
pos_float: PositiveFloat = None
neg_float: NegativeFloat = None
email_address: EmailStr = None
email_and_name: NameEmail = None
@@ -39,6 +43,9 @@ m = Model(
big_int=1001,
pos_int=1,
neg_int=-1,
big_float=1002.1,
pos_float=2.2,
neg_float=-2.3,
email_address='Samuel Colvin <s@muelcolvin.com >',
email_and_name='Samuel Colvin <s@muelcolvin.com >',
uuid='ebcdab58-6eb8-46fb-a190-d07a33e9eac8'
@@ -54,6 +61,9 @@ print(m.dict())
'big_int': 1001,
'pos_int': 1,
'neg_int': -1,
'big_float': 1002.1,
'pos_float': 2.2,
'neg_float': -2.3,
'email_address': 's@muelcolvin.com',
'email_and_name': <NameEmail("Samuel Colvin <s@muelcolvin.com>")>,
...
+36
View File
@@ -25,6 +25,10 @@ __all__ = [
'conint',
'PositiveInt',
'NegativeInt',
'ConstrainedFloat',
'confloat',
'PositiveFloat',
'NegativeFloat',
]
NoneStr = Optional[str]
@@ -201,4 +205,36 @@ class NegativeInt(ConstrainedInt):
lt = 0
class ConstrainedFloat(float):
gt: Union[int, float] = None
lt: Union[int, float] = None
@classmethod
def get_validators(cls):
yield float
yield cls.validate
@classmethod
def validate(cls, value: float) -> float:
if cls.gt is not None and value <= cls.gt:
raise ValueError(f'size less than minimum allowed: {cls.gt}')
elif cls.lt is not None and value >= cls.lt:
raise ValueError(f'size greater than maximum allowed: {cls.lt}')
return value
def confloat(*, gt=None, lt=None) -> Type[float]:
# use kwargs then define conf in a dict to aid with IDE type hinting
namespace = dict(gt=gt, lt=lt)
return type('ConstrainedFloatValue', (ConstrainedFloat,), namespace)
class PositiveFloat(ConstrainedFloat):
gt = 0
class NegativeFloat(ConstrainedFloat):
lt = 0
# TODO, JsonEither, JsonList, JsonDict
+16 -2
View File
@@ -6,8 +6,8 @@ from uuid import UUID
import pytest
from pydantic import (DSN, BaseModel, EmailStr, NameEmail, NegativeInt, PositiveInt, PyObject, StrictStr,
ValidationError, conint, constr)
from pydantic import (DSN, BaseModel, EmailStr, NameEmail, NegativeFloat, NegativeInt, PositiveFloat, PositiveInt,
PyObject, StrictStr, ValidationError, confloat, conint, constr)
try:
import email_validator
@@ -410,6 +410,20 @@ def test_int_validation():
assert exc_info.value.message == '3 errors validating input'
class FloatModel(BaseModel):
a: PositiveFloat = None
b: NegativeFloat = None
c: confloat(gt=4, lt=12.2) = None
def test_float_validation():
m = FloatModel(a=5.1, b=-5.2, c=5.3)
assert m == {'a': 5.1, 'b': -5.2, 'c': 5.3}
with pytest.raises(ValidationError) as exc_info:
FloatModel(a=-5.1, b=5.2, c=-5.3)
assert exc_info.value.message == '3 errors validating input'
def test_set():
class SetModel(BaseModel):
v: set = ...