Repository: samuelcolvin/pydantic
Pull request #2352 (2021-02-12 23:14:46)

fix ci on i686 by ensuring order of created list
<!-- 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
The error was so dumb... 🤦 I had a `set` in my example and order of `set` is not guaranteed.
Tested locally in a `quay.io/pypa/manylinux2014_i686` container

### BEFORE
<img width="830" alt="Screen Shot 2021-02-13 at 12 10 27 AM" src="https://user-images.githubusercontent.com/18406791/107832321-67418f00-6d90-11eb-8d05-707f056611fb.png">

### AFTER
<img width="833" alt="Screen Shot 2021-02-13 at 12 11 00 AM" src="https://user-images.githubusercontent.com/18406791/107832324-6ad51600-6d90-11eb-842a-055239df6cfc.png">


<!-- Please give a short summary of the changes. -->

## Related issue number

<!-- Are there any issues opened that will be resolved by merging this change? -->

## Checklist

* [ ] Unit tests for the changes exist
* [ ] Tests pass on CI and coverage remains at 100%
* [ ] Documentation reflects the changes where applicable
* [ ] `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/2352?src=pr&el=h1) Report
> Merging [#2352](https://codecov.io/gh/samuelcolvin/pydantic/pull/2352?src=pr&el=desc) (1a1c606) into [master](https://codecov.io/gh/samuelcolvin/pydantic/commit/502570706a09f9bae1c04486f5dd9e5d8a765c37?el=desc) (5025707) will **decrease** coverage by `0.71%`.
> The diff coverage is `n/a`.


```diff
@@             Coverage Diff             @@
##            master    #2352      +/-   ##
===========================================
- Coverage   100.00%   99.28%   -0.72%     
===========================================
  Files           22       22              
  Lines         4357     4357              
  Branches       875      875              
===========================================
- Hits          4357     4326      -31     
- Misses           0       23      +23     
- Partials         0        8       +8     
```


| [Impacted Files](https://codecov.io/gh/samuelcolvin/pydantic/pull/2352?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pydantic/typing.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2352/diff?src=pr&el=tree#diff-cHlkYW50aWMvdHlwaW5nLnB5) | `85.10% <0.00%> (-14.90%)` | :arrow_down: |
| [pydantic/version.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2352/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/2352/diff?src=pr&el=tree#diff-cHlkYW50aWMvZW52X3NldHRpbmdzLnB5) | `98.40% <0.00%> (-1.60%)` | :arrow_down: |
| [pydantic/networks.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2352/diff?src=pr&el=tree#diff-cHlkYW50aWMvbmV0d29ya3MucHk=) | `98.52% <0.00%> (-1.48%)` | :arrow_down: |
| [pydantic/utils.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2352/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/2352?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/2352?src=pr&el=footer). Last update [5025707...1a1c606](https://codecov.io/gh/samuelcolvin/pydantic/pull/2352?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/changes/2175-davidolrik.md b/changes/2175-davidolrik.md
new file mode 100644
index 000000000..eff5995e1
--- /dev/null
+++ b/changes/2175-davidolrik.md
@@ -0,0 +1,1 @@
+Allow non-existent secrets directory by only issuing a warning
diff --git a/changes/2216-PrettyWood.md b/changes/2216-PrettyWood.md
new file mode 100644
index 000000000..bcf9e6d5a
--- /dev/null
+++ b/changes/2216-PrettyWood.md
@@ -0,0 +1,3 @@
+Add support for `NamedTuple` and `TypedDict` types.
+Those two types are now handled and validated when used inside `BaseModel` or _pydantic_ `dataclass`.
+Two utils are also added `create_model_from_namedtuple` and `create_model_from_typeddict`.
\ No newline at end of file
diff --git a/changes/2220-PrettyWood.md b/changes/2220-PrettyWood.md
new file mode 100644
index 000000000..259597448
--- /dev/null
+++ b/changes/2220-PrettyWood.md
@@ -0,0 +1,1 @@
+Resolve forward refs for stdlib dataclasses converted into _pydantic_ ones
diff --git a/changes/2237-PrettyWood.md b/changes/2237-PrettyWood.md
new file mode 100644
index 000000000..14e811b55
--- /dev/null
+++ b/changes/2237-PrettyWood.md
@@ -0,0 +1,1 @@
+Support custom root type (aka `__root__`) with `from_orm()`
diff --git a/changes/2238-PrettyWood.md b/changes/2238-PrettyWood.md
new file mode 100644
index 000000000..4a0f9239b
--- /dev/null
+++ b/changes/2238-PrettyWood.md
@@ -0,0 +1,1 @@
+Support custom root type (aka `__root__`) when using `parse_obj()` with nested models
\ No newline at end of file
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/2249-cybojenix.md b/changes/2249-cybojenix.md
new file mode 100644
index 000000000..ad7021f59
--- /dev/null
+++ b/changes/2249-cybojenix.md
@@ -0,0 +1,2 @@
+Prevent overriding positional arguments with keyword arguments in
+`validate_arguments`, as per behaviour with native 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/changes/2281-PrettyWood.md b/changes/2281-PrettyWood.md
new file mode 100644
index 000000000..b554eb5c8
--- /dev/null
+++ b/changes/2281-PrettyWood.md
@@ -0,0 +1,1 @@
+fix: keep order of fields with `BaseModel.construct()`
diff --git a/changes/2290-PrettyWood.md b/changes/2290-PrettyWood.md
new file mode 100644
index 000000000..13df89087
--- /dev/null
+++ b/changes/2290-PrettyWood.md
@@ -0,0 +1,1 @@
+fix: update `__fields_set__` in `BaseModel.copy(update=…)`
diff --git a/changes/2318-PrettyWood.md b/changes/2318-PrettyWood.md
new file mode 100644
index 000000000..6ffb3cfb1
--- /dev/null
+++ b/changes/2318-PrettyWood.md
@@ -0,0 +1,1 @@
+Support empty tuple type
diff --git a/docs/examples/annotated_types_named_tuple.py b/docs/examples/annotated_types_named_tuple.py
new file mode 100644
index 000000000..6fd36cdaa
--- /dev/null
+++ b/docs/examples/annotated_types_named_tuple.py
@@ -0,0 +1,20 @@
+from typing import NamedTuple
+
+from pydantic import BaseModel, ValidationError
+
+
+class Point(NamedTuple):
+    x: int
+    y: int
+
+
+class Model(BaseModel):
+    p: Point
+
+
+print(Model(p=('1', '2')))
+
+try:
+    Model(p=('1.3', '2'))
+except ValidationError as e:
+    print(e)
diff --git a/docs/examples/annotated_types_typed_dict.py b/docs/examples/annotated_types_typed_dict.py
new file mode 100644
index 000000000..382ba65f3
--- /dev/null
+++ b/docs/examples/annotated_types_typed_dict.py
@@ -0,0 +1,45 @@
+from typing import TypedDict
+
+from pydantic import BaseModel, Extra, ValidationError
+
+
+# `total=False` means keys are non-required
+class UserIdentity(TypedDict, total=False):
+    name: str
+    surname: str
+
+
+class User(TypedDict):
+    identity: UserIdentity
+    age: int
+
+
+class Model(BaseModel):
+    u: User
+
+    class Config:
+        extra = Extra.forbid
+
+
+print(Model(u={'identity': {'name': 'Smith', 'surname': 'John'}, 'age': '37'}))
+
+print(Model(u={'identity': {'name': None, 'surname': 'John'}, 'age': '37'}))
+
+print(Model(u={'identity': {}, 'age': '37'}))
+
+
+try:
+    Model(u={'identity': {'name': ['Smith'], 'surname': 'John'}, 'age': '24'})
+except ValidationError as e:
+    print(e)
+
+try:
+    Model(
+        u={
+            'identity': {'name': 'Smith', 'surname': 'John'},
+            'age': '37',
+            'email': 'john.smith@me.com',
+        }
+    )
+except ValidationError as e:
+    print(e)
diff --git a/docs/examples/models_from_typeddict.py b/docs/examples/models_from_typeddict.py
new file mode 100644
index 000000000..54136274e
--- /dev/null
+++ b/docs/examples/models_from_typeddict.py
@@ -0,0 +1,21 @@
+from typing import TypedDict
+
+from pydantic import ValidationError, create_model_from_typeddict
+
+
+class User(TypedDict):
+    name: str
+    id: int
+
+
+class Config:
+    extra = 'forbid'
+
+
+UserM = create_model_from_typeddict(User, __config__=Config)
+print(repr(UserM(name=123, id='3')))
+
+try:
+    UserM(name=123, id='3', other='no')
+except ValidationError as e:
+    print(e)
diff --git a/docs/examples/validation_decorator_parameter_types.py b/docs/examples/validation_decorator_parameter_types.py
index 0f80133b5..9d0b59148 100644
--- a/docs/examples/validation_decorator_parameter_types.py
+++ b/docs/examples/validation_decorator_parameter_types.py
@@ -65,4 +65,4 @@ def armageddon(
 
 
 print(armageddon(1, 2, e=3))
-print(armageddon(1, 2, 3, 4, 5, 6, c=7, e=8, f=9, g=10, spam=11))
+print(armageddon(1, 2, 3, 4, 5, 6, e=8, f=9, g=10, spam=11))
diff --git a/docs/usage/models.md b/docs/usage/models.md
index 87312469d..062808f6f 100644
--- a/docs/usage/models.md
+++ b/docs/usage/models.md
@@ -384,6 +384,18 @@ You can also add validators by passing a dict to the `__validators__` argument.
 {!.tmp_examples/models_dynamic_validators.py!}
 ```
 
+## Model creation from `NamedTuple` or `TypedDict`
+
+Sometimes you already use in your application classes that inherit from `NamedTuple` or `TypedDict`
+and you don't want to duplicate all your information to have a `BaseModel`.
+For this _pydantic_ provides `create_model_from_namedtuple` and `create_model_from_typeddict` methods.
+Those methods have the exact same keyword arguments as `create_model`.
+
+
+```py
+{!.tmp_examples/models_from_typeddict.py!}
+```
+
 ## Custom Root Types
 
 Pydantic models can be defined with a custom root type by declaring the `__root__` field. 
diff --git a/docs/usage/settings.md b/docs/usage/settings.md
index b14a1baf9..654bc2cdd 100644
--- a/docs/usage/settings.md
+++ b/docs/usage/settings.md
@@ -160,8 +160,8 @@ settings = Settings(_secrets_dir='/var/run')
 ```
 
 In either case, the value of the passed argument can be any valid directory, either absolute or relative to the
-current working directory. From there, *pydantic* will handle everything for you by loading in your variables and
-validating them.
+current working directory. **Note that a non existent directory will only generate a warning**.
+From there, *pydantic* will handle everything for you by loading in your variables and validating them.
 
 Even when using a secrets directory, *pydantic* will still read environment variables from a dotenv file or the environment,
 **a dotenv file and environment variables will always take priority over values loaded from the secrets directory**.
diff --git a/docs/usage/types.md b/docs/usage/types.md
index 808ad4bd9..39aeb92a8 100644
--- a/docs/usage/types.md
+++ b/docs/usage/types.md
@@ -88,9 +88,20 @@ with custom properties and validation.
 `typing.Tuple`
 : see [Typing Iterables](#typing-iterables) below for more detail on parsing and validation
 
+`subclass of typing.NamedTuple`
+: Same as `tuple` but instantiates with the given namedtuple and validates fields since they are annotated.
+  See [Annotated Types](#annotated-types) below for more detail on parsing and validation
+
+`subclass of collections.namedtuple`
+: Same as `subclass of typing.NamedTuple` but all fields will have type `Any` since they are not annotated
+
 `typing.Dict`
 : see [Typing Iterables](#typing-iterables) below for more detail on parsing and validation
 
+`subclass of typing.TypedDict`
+: Same as `dict` but _pydantic_ will validate the dictionary since keys are annotated.  
+  See [Annotated Types](#annotated-types) below for more detail on parsing and validation
+
 `typing.Set`
 : see [Typing Iterables](#typing-iterables) below for more detail on parsing and validation
 
@@ -395,6 +406,29 @@ With proper ordering in an annotated `Union`, you can use this to parse types of
 ```
 _(This script is complete, it should run "as is")_
 
+## Annotated Types
+
+### NamedTuple
+
+```py
+{!.tmp_examples/annotated_types_named_tuple.py!}
+```
+_(This script is complete, it should run "as is")_
+
+### TypedDict
+
+!!! note
+    This is a new feature of the python standard library as of python 3.8.
+    Prior to python 3.8, it requires the [typing-extensions](https://pypi.org/project/typing-extensions/) package.
+    But required and optional fields are properly differentiated only since python 3.9.
+    We therefore recommend using [typing-extensions](https://pypi.org/project/typing-extensions/) with python 3.8 as well.
+
+
+```py
+{!.tmp_examples/annotated_types_typed_dict.py!}
+```
+_(This script is complete, it should run "as is")_
+
 ## Pydantic Types
 
 *pydantic* also provides a variety of other useful types:
@@ -722,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
 
@@ -741,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/__init__.py b/pydantic/__init__.py
index 8f67da54b..4fdba0824 100644
--- a/pydantic/__init__.py
+++ b/pydantic/__init__.py
@@ -1,5 +1,6 @@
 # flake8: noqa
 from . import dataclasses
+from .annotated_types import create_model_from_namedtuple, create_model_from_typeddict
 from .class_validators import root_validator, validator
 from .decorator import validate_arguments
 from .env_settings import BaseSettings
@@ -16,6 +17,9 @@ from .version import VERSION
 # WARNING __all__ from .errors is not included here, it will be removed as an export here in v2
 # please use "from pydantic.errors import ..." instead
 __all__ = [
+    # annotated types utils
+    'create_model_from_namedtuple',
+    'create_model_from_typeddict',
     # dataclasses
     'dataclasses',
     # class_validators
diff --git a/pydantic/annotated_types.py b/pydantic/annotated_types.py
new file mode 100644
index 000000000..a29da28c0
--- /dev/null
+++ b/pydantic/annotated_types.py
@@ -0,0 +1,59 @@
+from typing import TYPE_CHECKING, Any, Dict, FrozenSet, NamedTuple, Type
+
+from .fields import Required
+from .main import BaseModel, create_model
+
+if TYPE_CHECKING:
+
+    class TypedDict(Dict[str, Any]):
+        __annotations__: Dict[str, Type[Any]]
+        __total__: bool
+        __required_keys__: FrozenSet[str]
+        __optional_keys__: FrozenSet[str]
+
+
+def create_model_from_typeddict(typeddict_cls: Type['TypedDict'], **kwargs: Any) -> Type['BaseModel']:
+    """
+    Create a `BaseModel` based on the fields of a `TypedDict`.
+    Since `typing.TypedDict` in Python 3.8 does not store runtime information about optional keys,
+    we warn the user if that's the case (see https://bugs.python.org/issue38834).
+    """
+    field_definitions: Dict[str, Any]
+
+    # Best case scenario: with python 3.9+ or when `TypedDict` is imported from `typing_extensions`
+    if hasattr(typeddict_cls, '__required_keys__'):
+        field_definitions = {
+            field_name: (field_type, Required if field_name in typeddict_cls.__required_keys__ else None)
+            for field_name, field_type in typeddict_cls.__annotations__.items()
+        }
+    else:
+        import warnings
+
+        warnings.warn(
+            'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` for better support! '
+            'Without it, there is no way to differentiate required and optional fields when subclassed. '
+            'Fields will therefore be considered all required or all optional depending on class totality.',
+            UserWarning,
+        )
+        default_value = Required if typeddict_cls.__total__ else None
+        field_definitions = {
+            field_name: (field_type, default_value) for field_name, field_type in typeddict_cls.__annotations__.items()
+        }
+
+    return create_model(typeddict_cls.__name__, **kwargs, **field_definitions)
+
+
+def create_model_from_namedtuple(namedtuple_cls: Type['NamedTuple'], **kwargs: Any) -> Type['BaseModel']:
+    """
+    Create a `BaseModel` based on the fields of a named tuple.
+    A named tuple can be created with `typing.NamedTuple` and declared annotations
+    but also with `collections.namedtuple`, in this case we consider all fields
+    to have type `Any`.
+    """
+    namedtuple_annotations: Dict[str, Type[Any]] = getattr(
+        namedtuple_cls, '__annotations__', {k: Any for k in namedtuple_cls._fields}
+    )
+    field_definitions: Dict[str, Any] = {
+        field_name: (field_type, Required) for field_name, field_type in namedtuple_annotations.items()
+    }
+    return create_model(namedtuple_cls.__name__, **kwargs, **field_definitions)
diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py
index 53f1427d8..485437d8a 100644
--- a/pydantic/dataclasses.py
+++ b/pydantic/dataclasses.py
@@ -5,6 +5,7 @@ from .error_wrappers import ValidationError
 from .errors import DataclassTypeError
 from .fields import Required
 from .main import create_model, validate_model
+from .typing import resolve_annotations
 from .utils import ClassAttribute
 
 if TYPE_CHECKING:
@@ -128,7 +129,7 @@ def _process_class(
             _cls.__name__,
             (_cls,),
             {
-                '__annotations__': _cls.__annotations__,
+                '__annotations__': resolve_annotations(_cls.__annotations__, _cls.__module__),
                 '__post_init__': _pydantic_post_init,
                 # attrs for pickle to find this class
                 '__module__': __name__,
diff --git a/pydantic/decorator.py b/pydantic/decorator.py
index 98bc4e21b..383fa2fef 100644
--- a/pydantic/decorator.py
+++ b/pydantic/decorator.py
@@ -66,6 +66,7 @@ def validate_arguments(func: Optional['AnyCallableT'] = None, *, config: 'Config
 ALT_V_ARGS = 'v__args'
 ALT_V_KWARGS = 'v__kwargs'
 V_POSITIONAL_ONLY_NAME = 'v__positional_only'
+V_DUPLICATE_KWARGS = 'v__duplicate_kwargs'
 
 
 class ValidatedFunction:
@@ -74,10 +75,10 @@ class ValidatedFunction:
 
         parameters: Mapping[str, Parameter] = signature(function).parameters
 
-        if parameters.keys() & {ALT_V_ARGS, ALT_V_KWARGS, V_POSITIONAL_ONLY_NAME}:
+        if parameters.keys() & {ALT_V_ARGS, ALT_V_KWARGS, V_POSITIONAL_ONLY_NAME, V_DUPLICATE_KWARGS}:
             raise ConfigError(
-                f'"{ALT_V_ARGS}", "{ALT_V_KWARGS}" and "{V_POSITIONAL_ONLY_NAME}" are not permitted as argument '
-                f'names when using the "{validate_arguments.__name__}" decorator'
+                f'"{ALT_V_ARGS}", "{ALT_V_KWARGS}", "{V_POSITIONAL_ONLY_NAME}" and "{V_DUPLICATE_KWARGS}" '
+                f'are not permitted as argument names when using the "{validate_arguments.__name__}" decorator'
             )
 
         self.raw_function = function
@@ -105,6 +106,7 @@ class ValidatedFunction:
             elif p.kind == Parameter.POSITIONAL_OR_KEYWORD:
                 self.arg_mapping[i] = name
                 fields[name] = annotation, default
+                fields[V_DUPLICATE_KWARGS] = List[str], None
             elif p.kind == Parameter.KEYWORD_ONLY:
                 fields[name] = annotation, default
             elif p.kind == Parameter.VAR_POSITIONAL:
@@ -161,10 +163,14 @@ class ValidatedFunction:
 
         var_kwargs = {}
         wrong_positional_args = []
+        duplicate_kwargs = []
+        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)
+                if k in values:
+                    duplicate_kwargs.append(k)
                 values[k] = v
             else:
                 var_kwargs[k] = v
@@ -173,13 +179,13 @@ class ValidatedFunction:
             values[self.v_kwargs_name] = var_kwargs
         if wrong_positional_args:
             values[V_POSITIONAL_ONLY_NAME] = wrong_positional_args
+        if duplicate_kwargs:
+            values[V_DUPLICATE_KWARGS] = duplicate_kwargs
         return values
 
     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 +199,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 +208,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)
@@ -247,6 +253,12 @@ class ValidatedFunction:
                 keys = ', '.join(map(repr, v))
                 raise TypeError(f'positional-only argument{plural} passed as keyword argument{plural}: {keys}')
 
+            @validator(V_DUPLICATE_KWARGS, check_fields=False, allow_reuse=True)
+            def check_duplicate_kwargs(cls, v: List[str]) -> None:
+                plural = '' if len(v) == 1 else 's'
+                keys = ', '.join(map(repr, v))
+                raise TypeError(f'multiple values for argument{plural}: {keys}')
+
             class Config(CustomConfig):
                 extra = Extra.forbid
 
diff --git a/pydantic/env_settings.py b/pydantic/env_settings.py
index ff8ab140a..71b5a976c 100644
--- a/pydantic/env_settings.py
+++ b/pydantic/env_settings.py
@@ -197,7 +197,9 @@ class SecretsSettingsSource:
         secrets_path = Path(self.secrets_dir).expanduser()
 
         if not secrets_path.exists():
-            raise SettingsError(f'directory "{secrets_path}" does not exist')
+            warnings.warn(f'directory "{secrets_path}" does not exist')
+            return secrets
+
         if not secrets_path.is_dir():
             raise SettingsError(f'secrets_dir must reference a directory, not a {path_type(secrets_path)}')
 
diff --git a/pydantic/fields.py b/pydantic/fields.py
index 84143a477..578f2a882 100644
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -38,6 +38,7 @@ from .typing import (
     get_origin,
     is_literal_type,
     is_new_type,
+    is_typeddict,
     new_type_supertype,
 )
 from .utils import PyObjectStr, Representation, lenient_issubclass, sequence_like, smart_deepcopy
@@ -416,6 +417,8 @@ class ModelField(Representation):
             return
         elif is_literal_type(self.type_):
             return
+        elif is_typeddict(self.type_):
+            return
 
         origin = get_origin(self.type_)
         if origin is None:
@@ -449,18 +452,20 @@ class ModelField(Representation):
 
         if issubclass(origin, Tuple):  # type: ignore
             # origin == Tuple without item type
-            if not get_args(self.type_):
+            args = get_args(self.type_)
+            if not args:  # plain tuple
                 self.type_ = Any
                 self.shape = SHAPE_TUPLE_ELLIPSIS
-            else:
+            elif len(args) == 2 and args[1] is Ellipsis:  # e.g. Tuple[int, ...]
+                self.type_ = args[0]
+                self.shape = SHAPE_TUPLE_ELLIPSIS
+            elif args == ((),):  # Tuple[()] means empty tuple
                 self.shape = SHAPE_TUPLE
+                self.type_ = Any
                 self.sub_fields = []
-                for i, t in enumerate(get_args(self.type_)):
-                    if t is Ellipsis:
-                        self.type_ = get_args(self.type_)[0]
-                        self.shape = SHAPE_TUPLE_ELLIPSIS
-                        return
-                    self.sub_fields.append(self._create_sub_type(t, f'{self.name}_{i}'))
+            else:
+                self.shape = SHAPE_TUPLE
+                self.sub_fields = [self._create_sub_type(t, f'{self.name}_{i}') for i, t in enumerate(args)]
             return
 
         if issubclass(origin, List):
diff --git a/pydantic/main.py b/pydantic/main.py
index 250bce55c..780417654 100644
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -34,7 +34,15 @@ from .json import custom_pydantic_encoder, pydantic_encoder
 from .parse import Protocol, load_file, load_str_bytes
 from .schema import default_ref_template, model_schema
 from .types import PyObject, StrBytes
-from .typing import AnyCallable, get_args, get_origin, is_classvar, resolve_annotations, update_field_forward_refs
+from .typing import (
+    AnyCallable,
+    get_args,
+    get_origin,
+    is_classvar,
+    is_namedtuple,
+    resolve_annotations,
+    update_field_forward_refs,
+)
 from .utils import (
     ROOT_KEY,
     ClassAttribute,
@@ -199,7 +207,7 @@ def prepare_config(config: Type[BaseConfig], cls_name: str) -> None:
 
 def validate_custom_root_type(fields: Dict[str, ModelField]) -> None:
     if len(fields) > 1:
-        raise ValueError('__root__ cannot be mixed with other fields')
+        raise ValueError(f'{ROOT_KEY} cannot be mixed with other fields')
 
 
 # Annotated fields can have many types like `str`, `int`, `List[str]`, `Callable`...
@@ -522,12 +530,18 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
         return self.__config__.json_dumps(data, default=encoder, **dumps_kwargs)
 
     @classmethod
-    def parse_obj(cls: Type['Model'], obj: Any) -> 'Model':
+    def _enforce_dict_if_root(cls, obj: Any) -> Any:
         if cls.__custom_root_type__ and (
             not (isinstance(obj, dict) and obj.keys() == {ROOT_KEY}) or cls.__fields__[ROOT_KEY].shape == SHAPE_MAPPING
         ):
-            obj = {ROOT_KEY: obj}
-        elif not isinstance(obj, dict):
+            return {ROOT_KEY: obj}
+        else:
+            return obj
+
+    @classmethod
+    def parse_obj(cls: Type['Model'], obj: Any) -> 'Model':
+        obj = cls._enforce_dict_if_root(obj)
+        if not isinstance(obj, dict):
             try:
                 obj = dict(obj)
             except (TypeError, ValueError) as e:
@@ -582,7 +596,7 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
     def from_orm(cls: Type['Model'], obj: Any) -> 'Model':
         if not cls.__config__.orm_mode:
             raise ConfigError('You must have the config attribute orm_mode=True to use from_orm')
-        obj = cls._decompose_class(obj)
+        obj = {ROOT_KEY: obj} if cls.__custom_root_type__ else cls._decompose_class(obj)
         m = cls.__new__(cls)
         values, fields_set, validation_error = validate_model(cls, obj)
         if validation_error:
@@ -597,10 +611,15 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
         """
         Creates a new model setting __dict__ and __fields_set__ from trusted or pre-validated data.
         Default values are respected, but no other validation is performed.
+        Behaves as if `Config.extra = 'allow'` was set since it adds all passed values
         """
         m = cls.__new__(cls)
-        # default field values
-        fields_values = {name: field.get_default() for name, field in cls.__fields__.items() if not field.required}
+        fields_values: Dict[str, Any] = {}
+        for name, field in cls.__fields__.items():
+            if name in values:
+                fields_values[name] = values[name]
+            elif not field.required:
+                fields_values[name] = field.get_default()
         fields_values.update(values)
         object_setattr(m, '__dict__', fields_values)
         if _fields_set is None:
@@ -640,7 +659,12 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
         cls = self.__class__
         m = cls.__new__(cls)
         object_setattr(m, '__dict__', v)
-        object_setattr(m, '__fields_set__', self.__fields_set__.copy())
+        # new `__fields_set__` can have unset optional fields with a set value in `update` kwarg
+        if update:
+            fields_set = self.__fields_set__ | update.keys()
+        else:
+            fields_set = set(self.__fields_set__)
+        object_setattr(m, '__fields_set__', fields_set)
         for name in self.__private_attributes__:
             value = getattr(self, name, Undefined)
             if value is not Undefined:
@@ -675,14 +699,13 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
 
     @classmethod
     def validate(cls: Type['Model'], value: Any) -> 'Model':
+        value = cls._enforce_dict_if_root(value)
         if isinstance(value, dict):
             return cls(**value)
         elif isinstance(value, cls):
             return value.copy() if cls.__config__.copy_on_model_validation else value
         elif cls.__config__.orm_mode:
             return cls.from_orm(value)
-        elif cls.__custom_root_type__:
-            return cls.parse_obj(value)
         else:
             try:
                 value_as_dict = dict(value)
@@ -718,8 +741,8 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
                     exclude=exclude,
                     exclude_none=exclude_none,
                 )
-                if '__root__' in v_dict:
-                    return v_dict['__root__']
+                if ROOT_KEY in v_dict:
+                    return v_dict[ROOT_KEY]
                 return v_dict
             else:
                 return v.copy(include=include, exclude=exclude)
@@ -745,7 +768,7 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
             }
 
         elif sequence_like(v):
-            return v.__class__(
+            seq_args = (
                 cls._get_value(
                     v_,
                     to_dict=to_dict,
@@ -761,6 +784,8 @@ class BaseModel(Representation, metaclass=ModelMetaclass):
                 and (not value_include or value_include.is_included(i))
             )
 
+            return v.__class__(*seq_args) if is_namedtuple(v.__class__) else v.__class__(seq_args)
+
         elif isinstance(v, Enum) and getattr(cls.Config, 'use_enum_values', False):
             return v.value
 
diff --git a/pydantic/schema.py b/pydantic/schema.py
index 618a426bb..6d80ea302 100644
--- a/pydantic/schema.py
+++ b/pydantic/schema.py
@@ -65,6 +65,7 @@ from .typing import (
     get_origin,
     is_callable_type,
     is_literal_type,
+    is_namedtuple,
     literal_values,
 )
 from .utils import ROOT_KEY, get_model, lenient_issubclass, sequence_like
@@ -800,7 +801,17 @@ def field_singleton_schema(  # noqa: C901 (ignore complexity)
         f_schema, schema_overrides = get_field_info_schema(field)
         f_schema.update(get_schema_ref(enum_name, ref_prefix, ref_template, schema_overrides))
         definitions[enum_name] = enum_process_schema(field_type)
-    else:
+    elif is_namedtuple(field_type):
+        sub_schema, *_ = model_process_schema(
+            field_type.__pydantic_model__,
+            by_alias=by_alias,
+            model_name_map=model_name_map,
+            ref_prefix=ref_prefix,
+            ref_template=ref_template,
+            known_models=known_models,
+        )
+        f_schema.update({'type': 'array', 'items': list(sub_schema['properties'].values())})
+    elif not hasattr(field_type, '__pydantic_model__'):
         add_field_type_to_schema(field_type, f_schema)
 
         modify_schema = getattr(field_type, '__modify_schema__', None)
diff --git a/pydantic/typing.py b/pydantic/typing.py
index e04470fe6..91cdf4e6b 100644
--- a/pydantic/typing.py
+++ b/pydantic/typing.py
@@ -188,6 +188,8 @@ __all__ = (
     'is_literal_type',
     'literal_values',
     'Literal',
+    'is_namedtuple',
+    'is_typeddict',
     'is_new_type',
     'new_type_supertype',
     'is_classvar',
@@ -299,6 +301,26 @@ def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]:
     return tuple(x for value in values for x in all_literal_values(value))
 
 
+def is_namedtuple(type_: Type[Any]) -> bool:
+    """
+    Check if a given class is a named tuple.
+    It can be either a `typing.NamedTuple` or `collections.namedtuple`
+    """
+    from .utils import lenient_issubclass
+
+    return lenient_issubclass(type_, tuple) and hasattr(type_, '_fields')
+
+
+def is_typeddict(type_: Type[Any]) -> bool:
+    """
+    Check if a given class is a typed dict (from `typing` or `typing_extensions`)
+    In 3.10, there will be a public method (https://docs.python.org/3.10/library/typing.html#typing.is_typeddict)
+    """
+    from .utils import lenient_issubclass
+
+    return lenient_issubclass(type_, dict) and hasattr(type_, '__total__')
+
+
 test_type = NewType('test_type', str)
 
 
diff --git a/pydantic/validators.py b/pydantic/validators.py
index d12eadc1c..f4e21bbb2 100644
--- a/pydantic/validators.py
+++ b/pydantic/validators.py
@@ -15,6 +15,7 @@ from typing import (
     FrozenSet,
     Generator,
     List,
+    NamedTuple,
     Pattern,
     Set,
     Tuple,
@@ -36,10 +37,13 @@ from .typing import (
     get_class,
     is_callable_type,
     is_literal_type,
+    is_namedtuple,
+    is_typeddict,
 )
 from .utils import almost_equal_floats, lenient_issubclass, sequence_like
 
 if TYPE_CHECKING:
+    from .annotated_types import TypedDict
     from .fields import ModelField
     from .main import BaseConfig
     from .types import ConstrainedDecimal, ConstrainedFloat, ConstrainedInt
@@ -547,6 +551,42 @@ def pattern_validator(v: Any) -> Pattern[str]:
         raise errors.PatternError()
 
 
+NamedTupleT = TypeVar('NamedTupleT', bound=NamedTuple)
+
+
+def make_namedtuple_validator(namedtuple_cls: Type[NamedTupleT]) -> Callable[[Tuple[Any, ...]], NamedTupleT]:
+    from .annotated_types import create_model_from_namedtuple
+
+    NamedTupleModel = create_model_from_namedtuple(namedtuple_cls)
+    namedtuple_cls.__pydantic_model__ = NamedTupleModel  # type: ignore[attr-defined]
+
+    def namedtuple_validator(values: Tuple[Any, ...]) -> NamedTupleT:
+        annotations = NamedTupleModel.__annotations__
+
+        if len(values) > len(annotations):
+            raise errors.ListMaxLengthError(limit_value=len(annotations))
+
+        dict_values: Dict[str, Any] = dict(zip(annotations, values))
+        validated_dict_values: Dict[str, Any] = dict(NamedTupleModel(**dict_values))
+        return namedtuple_cls(**validated_dict_values)
+
+    return namedtuple_validator
+
+
+def make_typeddict_validator(
+    typeddict_cls: Type['TypedDict'], config: Type['BaseConfig']
+) -> Callable[[Any], Dict[str, Any]]:
+    from .annotated_types import create_model_from_typeddict
+
+    TypedDictModel = create_model_from_typeddict(typeddict_cls, __config__=config)
+    typeddict_cls.__pydantic_model__ = TypedDictModel  # type: ignore[attr-defined]
+
+    def typeddict_validator(values: 'TypedDict') -> Dict[str, Any]:
+        return TypedDictModel.parse_obj(values).dict(exclude_unset=True)
+
+    return typeddict_validator
+
+
 class IfConfig:
     def __init__(self, validator: AnyCallable, *config_attr_names: str) -> None:
         self.validator = validator
@@ -639,6 +679,13 @@ def find_validators(  # noqa: C901 (ignore complexity)
     if type_ is IntEnum:
         yield int_enum_validator
         return
+    if is_namedtuple(type_):
+        yield tuple_validator
+        yield make_namedtuple_validator(type_)
+        return
+    if is_typeddict(type_):
+        yield make_typeddict_validator(type_, config)
+        return
 
     class_ = get_class(type_)
     if class_ is not None:
diff --git a/requirements.txt b/requirements.txt
index 065d6b6d7..94aa76af5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,5 +4,5 @@ Cython==0.29.21;sys_platform!='win32'
 devtools==0.6.1
 email-validator==1.1.2
 dataclasses==0.6; python_version < '3.7'
-typing-extensions==3.7.4.1; python_version < '3.8'
+typing-extensions==3.7.4.3; python_version < '3.9'
 python-dotenv==0.15.0


Test patch:
diff --git a/tests/test_annotated_types.py b/tests/test_annotated_types.py
new file mode 100644
index 000000000..8c3fc087b
--- /dev/null
+++ b/tests/test_annotated_types.py
@@ -0,0 +1,278 @@
+"""
+Tests for annotated types that _pydantic_ can validate like
+- NamedTuple
+- TypedDict
+"""
+import json
+import sys
+from collections import namedtuple
+from typing import List, NamedTuple, Tuple
+
+if sys.version_info < (3, 9):
+    try:
+        from typing import TypedDict as LegacyTypedDict
+    except ImportError:
+        LegacyTypedDict = None
+
+    try:
+        from typing_extensions import TypedDict
+    except ImportError:
+        TypedDict = None
+else:
+    from typing import TypedDict
+
+    LegacyTypedDict = None
+
+import pytest
+
+from pydantic import BaseModel, ValidationError
+
+
+def test_namedtuple():
+    Position = namedtuple('Pos', 'x y')
+
+    class Event(NamedTuple):
+        a: int
+        b: int
+        c: int
+        d: str
+
+    class Model(BaseModel):
+        pos: Position
+        events: List[Event]
+
+    model = Model(pos=('1', 2), events=[[b'1', '2', 3, 'qwe']])
+    assert isinstance(model.pos, Position)
+    assert isinstance(model.events[0], Event)
+    assert model.pos.x == '1'
+    assert model.pos == Position('1', 2)
+    assert model.events[0] == Event(1, 2, 3, 'qwe')
+    assert repr(model) == "Model(pos=Pos(x='1', y=2), events=[Event(a=1, b=2, c=3, d='qwe')])"
+    assert model.json() == json.dumps(model.dict()) == '{"pos": ["1", 2], "events": [[1, 2, 3, "qwe"]]}'
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(pos=('1', 2), events=[['qwe', '2', 3, 'qwe']])
+    assert exc_info.value.errors() == [
+        {
+            'loc': ('events', 0, 'a'),
+            'msg': 'value is not a valid integer',
+            'type': 'type_error.integer',
+        }
+    ]
+
+
+def test_namedtuple_schema():
+    class Position1(NamedTuple):
+        x: int
+        y: int
+
+    Position2 = namedtuple('Position2', 'x y')
+
+    class Model(BaseModel):
+        pos1: Position1
+        pos2: Position2
+        pos3: Tuple[int, int]
+
+    assert Model.schema() == {
+        'title': 'Model',
+        'type': 'object',
+        'properties': {
+            'pos1': {
+                'title': 'Pos1',
+                'type': 'array',
+                'items': [
+                    {'title': 'X', 'type': 'integer'},
+                    {'title': 'Y', 'type': 'integer'},
+                ],
+            },
+            'pos2': {
+                'title': 'Pos2',
+                'type': 'array',
+                'items': [
+                    {'title': 'X'},
+                    {'title': 'Y'},
+                ],
+            },
+            'pos3': {
+                'title': 'Pos3',
+                'type': 'array',
+                'items': [
+                    {'type': 'integer'},
+                    {'type': 'integer'},
+                ],
+            },
+        },
+        'required': ['pos1', 'pos2', 'pos3'],
+    }
+
+
+def test_namedtuple_right_length():
+    class Point(NamedTuple):
+        x: int
+        y: int
+
+    class Model(BaseModel):
+        p: Point
+
+    assert isinstance(Model(p=(1, 2)), Model)
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(p=(1, 2, 3))
+    assert exc_info.value.errors() == [
+        {
+            'loc': ('p',),
+            'msg': 'ensure this value has at most 2 items',
+            'type': 'value_error.list.max_items',
+            'ctx': {'limit_value': 2},
+        }
+    ]
+
+
+@pytest.mark.skipif(not TypedDict, reason='typing_extensions not installed')
+def test_typeddict():
+    class TD(TypedDict):
+        a: int
+        b: int
+        c: int
+        d: str
+
+    class Model(BaseModel):
+        td: TD
+
+    m = Model(td={'a': '3', 'b': b'1', 'c': 4, 'd': 'qwe'})
+    assert m.td == {'a': 3, 'b': 1, 'c': 4, 'd': 'qwe'}
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(td={'a': [1], 'b': 2, 'c': 3, 'd': 'qwe'})
+    assert exc_info.value.errors() == [
+        {
+            'loc': ('td', 'a'),
+            'msg': 'value is not a valid integer',
+            'type': 'type_error.integer',
+        }
+    ]
+
+
+@pytest.mark.skipif(not TypedDict, reason='typing_extensions not installed')
+def test_typeddict_non_total():
+    class FullMovie(TypedDict, total=True):
+        name: str
+        year: int
+
+    class Model(BaseModel):
+        movie: FullMovie
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(movie={'year': '2002'})
+    assert exc_info.value.errors() == [
+        {
+            'loc': ('movie', 'name'),
+            'msg': 'field required',
+            'type': 'value_error.missing',
+        }
+    ]
+
+    class PartialMovie(TypedDict, total=False):
+        name: str
+        year: int
+
+    class Model(BaseModel):
+        movie: PartialMovie
+
+    m = Model(movie={'year': '2002'})
+    assert m.movie == {'year': 2002}
+
+
+@pytest.mark.skipif(not TypedDict, reason='typing_extensions not installed')
+def test_partial_new_typeddict():
+    class OptionalUser(TypedDict, total=False):
+        name: str
+
+    class User(OptionalUser):
+        id: int
+
+    class Model(BaseModel):
+        user: User
+
+    m = Model(user={'id': 1})
+    assert m.user == {'id': 1}
+
+
+@pytest.mark.skipif(not LegacyTypedDict, reason='python 3.9+ is used or typing_extensions is installed')
+def test_partial_legacy_typeddict():
+    class OptionalUser(LegacyTypedDict, total=False):
+        name: str
+
+    class User(OptionalUser):
+        id: int
+
+    with pytest.warns(
+        UserWarning,
+        match='You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` for better support!',
+    ):
+
+        class Model(BaseModel):
+            user: User
+
+        with pytest.raises(ValidationError) as exc_info:
+            Model(user={'id': 1})
+        assert exc_info.value.errors() == [
+            {
+                'loc': ('user', 'name'),
+                'msg': 'field required',
+                'type': 'value_error.missing',
+            }
+        ]
+
+
+@pytest.mark.skipif(not TypedDict, reason='typing_extensions not installed')
+def test_typeddict_extra():
+    class User(TypedDict):
+        name: str
+        age: int
+
+    class Model(BaseModel):
+        u: User
+
+        class Config:
+            extra = 'forbid'
+
+    with pytest.raises(ValidationError) as exc_info:
+        Model(u={'name': 'pika', 'age': 7, 'rank': 1})
+    assert exc_info.value.errors() == [
+        {'loc': ('u', 'rank'), 'msg': 'extra fields not permitted', 'type': 'value_error.extra'},
+    ]
+
+
+@pytest.mark.skipif(not TypedDict, reason='typing_extensions not installed')
+def test_typeddict_schema():
+    class Data(BaseModel):
+        a: int
+
+    class DataTD(TypedDict):
+        a: int
+
+    class Model(BaseModel):
+        data: Data
+        data_td: DataTD
+
+    assert Model.schema() == {
+        'title': 'Model',
+        'type': 'object',
+        'properties': {'data': {'$ref': '#/definitions/Data'}, 'data_td': {'$ref': '#/definitions/DataTD'}},
+        'required': ['data', 'data_td'],
+        'definitions': {
+            'Data': {
+                'type': 'object',
+                'title': 'Data',
+                'properties': {'a': {'title': 'A', 'type': 'integer'}},
+                'required': ['a'],
+            },
+            'DataTD': {
+                'type': 'object',
+                'title': 'DataTD',
+                'properties': {'a': {'title': 'A', 'type': 'integer'}},
+                'required': ['a'],
+            },
+        },
+    }
diff --git a/tests/test_construction.py b/tests/test_construction.py
index 00657153c..667eca86e 100644
--- a/tests/test_construction.py
+++ b/tests/test_construction.py
@@ -1,5 +1,5 @@
 import pickle
-from typing import Any, List
+from typing import Any, List, Optional
 
 import pytest
 
@@ -35,6 +35,28 @@ def test_construct_fields_set():
     assert m.dict() == {'a': 3, 'b': -1}
 
 
+def test_construct_allow_extra():
+    """construct() should allow extra fields"""
+
+    class Foo(BaseModel):
+        x: int
+
+    assert Foo.construct(x=1, y=2).dict() == {'x': 1, 'y': 2}
+
+
+def test_construct_keep_order():
+    class Foo(BaseModel):
+        a: int
+        b: int = 42
+        c: float
+
+    instance = Foo(a=1, b=321, c=3.14)
+    instance_construct = Foo.construct(**instance.dict())
+    assert instance == instance_construct
+    assert instance.dict() == instance_construct.dict()
+    assert instance.json() == instance_construct.json()
+
+
 def test_large_any_str():
     class Model(BaseModel):
         a: bytes
@@ -191,6 +213,14 @@ def test_copy_update():
     assert m != m2
 
 
+def test_copy_update_unset():
+    class Foo(BaseModel):
+        foo: Optional[str]
+        bar: Optional[str]
+
+    assert Foo(foo='hello').copy(update={'bar': 'world'}).json(exclude_unset=True) == '{"foo": "hello", "bar": "world"}'
+
+
 def test_copy_set_fields():
     m = ModelTwo(a=24, d=Model(a='12'))
     m2 = m.copy()
diff --git a/tests/test_decorator.py b/tests/test_decorator.py
index ed7efd3ba..83d159c10 100644
--- a/tests/test_decorator.py
+++ b/tests/test_decorator.py
@@ -52,6 +52,20 @@ def test_args():
         {'loc': ('kwargs',), 'msg': "unexpected keyword argument: 'apple'", 'type': 'type_error'}
     ]
 
+    with pytest.raises(ValidationError) as exc_info:
+        foo(1, 2, a=3)
+
+    assert exc_info.value.errors() == [
+        {'loc': ('v__duplicate_kwargs',), 'msg': "multiple values for argument: 'a'", 'type': 'type_error'}
+    ]
+
+    with pytest.raises(ValidationError) as exc_info:
+        foo(1, 2, a=3, b=4)
+
+    assert exc_info.value.errors() == [
+        {'loc': ('v__duplicate_kwargs',), 'msg': "multiple values for arguments: 'a', 'b'", 'type': 'type_error'}
+    ]
+
 
 def test_wrap():
     @validate_arguments
@@ -68,7 +82,7 @@ def test_wrap():
     assert foo_bar.vd.arg_mapping == {0: 'a', 1: 'b'}
     assert foo_bar.vd.positional_only_args == set()
     assert issubclass(foo_bar.model, BaseModel)
-    assert foo_bar.model.__fields__.keys() == {'a', 'b', 'args', 'kwargs'}
+    assert foo_bar.model.__fields__.keys() == {'a', 'b', 'args', 'kwargs', 'v__duplicate_kwargs'}
     assert foo_bar.model.__name__ == 'FooBar'
     assert foo_bar.model.schema()['title'] == 'FooBar'
     # signature is slightly different on 3.6
@@ -110,15 +124,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
@@ -161,7 +182,7 @@ def test_args_name():
     def foo(args: int, kwargs: int):
         return f'args={args!r}, kwargs={kwargs!r}'
 
-    assert foo.model.__fields__.keys() == {'args', 'kwargs', 'v__args', 'v__kwargs'}
+    assert foo.model.__fields__.keys() == {'args', 'kwargs', 'v__args', 'v__kwargs', 'v__duplicate_kwargs'}
     assert foo(1, 2) == 'args=1, kwargs=2'
 
     with pytest.raises(ValidationError) as exc_info:
@@ -184,10 +205,36 @@ def test_args_name():
 
 
 def test_v_args():
-    with pytest.raises(ConfigError, match='"v__args", "v__kwargs" and "v__positional_only" are not permitted'):
+    with pytest.raises(
+        ConfigError, match='"v__args", "v__kwargs", "v__positional_only" and "v__duplicate_kwargs" are not permitted'
+    ):
+
+        @validate_arguments
+        def foo1(v__args: int):
+            pass
+
+    with pytest.raises(
+        ConfigError, match='"v__args", "v__kwargs", "v__positional_only" and "v__duplicate_kwargs" are not permitted'
+    ):
+
+        @validate_arguments
+        def foo2(v__kwargs: int):
+            pass
+
+    with pytest.raises(
+        ConfigError, match='"v__args", "v__kwargs", "v__positional_only" and "v__duplicate_kwargs" are not permitted'
+    ):
+
+        @validate_arguments
+        def foo3(v__positional_only: int):
+            pass
+
+    with pytest.raises(
+        ConfigError, match='"v__args", "v__kwargs", "v__positional_only" and "v__duplicate_kwargs" are not permitted'
+    ):
 
         @validate_arguments
-        def foo(v__args: int):
+        def foo4(v__duplicate_kwargs: int):
             pass
 
 
diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py
index 20977ce23..04d34aa60 100644
--- a/tests/test_edge_cases.py
+++ b/tests/test_edge_cases.py
@@ -194,26 +194,38 @@ def test_tuple():
 
 def test_tuple_more():
     class Model(BaseModel):
+        empty_tuple: Tuple[()]
         simple_tuple: tuple = None
         tuple_of_different_types: Tuple[int, float, str, bool] = None
 
-    m = Model(simple_tuple=[1, 2, 3, 4], tuple_of_different_types=[4, 3, 2, 1])
-    assert m.dict() == {'simple_tuple': (1, 2, 3, 4), 'tuple_of_different_types': (4, 3.0, '2', True)}
+    m = Model(empty_tuple=[], simple_tuple=[1, 2, 3, 4], tuple_of_different_types=[4, 3, 2, 1])
+    assert m.dict() == {
+        'empty_tuple': (),
+        'simple_tuple': (1, 2, 3, 4),
+        'tuple_of_different_types': (4, 3.0, '2', True),
+    }
 
 
 def test_tuple_length_error():
     class Model(BaseModel):
         v: Tuple[int, float, bool]
+        w: Tuple[()]
 
     with pytest.raises(ValidationError) as exc_info:
-        Model(v=[1, 2])
+        Model(v=[1, 2], w=[1])
     assert exc_info.value.errors() == [
         {
             'loc': ('v',),
             'msg': 'wrong tuple length 2, expected 3',
             'type': 'value_error.tuple.length',
             'ctx': {'actual_length': 2, 'expected_length': 3},
-        }
+        },
+        {
+            'loc': ('w',),
+            'msg': 'wrong tuple length 1, expected 0',
+            'type': 'value_error.tuple.length',
+            'ctx': {'actual_length': 1, 'expected_length': 0},
+        },
     ]
 
 
diff --git a/tests/test_forward_ref.py b/tests/test_forward_ref.py
index 459c384dc..ffcac4ec5 100644
--- a/tests/test_forward_ref.py
+++ b/tests/test_forward_ref.py
@@ -4,6 +4,7 @@ from typing import Optional, Tuple
 import pytest
 
 from pydantic import BaseModel, ConfigError, ValidationError
+from pydantic.typing import Literal
 
 skip_pre_37 = pytest.mark.skipif(sys.version_info < (3, 7), reason='testing >= 3.7 behaviour only')
 
@@ -480,6 +481,32 @@ def test_forward_ref_with_create_model(create_module):
         assert instance.sub.dict() == {'foo': 'bar'}
 
 
+@skip_pre_37
+@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
+def test_resolve_forward_ref_dataclass(create_module):
+    module = create_module(
+        # language=Python
+        """
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+from pydantic import BaseModel
+from pydantic.typing import Literal
+
+@dataclass
+class Base:
+    literal: Literal[1, 2]
+
+class What(BaseModel):
+    base: Base
+        """
+    )
+
+    m = module.What(base=module.Base(literal=1))
+    assert m.base.literal == 1
+
+
 def test_nested_forward_ref():
     class NestedTuple(BaseModel):
         x: Tuple[int, Optional['NestedTuple']]  # noqa: F821
diff --git a/tests/test_main.py b/tests/test_main.py
index e290ba03b..dce214b1e 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1120,6 +1120,60 @@ def test_parse_obj_non_mapping_root():
     ]
 
 
+def test_parse_obj_nested_root():
+    class Pokemon(BaseModel):
+        name: str
+        level: int
+
+    class Pokemons(BaseModel):
+        __root__: List[Pokemon]
+
+    class Player(BaseModel):
+        rank: int
+        pokemons: Pokemons
+
+    class Players(BaseModel):
+        __root__: Dict[str, Player]
+
+    class Tournament(BaseModel):
+        players: Players
+        city: str
+
+    payload = {
+        'players': {
+            'Jane': {
+                'rank': 1,
+                'pokemons': [
+                    {
+                        'name': 'Pikachu',
+                        'level': 100,
+                    },
+                    {
+                        'name': 'Bulbasaur',
+                        'level': 13,
+                    },
+                ],
+            },
+            'Tarzan': {
+                'rank': 2,
+                'pokemons': [
+                    {
+                        'name': 'Jigglypuff',
+                        'level': 7,
+                    },
+                ],
+            },
+        },
+        'city': 'Qwerty',
+    }
+
+    tournament = Tournament.parse_obj(payload)
+    assert tournament.city == 'Qwerty'
+    assert len(tournament.players.__root__) == 2
+    assert len(tournament.players.__root__['Jane'].pokemons.__root__) == 2
+    assert tournament.players.__root__['Jane'].pokemons.__root__[0].name == 'Pikachu'
+
+
 def test_untouched_types():
     from pydantic import BaseModel
 
@@ -1449,7 +1503,6 @@ def test_inherited_model_field_copy():
     assert id(image_2) != id(item.images[1])
 
 
-@pytest.mark.xfail(reason='see https://github.com/samuelcolvin/pydantic/pull/2193#issuecomment-778345456')
 def test_inherited_model_field_untouched():
     """It should not copy models used as fields if explicitly asked"""
 
@@ -1468,7 +1521,7 @@ def test_inherited_model_field_untouched():
     image_1 = Image(path='my_image1.png')
     image_2 = Image(path='my_image2.png')
 
-    item = Item(images={image_1, image_2})
+    item = Item(images=(image_1, image_2))
     assert image_1 in item.images
 
     assert id(image_1) == id(item.images[0])
diff --git a/tests/test_orm_mode.py b/tests/test_orm_mode.py
index 6e30d66a0..124895959 100644
--- a/tests/test_orm_mode.py
+++ b/tests/test_orm_mode.py
@@ -1,4 +1,4 @@
-from typing import Any, List
+from typing import Any, Dict, List
 
 import pytest
 
@@ -47,6 +47,47 @@ def test_getdict():
     assert repr(gd) == "GetterDict[TestCls]({'a': 1, 'c': 3, 'd': 4})"
 
 
+def test_orm_mode_root():
+    class PokemonCls:
+        def __init__(self, *, en_name: str, jp_name: str):
+            self.en_name = en_name
+            self.jp_name = jp_name
+
+    class Pokemon(BaseModel):
+        en_name: str
+        jp_name: str
+
+        class Config:
+            orm_mode = True
+
+    class PokemonList(BaseModel):
+        __root__: List[Pokemon]
+
+        class Config:
+            orm_mode = True
+
+    pika = PokemonCls(en_name='Pikachu', jp_name='ピカチュウ')
+    bulbi = PokemonCls(en_name='Bulbasaur', jp_name='フシギダネ')
+
+    pokemons = PokemonList.from_orm([pika, bulbi])
+    assert pokemons.__root__ == [
+        Pokemon(en_name='Pikachu', jp_name='ピカチュウ'),
+        Pokemon(en_name='Bulbasaur', jp_name='フシギダネ'),
+    ]
+
+    class PokemonDict(BaseModel):
+        __root__: Dict[str, Pokemon]
+
+        class Config:
+            orm_mode = True
+
+    pokemons = PokemonDict.from_orm({'pika': pika, 'bulbi': bulbi})
+    assert pokemons.__root__ == {
+        'pika': Pokemon(en_name='Pikachu', jp_name='ピカチュウ'),
+        'bulbi': Pokemon(en_name='Bulbasaur', jp_name='フシギダネ'),
+    }
+
+
 def test_orm_mode():
     class PetCls:
         def __init__(self, *, name: str, species: str):
diff --git a/tests/test_settings.py b/tests/test_settings.py
index 9a4f53c88..7513c4c53 100644
--- a/tests/test_settings.py
+++ b/tests/test_settings.py
@@ -835,12 +835,10 @@ def test_secrets_invalid_secrets_dir(tmp_path):
 @pytest.mark.skipif(sys.platform.startswith('win'), reason='windows paths break regex')
 def test_secrets_missing_location(tmp_path):
     class Settings(BaseSettings):
-        foo: str
-
         class Config:
             secrets_dir = tmp_path / 'does_not_exist'
 
-    with pytest.raises(SettingsError, match=f'directory "{tmp_path}/does_not_exist" does not exist'):
+    with pytest.warns(UserWarning, match=f'directory "{tmp_path}/does_not_exist" does not exist'):
         Settings()
 
 
diff --git a/tests/test_typing.py b/tests/test_typing.py
new file mode 100644
index 000000000..d0d99125e
--- /dev/null
+++ b/tests/test_typing.py
@@ -0,0 +1,56 @@
+from collections import namedtuple
+from typing import NamedTuple
+
+import pytest
+
+from pydantic.typing import is_namedtuple, is_typeddict
+
+try:
+    from typing import TypedDict as typing_TypedDict
+except ImportError:
+    typing_TypedDict = None
+
+try:
+    from typing_extensions import TypedDict as typing_extensions_TypedDict
+except ImportError:
+    typing_extensions_TypedDict = None
+
+
+try:
+    from mypy_extensions import TypedDict as mypy_extensions_TypedDict
+except ImportError:
+    mypy_extensions_TypedDict = None
+
+ALL_TYPEDDICT_KINDS = (typing_TypedDict, typing_extensions_TypedDict, mypy_extensions_TypedDict)
+
+
+def test_is_namedtuple():
+    class Employee(NamedTuple):
+        name: str
+        id: int = 3
+
+    assert is_namedtuple(namedtuple('Point', 'x y')) is True
+    assert is_namedtuple(Employee) is True
+    assert is_namedtuple(NamedTuple('Employee', [('name', str), ('id', int)])) is True
+
+    class Other(tuple):
+        name: str
+        id: int
+
+    assert is_namedtuple(Other) is False
+
+
+@pytest.mark.parametrize('TypedDict', (t for t in ALL_TYPEDDICT_KINDS if t is not None))
+def test_is_typeddict_typing(TypedDict):
+    class Employee(TypedDict):
+        name: str
+        id: int
+
+    assert is_typeddict(Employee) is True
+    assert is_typeddict(TypedDict('Employee', {'name': str, 'id': int})) is True
+
+    class Other(dict):
+        name: str
+        id: int
+
+    assert is_typeddict(Other) is False