Repository: samuelcolvin/pydantic
Pull request #2083 (2020-11-02 05:51:03)

Bump python-dotenv from 0.14.0 to 0.15.0
Bumps [python-dotenv](https://github.com/theskumar/python-dotenv) from 0.14.0 to 0.15.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/theskumar/python-dotenv/blob/master/CHANGELOG.md">python-dotenv's changelog</a>.</em></p>
<blockquote>
<h2>[0.15.0] - 2020-10-28</h2>
<h3>Added</h3>
<ul>
<li>Add <code>--export</code> option to <code>set</code> to make it prepend the binding with <code>export</code> (<a href="https://github-redirect.dependabot.com/theskumar/python-dotenv/issues/270">#270</a> by
[<a href="https://github.com/jadutter">@jadutter</a>]).</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Make <code>set</code> command create the <code>.env</code> file in the current directory if no <code>.env</code> file was
found (<a href="https://github-redirect.dependabot.com/theskumar/python-dotenv/issues/270">#270</a> by [<a href="https://github.com/jadutter">@jadutter</a>]).</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Fix potentially empty expanded value for duplicate key (<a href="https://github-redirect.dependabot.com/theskumar/python-dotenv/issues/260">#260</a> by [<a href="https://github.com/bbc2">@bbc2</a>]).</li>
<li>Fix import error on Python 3.5.0 and 3.5.1 (<a href="https://github-redirect.dependabot.com/theskumar/python-dotenv/issues/267">#267</a> by [<a href="https://github.com/gongqingkui">@gongqingkui</a>]).</li>
<li>Fix parsing of unquoted values containing several adjacent space or tab characters
(<a href="https://github-redirect.dependabot.com/theskumar/python-dotenv/issues/277">#277</a> by [<a href="https://github.com/bbc2">@bbc2</a>], review by [<a href="https://github.com/x-yuri">@x-yuri</a>]).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/theskumar/python-dotenv/commit/e13d957bf48224453c5d9d9a7a83a13b999e0196"><code>e13d957</code></a> Release v0.15.0</li>
<li><a href="https://github.com/theskumar/python-dotenv/commit/fa354ce5d4c4f515e340265bf7c0f7e32fb0e75d"><code>fa354ce</code></a> Migrate from travis-ci.org to .com</li>
<li><a href="https://github.com/theskumar/python-dotenv/commit/6fc345833f148762bb8783896d1b1059b146886f"><code>6fc3458</code></a> Add python 3.9 to CI</li>
<li><a href="https://github.com/theskumar/python-dotenv/commit/7b172fe24830bb8fd8cd943b73570e8cd6515ba1"><code>7b172fe</code></a> Fix parsing of unquoted values with two spaces</li>
<li><a href="https://github.com/theskumar/python-dotenv/commit/6ca2e2ab399c7e41be276bc830f21af3092f5d28"><code>6ca2e2a</code></a> Add --export to <code>set</code> and make it create env file</li>
<li><a href="https://github.com/theskumar/python-dotenv/commit/92ec3b280db6e9f946994a3bf5815d6b6447892c"><code>92ec3b2</code></a> Update README.md</li>
<li><a href="https://github.com/theskumar/python-dotenv/commit/78be0f865d34c34120456ff0b6b9d34a95ac6879"><code>78be0f8</code></a> Fix import error on Python 3.5.0 and 3.5.1</li>
<li><a href="https://github.com/theskumar/python-dotenv/commit/4b434362e1832771fd08c14b8af67a8fd562b854"><code>4b43436</code></a> Fix empty expanded value for duplicate key</li>
<li>See full diff in <a href="https://github.com/theskumar/python-dotenv/compare/v0.14.0...v0.15.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=python-dotenv&package-manager=pip&previous-version=0.14.0&new-version=0.15.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/configuring-github-dependabot-security-updates)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

codecov[bot]: # [Codecov](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083?src=pr&el=h1) Report
> Merging [#2083](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083?src=pr&el=desc) into [master](https://codecov.io/gh/samuelcolvin/pydantic/commit/685303237273eb21f379227b721295e0bf7e253b?el=desc) will **decrease** coverage by `0.87%`.
> The diff coverage is `n/a`.


```diff
@@             Coverage Diff             @@
##            master    #2083      +/-   ##
===========================================
- Coverage   100.00%   99.12%   -0.88%     
===========================================
  Files           21       21              
  Lines         4093     4092       -1     
  Branches       823      823              
===========================================
- Hits          4093     4056      -37     
- Misses           0       29      +29     
- Partials         0        7       +7     
```


| [Impacted Files](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pydantic/typing.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083/diff?src=pr&el=tree#diff-cHlkYW50aWMvdHlwaW5nLnB5) | `77.23% <0.00%> (-22.77%)` | :arrow_down: |
| [pydantic/version.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083/diff?src=pr&el=tree#diff-cHlkYW50aWMvdmVyc2lvbi5weQ==) | `88.23% <0.00%> (-11.77%)` | :arrow_down: |
| [pydantic/env\_settings.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083/diff?src=pr&el=tree#diff-cHlkYW50aWMvZW52X3NldHRpbmdzLnB5) | `97.93% <0.00%> (-2.07%)` | :arrow_down: |
| [pydantic/networks.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083/diff?src=pr&el=tree#diff-cHlkYW50aWMvbmV0d29ya3MucHk=) | `99.22% <0.00%> (-0.78%)` | :arrow_down: |
| [pydantic/utils.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083/diff?src=pr&el=tree#diff-cHlkYW50aWMvdXRpbHMucHk=) | `99.29% <0.00%> (-0.71%)` | :arrow_down: |
| [pydantic/decorator.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083/diff?src=pr&el=tree#diff-cHlkYW50aWMvZGVjb3JhdG9yLnB5) | `100.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083?src=pr&el=footer). Last update [6853032...cc9259d](https://codecov.io/gh/samuelcolvin/pydantic/pull/2083?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/changes/1975-mdavis-xyz.md b/changes/1975-mdavis-xyz.md
new file mode 100644
index 000000000..82cf2eb29
--- /dev/null
+++ b/changes/1975-mdavis-xyz.md
@@ -0,0 +1,1 @@
+Add `NonNegativeInt`, `NonPositiveInt`, `NonNegativeFloat`, `NonPositiveFloat`
diff --git a/changes/2045-PrettyWood.md b/changes/2045-PrettyWood.md
new file mode 100644
index 000000000..1fdf47c1e
--- /dev/null
+++ b/changes/2045-PrettyWood.md
@@ -0,0 +1,1 @@
+add default encoder for `Pattern` type
\ No newline at end of file
diff --git a/changes/2071-uriyyo.md b/changes/2071-uriyyo.md
new file mode 100644
index 000000000..36b9a6aa1
--- /dev/null
+++ b/changes/2071-uriyyo.md
@@ -0,0 +1,1 @@
+Updated `create_model` return type annotation to return type which inherits from `__base__` argument.
\ No newline at end of file
diff --git a/changes/2090-PrettyWood.md b/changes/2090-PrettyWood.md
new file mode 100644
index 000000000..2e2e56f55
--- /dev/null
+++ b/changes/2090-PrettyWood.md
@@ -0,0 +1,1 @@
+Ignore `__doc__` as private attribute when `Config.underscore_attrs_are_private` is set
\ No newline at end of file
diff --git a/docs/build/schema_mapping.py b/docs/build/schema_mapping.py
index f9731c0a0..578172746 100755
--- a/docs/build/schema_mapping.py
+++ b/docs/build/schema_mapping.py
@@ -382,6 +382,20 @@ table = [
         'JSON Schema Validation',
         ''
     ],
+    [
+        'NonNegativeInt',
+        'integer',
+        {'minimum': 0},
+        'JSON Schema Validation',
+        ''
+    ],
+    [
+        'NonPositiveInt',
+        'integer',
+        {'maximum': 0},
+        'JSON Schema Validation',
+        ''
+    ],
     [
         'ConstrainedFloat',
         'number',
@@ -413,6 +427,20 @@ table = [
         'JSON Schema Validation',
         ''
     ],
+    [
+        'NonNegativeFloat',
+        'number',
+        {'minimum': 0},
+        'JSON Schema Validation',
+        ''
+    ],
+    [
+        'NonPositiveFloat',
+        'number',
+        {'maximum': 0},
+        'JSON Schema Validation',
+        ''
+    ],
     [
         'ConstrainedDecimal',
         'number',
diff --git a/docs/examples/types_constrained.py b/docs/examples/types_constrained.py
index c97fe24ae..53addddb1 100644
--- a/docs/examples/types_constrained.py
+++ b/docs/examples/types_constrained.py
@@ -6,6 +6,10 @@ from pydantic import (
     NegativeInt,
     PositiveFloat,
     PositiveInt,
+    NonNegativeFloat,
+    NonNegativeInt,
+    NonPositiveFloat,
+    NonPositiveInt,
     conbytes,
     condecimal,
     confloat,
@@ -29,12 +33,16 @@ class Model(BaseModel):
     mod_int: conint(multiple_of=5)
     pos_int: PositiveInt
     neg_int: NegativeInt
+    non_neg_int: NonNegativeInt
+    non_pos_int: NonPositiveInt
 
     big_float: confloat(gt=1000, lt=1024)
     unit_interval: confloat(ge=0, le=1)
     mod_float: confloat(multiple_of=0.5)
     pos_float: PositiveFloat
     neg_float: NegativeFloat
+    non_neg_float: NonNegativeFloat
+    non_pos_float: NonPositiveFloat
 
     short_list: conlist(int, min_items=1, max_items=4)
     short_set: conset(int, min_items=1, max_items=4)
diff --git a/pydantic/__init__.py b/pydantic/__init__.py
index 5cfc0583f..7b8a609b6 100644
--- a/pydantic/__init__.py
+++ b/pydantic/__init__.py
@@ -76,10 +76,14 @@ __all__ = [
     'conint',
     'PositiveInt',
     'NegativeInt',
+    'NonNegativeInt',
+    'NonPositiveInt',
     'ConstrainedFloat',
     'confloat',
     'PositiveFloat',
     'NegativeFloat',
+    'NonNegativeFloat',
+    'NonPositiveFloat',
     'ConstrainedDecimal',
     'condecimal',
     'UUID1',
diff --git a/pydantic/json.py b/pydantic/json.py
index 9ce9d57d5..143ae877b 100644
--- a/pydantic/json.py
+++ b/pydantic/json.py
@@ -5,7 +5,7 @@ from enum import Enum
 from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
 from pathlib import Path
 from types import GeneratorType
-from typing import Any, Callable, Dict, Type, Union
+from typing import Any, Callable, Dict, Pattern, Type, Union
 from uuid import UUID
 
 from .color import Color
@@ -54,6 +54,9 @@ def pydantic_encoder(obj: Any) -> Any:
     elif is_dataclass(obj):
         return asdict(obj)
 
+    if isinstance(obj, Pattern):
+        return obj.pattern
+
     # Check the class type and its superclasses for a matching encoder
     for base in obj.__class__.__mro__[:-1]:
         try:
diff --git a/pydantic/main.py b/pydantic/main.py
index 3d57b1bbc..b63c6cbaf 100644
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -870,11 +870,11 @@ def create_model(
     __model_name: str,
     *,
     __config__: Type[BaseConfig] = None,
-    __base__: Type[BaseModel] = None,
+    __base__: Type['Model'] = None,
     __module__: str = __name__,
     __validators__: Dict[str, classmethod] = None,
     **field_definitions: Any,
-) -> Type[BaseModel]:
+) -> Type['Model']:
     """
     Dynamically create a model.
     :param __model_name: name of the created model
@@ -887,11 +887,12 @@ def create_model(
         `foobar=(str, ...)` or `foobar=123`, or, for complex use-cases, in the format
         `<name>=<FieldInfo>`, e.g. `foo=Field(default_factory=datetime.utcnow, alias='bar')`
     """
-    if __base__:
+
+    if __base__ is not None:
         if __config__ is not None:
             raise ConfigError('to avoid confusion __config__ and __base__ cannot be used together')
     else:
-        __base__ = BaseModel
+        __base__ = cast(Type['Model'], BaseModel)
 
     fields = {}
     annotations = {}
diff --git a/pydantic/types.py b/pydantic/types.py
index 9c462e124..9c3279b99 100644
--- a/pydantic/types.py
+++ b/pydantic/types.py
@@ -63,10 +63,14 @@ __all__ = [
     'conint',
     'PositiveInt',
     'NegativeInt',
+    'NonNegativeInt',
+    'NonPositiveInt',
     'ConstrainedFloat',
     'confloat',
     'PositiveFloat',
     'NegativeFloat',
+    'NonNegativeFloat',
+    'NonPositiveFloat',
     'ConstrainedDecimal',
     'condecimal',
     'UUID1',
@@ -379,6 +383,14 @@ class NegativeInt(ConstrainedInt):
     lt = 0
 
 
+class NonPositiveInt(ConstrainedInt):
+    le = 0
+
+
+class NonNegativeInt(ConstrainedInt):
+    ge = 0
+
+
 class StrictInt(ConstrainedInt):
     strict = True
 
@@ -440,6 +452,14 @@ class NegativeFloat(ConstrainedFloat):
     lt = 0
 
 
+class NonPositiveFloat(ConstrainedFloat):
+    le = 0
+
+
+class NonNegativeFloat(ConstrainedFloat):
+    ge = 0
+
+
 class StrictFloat(ConstrainedFloat):
     strict = True
 
diff --git a/pydantic/utils.py b/pydantic/utils.py
index 4592223d1..e3e613ca3 100644
--- a/pydantic/utils.py
+++ b/pydantic/utils.py
@@ -631,4 +631,10 @@ def is_valid_field(name: str) -> bool:
 
 
 def is_valid_private_name(name: str) -> bool:
-    return not is_valid_field(name) and name not in {'__annotations__', '__classcell__', '__module__', '__qualname__'}
+    return not is_valid_field(name) and name not in {
+        '__annotations__',
+        '__classcell__',
+        '__doc__',
+        '__module__',
+        '__qualname__',
+    }
diff --git a/requirements.txt b/requirements.txt
index 80e8d9c1a..56f767b4d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,4 +5,4 @@ devtools==0.6.1
 email-validator==1.1.1
 dataclasses==0.6; python_version < '3.7'
 typing-extensions==3.7.4.1; python_version < '3.8'
-python-dotenv==0.14.0
+python-dotenv==0.15.0


Test patch:
diff --git a/tests/mypy/modules/plugin_success.py b/tests/mypy/modules/plugin_success.py
index af4b8e376..c173bc9e0 100644
--- a/tests/mypy/modules/plugin_success.py
+++ b/tests/mypy/modules/plugin_success.py
@@ -1,6 +1,6 @@
 from typing import ClassVar, Optional, Union
 
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field, create_model
 from pydantic.dataclasses import dataclass
 
 
@@ -133,3 +133,9 @@ class NestedModel(BaseModel):
 
 
 _ = NestedModel.Model
+
+
+DynamicModel = create_model('DynamicModel', __base__=Model)
+
+dynamic_model = DynamicModel(x=1, y='y')
+dynamic_model.x = 2
diff --git a/tests/test_json.py b/tests/test_json.py
index cf916f6ae..07bfc04f7 100644
--- a/tests/test_json.py
+++ b/tests/test_json.py
@@ -1,5 +1,6 @@
 import datetime
 import json
+import re
 import sys
 from dataclasses import dataclass as vanilla_dataclass
 from decimal import Decimal
@@ -51,6 +52,7 @@ class MyEnum(Enum):
         (Decimal('12.34'), '12.34'),
         (create_model('BarModel', a='b', c='d')(), '{"a": "b", "c": "d"}'),
         (MyEnum.foo, '"bar"'),
+        (re.compile('^regex$'), '"^regex$"'),
     ],
 )
 def test_encoding(input, output):
diff --git a/tests/test_private_attributes.py b/tests/test_private_attributes.py
index 48be872e2..21f52090b 100644
--- a/tests/test_private_attributes.py
+++ b/tests/test_private_attributes.py
@@ -55,6 +55,8 @@ def test_private_attribute_factory():
 
 def test_private_attribute_annotation():
     class Model(BaseModel):
+        """The best model"""
+
         __foo__: str
 
         class Config:
@@ -63,6 +65,7 @@ def test_private_attribute_annotation():
     assert Model.__slots__ == {'__foo__'}
     assert repr(Model.__foo__) == "<member '__foo__' of 'Model' objects>"
     assert Model.__private_attributes__ == {'__foo__': PrivateAttr(Undefined)}
+    assert repr(Model.__doc__) == "'The best model'"
 
     m = Model()
     with pytest.raises(AttributeError):
diff --git a/tests/test_schema.py b/tests/test_schema.py
index 9da7d14cc..13d92585a 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -42,6 +42,10 @@ from pydantic.types import (
     NoneBytes,
     NoneStr,
     NoneStrBytes,
+    NonNegativeFloat,
+    NonNegativeInt,
+    NonPositiveFloat,
+    NonPositiveInt,
     PositiveFloat,
     PositiveInt,
     PyObject,
@@ -754,6 +758,8 @@ def test_secret_types(field_type, inner_type):
         (conint(multiple_of=5), {'multipleOf': 5}),
         (PositiveInt, {'exclusiveMinimum': 0}),
         (NegativeInt, {'exclusiveMaximum': 0}),
+        (NonNegativeInt, {'minimum': 0}),
+        (NonPositiveInt, {'maximum': 0}),
     ],
 )
 def test_special_int_types(field_type, expected_schema):
@@ -780,6 +786,8 @@ def test_special_int_types(field_type, expected_schema):
         (confloat(multiple_of=5), {'multipleOf': 5}),
         (PositiveFloat, {'exclusiveMinimum': 0}),
         (NegativeFloat, {'exclusiveMaximum': 0}),
+        (NonNegativeFloat, {'minimum': 0}),
+        (NonPositiveFloat, {'maximum': 0}),
         (ConstrainedDecimal, {}),
         (condecimal(gt=5, lt=10), {'exclusiveMinimum': 5, 'exclusiveMaximum': 10}),
         (condecimal(ge=5, le=10), {'minimum': 5, 'maximum': 10}),
diff --git a/tests/test_types.py b/tests/test_types.py
index 7e4c7e0bd..b07d1408d 100644
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -43,6 +43,10 @@ from pydantic import (
     NameEmail,
     NegativeFloat,
     NegativeInt,
+    NonNegativeFloat,
+    NonNegativeInt,
+    NonPositiveFloat,
+    NonPositiveInt,
     PositiveFloat,
     PositiveInt,
     PyObject,
@@ -1178,15 +1182,17 @@ def test_int_validation():
     class Model(BaseModel):
         a: PositiveInt = None
         b: NegativeInt = None
-        c: conint(gt=4, lt=10) = None
-        d: conint(ge=0, le=10) = None
-        e: conint(multiple_of=5) = None
+        c: NonNegativeInt = None
+        d: NonPositiveInt = None
+        e: conint(gt=4, lt=10) = None
+        f: conint(ge=0, le=10) = None
+        g: conint(multiple_of=5) = None
 
-    m = Model(a=5, b=-5, c=5, d=0, e=25)
-    assert m == {'a': 5, 'b': -5, 'c': 5, 'd': 0, 'e': 25}
+    m = Model(a=5, b=-5, c=0, d=0, e=5, f=0, g=25)
+    assert m == {'a': 5, 'b': -5, 'c': 0, 'd': 0, 'e': 5, 'f': 0, 'g': 25}
 
     with pytest.raises(ValidationError) as exc_info:
-        Model(a=-5, b=5, c=-5, d=11, e=42)
+        Model(a=-5, b=5, c=-5, d=5, e=-5, f=11, g=42)
     assert exc_info.value.errors() == [
         {
             'loc': ('a',),
@@ -1202,18 +1208,30 @@ def test_int_validation():
         },
         {
             'loc': ('c',),
+            'msg': 'ensure this value is greater than or equal to 0',
+            'type': 'value_error.number.not_ge',
+            'ctx': {'limit_value': 0},
+        },
+        {
+            'loc': ('d',),
+            'msg': 'ensure this value is less than or equal to 0',
+            'type': 'value_error.number.not_le',
+            'ctx': {'limit_value': 0},
+        },
+        {
+            'loc': ('e',),
             'msg': 'ensure this value is greater than 4',
             'type': 'value_error.number.not_gt',
             'ctx': {'limit_value': 4},
         },
         {
-            'loc': ('d',),
+            'loc': ('f',),
             'msg': 'ensure this value is less than or equal to 10',
             'type': 'value_error.number.not_le',
             'ctx': {'limit_value': 10},
         },
         {
-            'loc': ('e',),
+            'loc': ('g',),
             'msg': 'ensure this value is a multiple of 5',
             'type': 'value_error.number.not_multiple',
             'ctx': {'multiple_of': 5},
@@ -1225,15 +1243,17 @@ def test_float_validation():
     class Model(BaseModel):
         a: PositiveFloat = None
         b: NegativeFloat = None
-        c: confloat(gt=4, lt=12.2) = None
-        d: confloat(ge=0, le=9.9) = None
-        e: confloat(multiple_of=0.5) = None
+        c: NonNegativeFloat = None
+        d: NonPositiveFloat = None
+        e: confloat(gt=4, lt=12.2) = None
+        f: confloat(ge=0, le=9.9) = None
+        g: confloat(multiple_of=0.5) = None
 
-    m = Model(a=5.1, b=-5.2, c=5.3, d=9.9, e=2.5)
-    assert m.dict() == {'a': 5.1, 'b': -5.2, 'c': 5.3, 'd': 9.9, 'e': 2.5}
+    m = Model(a=5.1, b=-5.2, c=0, d=0, e=5.3, f=9.9, g=2.5)
+    assert m.dict() == {'a': 5.1, 'b': -5.2, 'c': 0, 'd': 0, 'e': 5.3, 'f': 9.9, 'g': 2.5}
 
     with pytest.raises(ValidationError) as exc_info:
-        Model(a=-5.1, b=5.2, c=-5.3, d=9.91, e=4.2)
+        Model(a=-5.1, b=5.2, c=-5.1, d=5.1, e=-5.3, f=9.91, g=4.2)
     assert exc_info.value.errors() == [
         {
             'loc': ('a',),
@@ -1249,18 +1269,30 @@ def test_float_validation():
         },
         {
             'loc': ('c',),
+            'msg': 'ensure this value is greater than or equal to 0',
+            'type': 'value_error.number.not_ge',
+            'ctx': {'limit_value': 0},
+        },
+        {
+            'loc': ('d',),
+            'msg': 'ensure this value is less than or equal to 0',
+            'type': 'value_error.number.not_le',
+            'ctx': {'limit_value': 0},
+        },
+        {
+            'loc': ('e',),
             'msg': 'ensure this value is greater than 4',
             'type': 'value_error.number.not_gt',
             'ctx': {'limit_value': 4},
         },
         {
-            'loc': ('d',),
+            'loc': ('f',),
             'msg': 'ensure this value is less than or equal to 9.9',
             'type': 'value_error.number.not_le',
             'ctx': {'limit_value': 9.9},
         },
         {
-            'loc': ('e',),
+            'loc': ('g',),
             'msg': 'ensure this value is a multiple of 0.5',
             'type': 'value_error.number.not_multiple',
             'ctx': {'multiple_of': 0.5},