Repository: pydantic/pydantic

Issue:
Bad error message for gt validation
I am using the latest pydantic release and I came here to let you know that there are problems with the validation error messages related to "gt" and "lt".

I have:

    ZeroOrMore: conint = conint(gt=-1)

    class Config(Pydantic):
        max_file_size: ZeroOrMore = 0

I define `ZeroOrMore` with `gt=-1` because zero must be allowed and the nearest lower integer is -1. And this much is correct, inasmuch the value -1 causes a validation error:

    max_file_size:
      size less than minimum allowed: -1 (error_type=ValueError track=ConstrainedIntValue)

But there are 2 problems in this error message:

1. The value printed should be 0, not -1, because 0 is the "minimum allowed".
2. The error message says "size". But this is not a size. Who said anything about sizes? The error message should simply read, "Lower than the minimum 0.".

This is related to #171 and #174.


Discussion:
yes, I guess the error message should be improved.
With current implementation in master we got:
```
In [1]: from pydantic import BaseModel, conint

In [2]: ZeroOrMore: conint = conint(gt=-1)
   ...:
   ...: class Model(BaseModel):
   ...:     max_file_size: ZeroOrMore = 0
   ...:

In [3]: Model(max_file_size=-1)
Out[3]: <Model max_file_size=-1>

In [4]: Model(max_file_size=-2)
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-5-04c6fd7ede6b> in <module>()
----> 1 Model(max_file_size=-2)

~/Projects/Python/pydantic/pydantic/main.py in __init__(self, **data)
    147
    148     def __init__(self, **data):
--> 149         self.__setstate__(self._process_values(data))
    150
    151     def __getattr__(self, name):

~/Projects/Python/pydantic/pydantic/main.py in _process_values(self, input_data)
    296
    297         if errors:
--> 298             raise ValidationError(errors)
    299         return values
    300

ValidationError: validation errors
max_file_size
  ensure this value is greater than -1 (type=value_error.number.min_size; limit_value=-1)
```

@samuelcolvin I think this is not right and we need to change validation logic from `v < field.type_.gt` to `v <= field.type_.gt`. Same for `lt`:

```
In [6]: class Config(BaseModel):
   ...:     foo: conint(lt=3)
   ...:

In [7]: class Model(BaseModel):
   ...:     foo: conint(lt=3)
   ...:

In [8]: Model(foo=3)
Out[8]: <Model foo=3>

In [9]: Model(foo=4)
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-9-f5d9db1ef75e> in <module>()
----> 1 Model(foo=4)

~/Projects/Python/pydantic/pydantic/main.py in __init__(self, **data)
    147
    148     def __init__(self, **data):
--> 149         self.__setstate__(self._process_values(data))
    150
    151     def __getattr__(self, name):

~/Projects/Python/pydantic/pydantic/main.py in _process_values(self, input_data)
    296
    297         if errors:
--> 298             raise ValidationError(errors)
    299         return values
    300

ValidationError: validation errors
foo
  ensure this value is less than 3 (type=value_error.number.max_size; limit_value=3)
```
fixing now.

Resolving patch:
diff --git a/pydantic/errors.py b/pydantic/errors.py
--- a/pydantic/errors.py
+++ b/pydantic/errors.py
@@ -120,16 +120,16 @@ def __init__(self, *, pattern: str) -> None:
         super().__init__(pattern=pattern)
 
 
-class NumberMinSizeError(PydanticValueError):
-    code = 'number.min_size'
+class NumberGtError(PydanticValueError):
+    code = 'number.gt'
     msg_template = 'ensure this value is greater than {limit_value}'
 
     def __init__(self, *, limit_value: Union[int, float, Decimal]) -> None:
         super().__init__(limit_value=limit_value)
 
 
-class NumberMaxSizeError(PydanticValueError):
-    code = 'number.max_size'
+class NumberLtError(PydanticValueError):
+    code = 'number.lt'
     msg_template = 'ensure this value is less than {limit_value}'
 
     def __init__(self, *, limit_value: Union[int, float, Decimal]) -> None:
diff --git a/pydantic/validators.py b/pydantic/validators.py
--- a/pydantic/validators.py
+++ b/pydantic/validators.py
@@ -79,11 +79,11 @@ def float_validator(v) -> float:
 
 
 def number_size_validator(v, field, config, **kwargs):
-    if field.type_.gt is not None and v < field.type_.gt:
-        raise errors.NumberMinSizeError(limit_value=field.type_.gt)
+    if field.type_.gt is not None and v <= field.type_.gt:
+        raise errors.NumberGtError(limit_value=field.type_.gt)
 
-    if field.type_.lt is not None and v > field.type_.lt:
-        raise errors.NumberMaxSizeError(limit_value=field.type_.lt)
+    if field.type_.lt is not None and v >= field.type_.lt:
+        raise errors.NumberLtError(limit_value=field.type_.lt)
 
     return v
 


Test patch:
diff --git a/tests/test_error_wrappers.py b/tests/test_error_wrappers.py
--- a/tests/test_error_wrappers.py
+++ b/tests/test_error_wrappers.py
@@ -32,7 +32,7 @@
 g
   uuid version 1 expected (type=value_error.uuid.version; required_version=1)
 h
-  yet another error message template 42 (type=value_error.number.min_size; limit_value=42)""",
+  yet another error message template 42 (type=value_error.number.gt; limit_value=42)""",
     ),
     (
         'flatten_errors',
@@ -122,7 +122,7 @@
                     'h',
                 ),
                 'msg': 'yet another error message template 42',
-                'type': 'value_error.number.min_size',
+                'type': 'value_error.number.gt',
                 'ctx': {
                     'limit_value': 42,
                 },
@@ -221,7 +221,7 @@
       "h"
     ],
     "msg": "yet another error message template 42",
-    "type": "value_error.number.min_size"
+    "type": "value_error.number.gt"
   }
 ]"""
     ),
@@ -250,7 +250,7 @@
 g
   uuid version 1 expected (type=value_error.uuid.version; required_version=1)
 h
-  yet another error message template 42 (type=value_error.number.min_size; limit_value=42)"""
+  yet another error message template 42 (type=value_error.number.gt; limit_value=42)"""
     ),
 ))
 def test_validation_error(result, expected):
@@ -271,7 +271,7 @@ class Model(BaseModel):
 
         class Config:
             error_msg_templates = {
-                'value_error.number.min_size': 'yet another error message template {limit_value}',
+                'value_error.number.gt': 'yet another error message template {limit_value}',
             }
 
     with pytest.raises(ValidationError) as exc_info:
diff --git a/tests/test_types.py b/tests/test_types.py
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -519,7 +519,7 @@ class Model(BaseModel):
         {
             'loc': ('a',),
             'msg': 'ensure this value is greater than 0',
-            'type': 'value_error.number.min_size',
+            'type': 'value_error.number.gt',
             'ctx': {
                 'limit_value': 0,
             },
@@ -527,7 +527,7 @@ class Model(BaseModel):
         {
             'loc': ('b',),
             'msg': 'ensure this value is less than 0',
-            'type': 'value_error.number.max_size',
+            'type': 'value_error.number.lt',
             'ctx': {
                 'limit_value': 0,
             },
@@ -535,7 +535,7 @@ class Model(BaseModel):
         {
             'loc': ('c',),
             'msg': 'ensure this value is greater than 4',
-            'type': 'value_error.number.min_size',
+            'type': 'value_error.number.gt',
             'ctx': {
                 'limit_value': 4,
             },
@@ -558,7 +558,7 @@ class Model(BaseModel):
         {
             'loc': ('a',),
             'msg': 'ensure this value is greater than 0',
-            'type': 'value_error.number.min_size',
+            'type': 'value_error.number.gt',
             'ctx': {
                 'limit_value': 0,
             },
@@ -566,7 +566,7 @@ class Model(BaseModel):
         {
             'loc': ('b',),
             'msg': 'ensure this value is less than 0',
-            'type': 'value_error.number.max_size',
+            'type': 'value_error.number.lt',
             'ctx': {
                 'limit_value': 0,
             },
@@ -574,7 +574,7 @@ class Model(BaseModel):
         {
             'loc': ('c',),
             'msg': 'ensure this value is greater than 4',
-            'type': 'value_error.number.min_size',
+            'type': 'value_error.number.gt',
             'ctx': {
                 'limit_value': 4,
             },
@@ -704,7 +704,7 @@ class Config:
         {
             'loc': ('foo',),
             'msg': 'ensure this value is greater than 42.24',
-            'type': 'value_error.number.min_size',
+            'type': 'value_error.number.gt',
             'ctx': {
                 'limit_value': Decimal('42.24'),
             },
@@ -715,7 +715,7 @@ class Config:
         {
             'loc': ('foo',),
             'msg': 'ensure this value is less than 42.24',
-            'type': 'value_error.number.max_size',
+            'type': 'value_error.number.lt',
             'ctx': {
                 'limit_value': Decimal('42.24'),
             },
@@ -836,3 +836,27 @@ class Model(BaseModel):
         assert exc_info.value.flatten_errors() == result
     else:
         assert Model(foo=value).foo == result
+
+
+def test_number_gt():
+    class Model(BaseModel):
+        a: conint(gt=-1) = 0
+
+    assert Model(a=0).dict() == {'a': 0}
+    with pytest.raises(ValidationError) as exc_info:
+        Model(a=-1)
+    assert (
+        'ensure this value is greater than -1 (type=value_error.number.gt; limit_value=-1)'
+    ) in str(exc_info.value)
+
+
+def test_number_lt():
+    class Model(BaseModel):
+        a: conint(lt=5) = 0
+
+    assert Model(a=4).dict() == {'a': 4}
+    with pytest.raises(ValidationError) as exc_info:
+        Model(a=5)
+    assert (
+        'ensure this value is less than 5 (type=value_error.number.lt; limit_value=5)'
+    ) in str(exc_info.value)