Repository: samuelcolvin/pydantic
Pull request #2243 (2021-01-06 12:19:18)

Docs for con types
<!-- Thank you for your contribution! -->
<!-- Unless your change is trivial, please create an issue to discuss the change before creating a PR -->
<!-- See https://pydantic-docs.helpmanual.io/contributing/ for help on Contributing -->

## Change Summary

This change adds documentation for the con* type functions

## Related issue number
closes https://github.com/samuelcolvin/pydantic/issues/2242

## Checklist

* [x] Unit tests for the changes exist
* [x] Tests pass on CI and coverage remains at 100%
* [x] Documentation reflects the changes where applicable
* [x] `changes/<pull request or issue id>-<github username>.md` file added describing change
  (see [changes/README.md](https://github.com/samuelcolvin/pydantic/blob/master/changes/README.md) for details)


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


```diff
@@             Coverage Diff             @@
##            master    #2243      +/-   ##
===========================================
- Coverage   100.00%   99.14%   -0.86%     
===========================================
  Files           21       21              
  Lines         4199     4199              
  Branches       854      854              
===========================================
- Hits          4199     4163      -36     
- Misses           0       26      +26     
- Partials         0       10      +10     
```


| [Impacted Files](https://codecov.io/gh/samuelcolvin/pydantic/pull/2243?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pydantic/typing.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2243/diff?src=pr&el=tree#diff-cHlkYW50aWMvdHlwaW5nLnB5) | `80.14% <0.00%> (-19.86%)` | :arrow_down: |
| [pydantic/version.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2243/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/2243/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/2243/diff?src=pr&el=tree#diff-cHlkYW50aWMvbmV0d29ya3MucHk=) | `99.26% <0.00%> (-0.74%)` | :arrow_down: |
| [pydantic/utils.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2243/diff?src=pr&el=tree#diff-cHlkYW50aWMvdXRpbHMucHk=) | `99.30% <0.00%> (-0.70%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/samuelcolvin/pydantic/pull/2243?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/2243?src=pr&el=footer). Last update [13a5c7d...c4ba790](https://codecov.io/gh/samuelcolvin/pydantic/pull/2243?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/changes/2242-tayoogunbiyi.md b/changes/2242-tayoogunbiyi.md
new file mode 100644
index 000000000..db9bb03d4
--- /dev/null
+++ b/changes/2242-tayoogunbiyi.md
@@ -0,0 +1,1 @@
+add documentation for con* type functions
\ No newline at end of file
diff --git a/changes/2251-cybojenix.md b/changes/2251-cybojenix.md
new file mode 100644
index 000000000..c41d5e8b6
--- /dev/null
+++ b/changes/2251-cybojenix.md
@@ -0,0 +1,2 @@
+Fix `validate_decorator` so `**kwargs` doesn't exclude values when the keyword
+has the same name as the `*args` or `**kwargs` names.
\ No newline at end of file
diff --git a/docs/usage/types.md b/docs/usage/types.md
index f418dd94d..39aeb92a8 100644
--- a/docs/usage/types.md
+++ b/docs/usage/types.md
@@ -756,6 +756,51 @@ _(This script is complete, it should run "as is")_
 
 Where `Field` refers to the [field function](schema.md#field-customisation).
 
+### Arguments to `conlist`
+The following arguments are available when using the `conlist` type function
+
+- `item_type: Type[T]`: type of the list items
+- `min_items: int = None`: minimum number of items in the list
+- `max_items: int = None`: maximum number of items in the list
+
+### Arguments to `conset`
+The following arguments are available when using the `conset` type function
+
+- `item_type: Type[T]`: type of the set items
+- `min_items: int = None`: minimum number of items in the set
+- `max_items: int = None`: maximum number of items in the set
+
+### Arguments to `conint`
+The following arguments are available when using the `conint` type function
+
+- `strict: bool = False`: controls type coercion
+- `gt: int = None`: enforces integer to be greater than the set value
+- `ge: int = None`: enforces integer to be greater than or equal to the set value
+- `lt: int = None`: enforces integer to be less than the set value
+- `le: int = None`: enforces integer to be less than or equal to the set value
+- `multiple_of: int = None`: enforces integer to be a multiple of the set value
+
+### Arguments to `confloat`
+The following arguments are available when using the `confloat` type function
+
+- `strict: bool = False`: controls type coercion
+- `gt: float = None`: enforces float to be greater than the set value
+- `ge: float = None`: enforces float to be greater than or equal to the set value
+- `lt: float = None`: enforces float to be less than the set value
+- `le: float = None`: enforces float to be less than or equal to the set value
+- `multiple_of: float = None`: enforces float to be a multiple of the set value
+
+### Arguments to `condecimal`
+The following arguments are available when using the `condecimal` type function
+
+- `gt: Decimal = None`: enforces decimal to be greater than the set value
+- `ge: Decimal = None`: enforces decimal to be greater than or equal to the set value
+- `lt: Decimal = None`: enforces decimal to be less than the set value
+- `le: Decimal = None`: enforces decimal to be less than or equal to the set value
+- `max_digits: int = None`: maximum number of digits within the decimal. it does not include a zero before the decimal point or trailing decimal zeroes
+- `decimal_places: int = None`: max number of decimal places allowed. it does not include trailing decimal zeroes
+- `multiple_of: Decimal = None`: enforces decimal to be a multiple of the set value
+
 ### Arguments to `constr`
 The following arguments are available when using the `constr` type function
 
@@ -775,6 +820,7 @@ The following arguments are available when using the `conbytes` type function
 - `min_length: int = None`: minimum length of the byte string
 - `max_length: int = None`: maximum length of the byte string
 
+
 ## Strict Types
 
 You can use the `StrictStr`, `StrictBytes`, `StrictInt`, `StrictFloat`, and `StrictBool` types 
diff --git a/pydantic/decorator.py b/pydantic/decorator.py
index 98bc4e21b..85d04c610 100644
--- a/pydantic/decorator.py
+++ b/pydantic/decorator.py
@@ -161,8 +161,9 @@ class ValidatedFunction:
 
         var_kwargs = {}
         wrong_positional_args = []
+        non_var_fields = set(self.model.__fields__) - {self.v_args_name, self.v_kwargs_name}
         for k, v in kwargs.items():
-            if k in self.model.__fields__:
+            if k in non_var_fields:
                 if k in self.positional_only_args:
                     wrong_positional_args.append(k)
                 values[k] = v
@@ -177,9 +178,7 @@ class ValidatedFunction:
 
     def execute(self, m: BaseModel) -> Any:
         d = {k: v for k, v in m._iter() if k in m.__fields_set__}
-        kwargs = d.pop(self.v_kwargs_name, None)
-        if kwargs:
-            d.update(kwargs)
+        var_kwargs = d.pop(self.v_kwargs_name, {})
 
         if self.v_args_name in d:
             args_: List[Any] = []
@@ -193,7 +192,7 @@ class ValidatedFunction:
                     in_kwargs = True
                 else:
                     args_.append(value)
-            return self.raw_function(*args_, **kwargs)
+            return self.raw_function(*args_, **kwargs, **var_kwargs)
         elif self.positional_only_args:
             args_ = []
             kwargs = {}
@@ -202,9 +201,9 @@ class ValidatedFunction:
                     args_.append(value)
                 else:
                     kwargs[name] = value
-            return self.raw_function(*args_, **kwargs)
+            return self.raw_function(*args_, **kwargs, **var_kwargs)
         else:
-            return self.raw_function(**d)
+            return self.raw_function(**d, **var_kwargs)
 
     def create_model(self, fields: Dict[str, Any], takes_args: bool, takes_kwargs: bool, config: 'ConfigType') -> None:
         pos_args = len(self.arg_mapping)


Test patch:
diff --git a/tests/test_decorator.py b/tests/test_decorator.py
index ed7efd3ba..fa668d5f5 100644
--- a/tests/test_decorator.py
+++ b/tests/test_decorator.py
@@ -110,15 +110,22 @@ def test_untyped():
     assert foo(1, {'x': 2}, c='3', d='4') == "1, {'x': 2}, 3, 4"
 
 
-def test_var_args_kwargs():
-    @validate_arguments
+@pytest.mark.parametrize('validated', (True, False))
+def test_var_args_kwargs(validated):
     def foo(a, b, *args, d=3, **kwargs):
         return f'a={a!r}, b={b!r}, args={args!r}, d={d!r}, kwargs={kwargs!r}'
 
+    if validated:
+        foo = validate_arguments(foo)
+
     assert foo(1, 2) == 'a=1, b=2, args=(), d=3, kwargs={}'
     assert foo(1, 2, 3, d=4) == 'a=1, b=2, args=(3,), d=4, kwargs={}'
     assert foo(*[1, 2, 3], d=4) == 'a=1, b=2, args=(3,), d=4, kwargs={}'
+    assert foo(1, 2, args=(10, 11)) == "a=1, b=2, args=(), d=3, kwargs={'args': (10, 11)}"
+    assert foo(1, 2, 3, args=(10, 11)) == "a=1, b=2, args=(3,), d=3, kwargs={'args': (10, 11)}"
     assert foo(1, 2, 3, e=10) == "a=1, b=2, args=(3,), d=3, kwargs={'e': 10}"
+    assert foo(1, 2, kwargs=4) == "a=1, b=2, args=(), d=3, kwargs={'kwargs': 4}"
+    assert foo(1, 2, kwargs=4, e=5) == "a=1, b=2, args=(), d=3, kwargs={'kwargs': 4, 'e': 5}"
 
 
 @skip_pre_38