Repository: pydantic/pydantic

Issue:
`validate_arguments` does not assign `**kwargs` correctly
### Checks

* [x] I added a descriptive title to this issue
* [x] I have searched (google, github) for similar issues and couldn't find anything
* [x] I have read and followed [the docs](https://pydantic-docs.helpmanual.io/) and still think this is a bug

<!-- Sorry to sound so draconian, but every second saved replying to issues is time spend improving pydantic :-) -->

# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.7.3
            pydantic compiled: False
                 install path: /home/anthony/experiments/pydantic/pydantic
               python version: 3.8.5 (default, Aug 23 2020, 18:40:40)  [GCC 10.1.0]
                     platform: Linux-5.10.2-2-MANJARO-x86_64-with-glibc2.29
     optional deps. installed: ['devtools', 'dotenv', 'email-validator', 'typing-extensions']
```
<!-- or if you're using pydantic prior to v1.3, manually include: OS, python version and pydantic version -->

<!-- Please read the [docs](https://pydantic-docs.helpmanual.io/) and search through issues to
confirm your bug hasn't already been reported. -->

<!-- Where possible please include a self-contained code snippet describing your bug: -->

```py
from pydantic import validate_arguments

@validate_arguments
def foo(**kwargs):
    print(f"{kwargs=}")


def bar(**kwargs):
    print(f"{kwargs=}")

>>> foo(kwargs=1, b=2)
kwargs={'b': 2}
>>> bar(kwargs=1, b=2)
kwargs={'kwargs': 1, 'b': 2}
```

Kwargs doesn't get assigned as per native python implementation.

It looks like the field gets overwritten, as we're checking all input kwargs against all fields, including the special var_kwarg field.
https://github.com/samuelcolvin/pydantic/blob/8bad7bc91105616dfcc23ce1d68fba12f328a2a3/pydantic/decorator.py#L166

Should be as simple as `set(self.model.__fields__) - {self.v_kwargs_name}` to resolve, will prepare a patch now.



Comments:

Patch:
diff --git a/pydantic/decorator.py b/pydantic/decorator.py
--- a/pydantic/decorator.py
+++ b/pydantic/decorator.py
@@ -161,8 +161,9 @@ def build_values(self, args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> Dict[st
 
         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 @@ def build_values(self, args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> Dict[st
 
     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 @@ def execute(self, m: BaseModel) -> Any:
                     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 @@ def execute(self, m: BaseModel) -> Any:
                     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
--- a/tests/test_decorator.py
+++ b/tests/test_decorator.py
@@ -110,15 +110,22 @@ def foo(a, b, c='x', *, d='y'):
     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