Repository: samuelcolvin/pydantic (python)

Issue:
Configure models through class kwargs
### 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 feature/change is needed
* [x] After submitting this, I commit to one of:
    * Look through open issues and helped at least one other person
    * Hit the "watch" button on this repo to receive notifications and I commit to help at least 2 people that ask questions in the future
    * Implement a Pull Request for a confirmed bug
Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```py
             pydantic version: 1.6.1
            pydantic compiled: True
                 install path: /Users/rocky/.local/share/virtualenvs/superapp-search-hVQXXu_u/lib/python3.8/site-packages/pydantic
               python version: 3.8.7 (v3.8.7:6503f05dd5, Dec 21 2020, 12:45:15)  [Clang 6.0 (clang-600.0.57)]
                     platform: macOS-10.16-x86_64-i386-64bit
     optional deps. installed: ['typing-extensions']
```
# Feature Request
Allow to configure models through class kwargs, allowing more elegant and compact code

```py
from pydantic import BaseModel

class Model(BaseModel, alias_generator=str.upper):
    foo: int
    bar: int
```
==
```py
class Model(BaseModel)
    foo: int
    bar: int

    class Config:
        alias_generator = str.upper
```

Will be happy to implement this myself, once feature request is accepted.

Pull request:
Allow to configure models through class kwargs
<!-- 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
Allow to configure models through class kwargs

```py
from pydantic import BaseModel

class Model(BaseModel, extra='allow'):
    foo: int
    bar: int
```
## Related issue number
Closes #2330

## Implementation details
Any kwargs that are passed into class and match attrs of `BaseConfig` will be included in config. 
All unused kwargs will be passed to `type` untouched so #867 won't break.

## 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)


Patch:
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 91af5560a..bf1982bfa 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,11 +1,11 @@
 blank_issues_enabled: true
 contact_links:
 - name: Question
-  url: https://github.com/samuelcolvin/pydantic/discussions/new
+  url: 'https://github.com/samuelcolvin/pydantic/discussions/new?category=question'
   about: Ask a question about how to use pydantic using github discussions
 
 - name: Feature Request
-  url: https://github.com/samuelcolvin/pydantic/discussions/new
+  url: 'https://github.com/samuelcolvin/pydantic/discussions/new?category=feature-request'
   about: >
     If you think we should add a new feature to pydantic, please start a discussion, once it attracts
     wider support, it can be migrated to an issue
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 14426a992..2c865edb1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -81,7 +81,7 @@ jobs:
         DEPS: yes
 
     - name: uninstall deps
-      run: pip uninstall -y cython email-validator typing-extensions devtools python-dotenv
+      run: pip uninstall -y cython email-validator devtools python-dotenv
 
     - name: test compiled without deps
       run: make test
diff --git a/changes/2356-MrMrRobat.md b/changes/2356-MrMrRobat.md
new file mode 100644
index 000000000..d4adb3c2b
--- /dev/null
+++ b/changes/2356-MrMrRobat.md
@@ -0,0 +1,1 @@
+Allow configuring models through class kwargs
\ No newline at end of file
diff --git a/changes/2368-samuelcolvin.md b/changes/2368-samuelcolvin.md
new file mode 100644
index 000000000..c71748f2b
--- /dev/null
+++ b/changes/2368-samuelcolvin.md
@@ -0,0 +1,1 @@
+Making `typing-extensions` a required dependency.
diff --git a/docs/contributing.md b/docs/contributing.md
index 2ddfd118f..dfbacbcc1 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -2,7 +2,7 @@ We'd love you to contribute to *pydantic*!
 
 ## Issues
 
-Questions, feature requests and bug reports are all welcome as issues. **However, to report a security
+Questions, feature requests and bug reports are all welcome as [discussions or issues](https://github.com/samuelcolvin/pydantic/issues/new/choose). **However, to report a security
 vulnerability, please see our [security policy](https://github.com/samuelcolvin/pydantic/security/policy).**
 
 To make it as simple as possible for us to help you, please include the output of the following call in your issue:
diff --git a/docs/examples/annotated_types_typed_dict.py b/docs/examples/annotated_types_typed_dict.py
index 382ba65f3..bbfbc877b 100644
--- a/docs/examples/annotated_types_typed_dict.py
+++ b/docs/examples/annotated_types_typed_dict.py
@@ -1,4 +1,4 @@
-from typing import TypedDict
+from typing_extensions import TypedDict
 
 from pydantic import BaseModel, Extra, ValidationError
 
diff --git a/docs/examples/model_config_class_kwargs.py b/docs/examples/model_config_class_kwargs.py
new file mode 100644
index 000000000..3a1841c57
--- /dev/null
+++ b/docs/examples/model_config_class_kwargs.py
@@ -0,0 +1,11 @@
+from pydantic import BaseModel, ValidationError, Extra
+
+
+class Model(BaseModel, extra=Extra.forbid):
+    a: str
+
+
+try:
+    Model(a='spam', b='oh no')
+except ValidationError as e:
+    print(e)
diff --git a/docs/examples/models_from_typeddict.py b/docs/examples/models_from_typeddict.py
index 54136274e..2454db70f 100644
--- a/docs/examples/models_from_typeddict.py
+++ b/docs/examples/models_from_typeddict.py
@@ -1,4 +1,4 @@
-from typing import TypedDict
+from typing_extensions import TypedDict
 
 from pydantic import ValidationError, create_model_from_typeddict
 
diff --git a/docs/examples/schema_annotated.py b/docs/examples/schema_annotated.py
index ab29ad38a..12f1ab5f1 100644
--- a/docs/examples/schema_annotated.py
+++ b/docs/examples/schema_annotated.py
@@ -1,11 +1,7 @@
 from uuid import uuid4
 
-try:
-    from typing import Annotated
-except ImportError:
-    from typing_extensions import Annotated
-
 from pydantic import BaseModel, Field
+from typing_extensions import Annotated
 
 
 class Foo(BaseModel):
diff --git a/docs/install.md b/docs/install.md
index b9bcb4d00..9cbd9e285 100644
--- a/docs/install.md
+++ b/docs/install.md
@@ -4,7 +4,9 @@ Installation is as simple as:
 pip install pydantic
 ```
 
-*pydantic* has no required dependencies except python 3.6, 3.7, 3.8, or 3.9 (and the dataclasses package for python 3.6).
+*pydantic* has no required dependencies except python 3.6, 3.7, 3.8, or 3.9,
+[`typing-extensions`](https://pypi.org/project/typing-extensions/), and the
+[`dataclasses`](https://pypi.org/project/dataclasses/) backport package for python 3.6.
 If you've got python 3.6+ and `pip` installed, you're good to go.
 
 Pydantic is also available on [conda](https://www.anaconda.com) under the [conda-forge](https://conda-forge.org)
@@ -30,7 +32,6 @@ print('compiled:', pydantic.compiled)
 *pydantic* has three optional dependencies:
 
 * If you require email validation you can add [email-validator](https://github.com/JoshData/python-email-validator)
-* use of `Literal` prior to python 3.8 relies on [typing-extensions](https://pypi.org/project/typing-extensions/)
 * [dotenv file support](usage/settings.md#dotenv-env-support) with `Settings` requires
   [python-dotenv](https://pypi.org/project/python-dotenv)
 
@@ -38,18 +39,16 @@ To install these along with *pydantic*:
 ```bash
 pip install pydantic[email]
 # or
-pip install pydantic[typing_extensions]
-# or
 pip install pydantic[dotenv]
 # or just
-pip install pydantic[email,typing_extensions,dotenv]
+pip install pydantic[email,dotenv]
 ```
 
-Of course, you can also install these requirements manually with `pip install email-validator` and/or `pip install typing_extensions`.
+Of course, you can also install these requirements manually with `pip install email-validator` and/or `pip install`.
 
 And if you prefer to install *pydantic* directly from the repository:
 ```bash
 pip install git+git://github.com/samuelcolvin/pydantic@master#egg=pydantic
 # or with extras
-pip install git+git://github.com/samuelcolvin/pydantic@master#egg=pydantic[email,typing_extensions]
+pip install git+git://github.com/samuelcolvin/pydantic@master#egg=pydantic[email,dotenv]
 ```
diff --git a/docs/requirements.txt b/docs/requirements.txt
index b1b8cb912..1373876e7 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,12 +1,11 @@
 ansi2html==1.6.0
 flake8==3.8.4
 flake8-quotes==3.2.0
-hypothesis==5.44.0
+hypothesis==6.3.0
 mkdocs==1.1.2
 mkdocs-exclude==1.0.2
 mkdocs-material==6.2.8
 markdown-include==0.6.0
 sqlalchemy
-typing-extensions==3.7.4.3
 orjson
 ujson
diff --git a/docs/usage/model_config.md b/docs/usage/model_config.md
index 91c8d3d64..c2c09fc93 100644
--- a/docs/usage/model_config.md
+++ b/docs/usage/model_config.md
@@ -89,8 +89,13 @@ not be included in the model schemas. **Note**: this means that attributes on th
 ```
 _(This script is complete, it should run "as is")_
 
-Similarly, if using the `@dataclass` decorator:
+Also, you can specify config options as model class kwargs:
+```py
+{!.tmp_examples/model_config_class_kwargs.py!}
+```
+_(This script is complete, it should run "as is")_
 
+Similarly, if using the `@dataclass` decorator:
 ```py
 {!.tmp_examples/model_config_dataclass.py!}
 ```
diff --git a/pydantic/_hypothesis_plugin.py b/pydantic/_hypothesis_plugin.py
index 1cfa7c13b..16769e4df 100644
--- a/pydantic/_hypothesis_plugin.py
+++ b/pydantic/_hypothesis_plugin.py
@@ -152,11 +152,11 @@ st.register_type_strategy(pydantic.SecretStr, st.text().map(pydantic.SecretStr))
 st.register_type_strategy(pydantic.IPvAnyAddress, st.ip_addresses())
 st.register_type_strategy(
     pydantic.IPvAnyInterface,
-    st.from_type(ipaddress.IPv4Interface) | st.from_type(ipaddress.IPv6Interface),
+    st.from_type(ipaddress.IPv4Interface) | st.from_type(ipaddress.IPv6Interface),  # type: ignore[arg-type]
 )
 st.register_type_strategy(
     pydantic.IPvAnyNetwork,
-    st.from_type(ipaddress.IPv4Network) | st.from_type(ipaddress.IPv6Network),
+    st.from_type(ipaddress.IPv4Network) | st.from_type(ipaddress.IPv6Network),  # type: ignore[arg-type]
 )
 
 # We hook into the con***() functions and the ConstrainedNumberMeta metaclass,
@@ -212,7 +212,6 @@ def resolves(
 # Type-to-strategy resolver functions
 
 
-@resolves(pydantic.Json)
 @resolves(pydantic.JsonWrapper)
 def resolve_json(cls):  # type: ignore[no-untyped-def]
     try:
@@ -221,7 +220,7 @@ def resolve_json(cls):  # type: ignore[no-untyped-def]
         finite = st.floats(allow_infinity=False, allow_nan=False)
         inner = st.recursive(
             base=st.one_of(st.none(), st.booleans(), st.integers(), finite, st.text()),
-            extend=lambda x: st.lists(x) | st.dictionaries(st.text(), x),
+            extend=lambda x: st.lists(x) | st.dictionaries(st.text(), x),  # type: ignore
         )
     return st.builds(
         json.dumps,
@@ -347,3 +346,4 @@ def resolve_constr(cls):  # type: ignore[no-untyped-def]  # pragma: no cover
 for typ in pydantic.types._DEFINED_TYPES:
     _registered(typ)
 pydantic.types._registered = _registered
+st.register_type_strategy(pydantic.Json, resolve_json)
diff --git a/pydantic/annotated_types.py b/pydantic/annotated_types.py
index a29da28c0..bffcdc620 100644
--- a/pydantic/annotated_types.py
+++ b/pydantic/annotated_types.py
@@ -16,29 +16,21 @@ def create_model_from_typeddict(typeddict_cls: Type['TypedDict'], **kwargs: Any)
     """
     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).
+    we raise an error if this happens (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,
+    if not hasattr(typeddict_cls, '__required_keys__'):
+        raise TypeError(
+            'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict`. '
+            'Without it, there is no way to differentiate required and optional fields when subclassed.'
         )
-        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()
-        }
+
+    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()
+    }
 
     return create_model(typeddict_cls.__name__, **kwargs, **field_definitions)
 
diff --git a/pydantic/fields.py b/pydantic/fields.py
index 127f9cb1a..a59c598f4 100644
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -22,6 +22,8 @@ from typing import (
     Union,
 )
 
+from typing_extensions import Annotated
+
 from . import errors as errors_
 from .class_validators import Validator, make_generic_validator, prep_validators
 from .error_wrappers import ErrorWrapper
@@ -29,7 +31,6 @@ from .errors import ConfigError, NoneIsNotAllowedError
 from .types import Json, JsonWrapper
 from .typing import (
     NONE_TYPES,
-    Annotated,
     Callable,
     ForwardRef,
     NoArgAnyCallable,
diff --git a/pydantic/main.py b/pydantic/main.py
index 0f833f16f..fccdff668 100644
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -168,18 +168,18 @@ class BaseConfig:
         pass
 
 
-def inherit_config(self_config: 'ConfigType', parent_config: 'ConfigType') -> 'ConfigType':
-    namespace = {}
+def inherit_config(self_config: 'ConfigType', parent_config: 'ConfigType', **namespace: Any) -> 'ConfigType':
     if not self_config:
-        base_classes = (parent_config,)
+        base_classes: Tuple['ConfigType', ...] = (parent_config,)
     elif self_config == parent_config:
         base_classes = (self_config,)
     else:
-        base_classes = self_config, parent_config  # type: ignore
-        namespace['json_encoders'] = {
-            **getattr(parent_config, 'json_encoders', {}),
-            **getattr(self_config, 'json_encoders', {}),
-        }
+        base_classes = self_config, parent_config
+
+    namespace['json_encoders'] = {
+        **getattr(parent_config, 'json_encoders', {}),
+        **getattr(self_config, 'json_encoders', {}),
+    }
 
     return type('Config', base_classes, namespace)
 
@@ -251,7 +251,12 @@ class ModelMetaclass(ABCMeta):
                 private_attributes.update(base.__private_attributes__)
                 class_vars.update(base.__class_vars__)
 
-        config = inherit_config(namespace.get('Config'), config)
+        config_kwargs = {key: kwargs.pop(key) for key in kwargs.keys() & BaseConfig.__dict__.keys()}
+        config_from_namespace = namespace.get('Config')
+        if config_kwargs and config_from_namespace:
+            raise TypeError('Specifying config in two places is ambiguous, use either Config attribute or class kwargs')
+        config = inherit_config(config_from_namespace, config, **config_kwargs)
+
         validators = inherit_validators(extract_validators(namespace), validators)
         vg = ValidatorGroup(validators)
 
diff --git a/pydantic/schema.py b/pydantic/schema.py
index 2b68b89e5..582674755 100644
--- a/pydantic/schema.py
+++ b/pydantic/schema.py
@@ -26,6 +26,8 @@ from typing import (
 )
 from uuid import UUID
 
+from typing_extensions import Annotated, Literal
+
 from .fields import (
     SHAPE_FROZENSET,
     SHAPE_GENERIC,
@@ -61,9 +63,7 @@ from .types import (
 )
 from .typing import (
     NONE_TYPES,
-    Annotated,
     ForwardRef,
-    Literal,
     get_args,
     get_origin,
     is_callable_type,
diff --git a/pydantic/typing.py b/pydantic/typing.py
index 36cd1fb1d..ff0cb7ee2 100644
--- a/pydantic/typing.py
+++ b/pydantic/typing.py
@@ -20,6 +20,8 @@ from typing import (  # type: ignore
     cast,
 )
 
+from typing_extensions import Annotated, Literal
+
 try:
     from typing import _TypingBase as typing_base  # type: ignore
 except ImportError:
@@ -81,87 +83,21 @@ else:
     NoArgAnyCallable = TypingCallable[[], Any]
 
 
-if sys.version_info >= (3, 9):
-    from typing import Annotated
-else:
-    if TYPE_CHECKING:
-        from typing_extensions import Annotated
-    else:  # due to different mypy warnings raised during CI for python 3.7 and 3.8
-        try:
-            from typing_extensions import Annotated
-        except ImportError:
-            # Create mock Annotated values distinct from `None`, which is a valid `get_origin`
-            # return value.
-            class _FalseMeta(type):
-                # Allow short circuiting with "Annotated[...] if Annotated else None".
-                def __bool__(cls):
-                    return False
-
-                # Give a nice suggestion for unguarded use
-                def __getitem__(cls, key):
-                    raise RuntimeError(
-                        'Annotated is not supported in this python version, please `pip install typing-extensions`.'
-                    )
-
-            class Annotated(metaclass=_FalseMeta):
-                pass
-
-
 # Annotated[...] is implemented by returning an instance of one of these classes, depending on
 # python/typing_extensions version.
-AnnotatedTypeNames = ('AnnotatedMeta', '_AnnotatedAlias')
+AnnotatedTypeNames = {'AnnotatedMeta', '_AnnotatedAlias'}
 
 
-if sys.version_info < (3, 8):  # noqa: C901
-    if TYPE_CHECKING:
-        from typing_extensions import Literal
-    else:  # due to different mypy warnings raised during CI for python 3.7 and 3.8
-        try:
-            from typing_extensions import Literal
-        except ImportError:
-            Literal = None
-
-    if sys.version_info < (3, 7):
-
-        def get_args(t: Type[Any]) -> Tuple[Any, ...]:
-            """Simplest get_args compatibility layer possible.
-
-            The Python 3.6 typing module does not have `_GenericAlias` so
-            this won't work for everything. In particular this will not
-            support the `generics` module (we don't support generic models in
-            python 3.6).
-
-            """
-            if Annotated and type(t).__name__ in AnnotatedTypeNames:
-                return t.__args__ + t.__metadata__
-            return getattr(t, '__args__', ())
-
-    else:
-        from typing import _GenericAlias
-
-        def get_args(t: Type[Any]) -> Tuple[Any, ...]:
-            """Compatibility version of get_args for python 3.7.
-
-            Mostly compatible with the python 3.8 `typing` module version
-            and able to handle almost all use cases.
-            """
-            if Annotated and type(t).__name__ in AnnotatedTypeNames:
-                return t.__args__ + t.__metadata__
-            if isinstance(t, _GenericAlias):
-                res = t.__args__
-                if t.__origin__ is Callable and res and res[0] is not Ellipsis:
-                    res = (list(res[:-1]), res[-1])
-                return res
-            return getattr(t, '__args__', ())
+if sys.version_info < (3, 8):
 
     def get_origin(t: Type[Any]) -> Optional[Type[Any]]:
-        if Annotated and type(t).__name__ in AnnotatedTypeNames:
+        if type(t).__name__ in AnnotatedTypeNames:
             return cast(Type[Any], Annotated)  # mypy complains about _SpecialForm in py3.6
         return getattr(t, '__origin__', None)
 
 
 else:
-    from typing import Literal, get_args as typing_get_args, get_origin as typing_get_origin
+    from typing import get_origin as _typing_get_origin
 
     def get_origin(tp: Type[Any]) -> Type[Any]:
         """
@@ -170,11 +106,50 @@ else:
         It should be useless once https://github.com/cython/cython/issues/3537 is
         solved and https://github.com/samuelcolvin/pydantic/pull/1753 is merged.
         """
-        if Annotated and type(tp).__name__ in AnnotatedTypeNames:
+        if type(tp).__name__ in AnnotatedTypeNames:
             return cast(Type[Any], Annotated)  # mypy complains about _SpecialForm
-        return typing_get_origin(tp) or getattr(tp, '__origin__', None)
+        return _typing_get_origin(tp) or getattr(tp, '__origin__', None)
+
+
+if sys.version_info < (3, 7):  # noqa: C901 (ignore complexity)
+
+    def get_args(t: Type[Any]) -> Tuple[Any, ...]:
+        """Simplest get_args compatibility layer possible.
+
+        The Python 3.6 typing module does not have `_GenericAlias` so
+        this won't work for everything. In particular this will not
+        support the `generics` module (we don't support generic models in
+        python 3.6).
+
+        """
+        if type(t).__name__ in AnnotatedTypeNames:
+            return t.__args__ + t.__metadata__
+        return getattr(t, '__args__', ())
+
+
+elif sys.version_info < (3, 8):  # noqa: C901
+    from typing import _GenericAlias
+
+    def get_args(t: Type[Any]) -> Tuple[Any, ...]:
+        """Compatibility version of get_args for python 3.7.
+
+        Mostly compatible with the python 3.8 `typing` module version
+        and able to handle almost all use cases.
+        """
+        if type(t).__name__ in AnnotatedTypeNames:
+            return t.__args__ + t.__metadata__
+        if isinstance(t, _GenericAlias):
+            res = t.__args__
+            if t.__origin__ is Callable and res and res[0] is not Ellipsis:
+                res = (list(res[:-1]), res[-1])
+            return res
+        return getattr(t, '__args__', ())
+
+
+else:
+    from typing import get_args as _typing_get_args
 
-    def generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]:
+    def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]:
         """
         In python 3.9, `typing.Dict`, `typing.List`, ...
         do have an empty `__args__` by default (instead of the generic ~T for example).
@@ -196,10 +171,10 @@ else:
             get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
             get_args(Callable[[], T][int]) == ([], int)
         """
-        if Annotated and type(tp).__name__ in AnnotatedTypeNames:
+        if type(tp).__name__ in AnnotatedTypeNames:
             return tp.__args__ + tp.__metadata__
         # the fallback is needed for the same reasons as `get_origin` (see above)
-        return typing_get_args(tp) or getattr(tp, '__args__', ()) or generic_get_args(tp)
+        return _typing_get_args(tp) or getattr(tp, '__args__', ()) or _generic_get_args(tp)
 
 
 if TYPE_CHECKING:
@@ -220,7 +195,6 @@ if TYPE_CHECKING:
 __all__ = (
     'ForwardRef',
     'Callable',
-    'Annotated',
     'AnyCallable',
     'NoArgAnyCallable',
     'NoneType',
@@ -230,7 +204,6 @@ __all__ = (
     'is_callable_type',
     'is_literal_type',
     'literal_values',
-    'Literal',
     'is_namedtuple',
     'is_typeddict',
     'is_new_type',
@@ -255,9 +228,7 @@ __all__ = (
 
 
 NoneType = None.__class__
-NONE_TYPES: Set[Any] = {None, NoneType}
-if Literal:
-    NONE_TYPES.add(Literal[None])
+NONE_TYPES: Set[Any] = {None, NoneType, Literal[None]}
 
 
 def display_as_type(v: Type[Any]) -> str:
diff --git a/pydantic/validators.py b/pydantic/validators.py
index f4e21bbb2..57a1a23fc 100644
--- a/pydantic/validators.py
+++ b/pydantic/validators.py
@@ -25,13 +25,14 @@ from typing import (
 )
 from uuid import UUID
 
+from typing_extensions import Literal
+
 from . import errors
 from .datetime_parse import parse_date, parse_datetime, parse_duration, parse_time
 from .typing import (
     NONE_TYPES,
     AnyCallable,
     ForwardRef,
-    Literal,
     all_literal_values,
     display_as_type,
     get_class,
diff --git a/requirements.txt b/requirements.txt
index 94aa76af5..d9562692d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,8 +1,8 @@
 # requirements for compilation and from setup.py so dependabot prompts us to test with latest version of these packages
 
-Cython==0.29.21;sys_platform!='win32'
+Cython==0.29.22;sys_platform!='win32'
 devtools==0.6.1
 email-validator==1.1.2
 dataclasses==0.6; python_version < '3.7'
-typing-extensions==3.7.4.3; python_version < '3.9'
+typing-extensions==3.7.4.3
 python-dotenv==0.15.0
diff --git a/setup.py b/setup.py
index 96c735324..52baae278 100644
--- a/setup.py
+++ b/setup.py
@@ -127,11 +127,11 @@ setup(
     python_requires='>=3.6.1',
     zip_safe=False,  # https://mypy.readthedocs.io/en/latest/installed_packages.html
     install_requires=[
-        'dataclasses>=0.6;python_version<"3.7"'
+        'dataclasses>=0.6;python_version<"3.7"',
+        'typing-extensions>=3.7.4.3'
     ],
     extras_require={
         'email': ['email-validator>=1.0.3'],
-        'typing_extensions': ['typing-extensions>=3.7.4'],
         'dotenv': ['python-dotenv>=0.10.4'],
     },
     ext_modules=ext_modules,


Test patch:
diff --git a/tests/mypy/test_mypy.py b/tests/mypy/test_mypy.py
index a4b9fbef5..a81cfe0f8 100644
--- a/tests/mypy/test_mypy.py
+++ b/tests/mypy/test_mypy.py
@@ -11,9 +11,9 @@ except ImportError:
     mypy_api = None
 
 try:
-    import typing_extensions
+    import dotenv
 except ImportError:
-    typing_extensions = None
+    dotenv = None
 
 # This ensures mypy can find the test files, no matter where tests are run from:
 os.chdir(Path(__file__).parent.parent.parent)
@@ -33,7 +33,7 @@ cases = [
 executable_modules = list({fname[:-3] for _, fname, out_fname in cases if out_fname is None})
 
 
-@pytest.mark.skipif(not (typing_extensions and mypy_api), reason='typing_extensions or mypy are not installed')
+@pytest.mark.skipif(not (dotenv and mypy_api), reason='dotenv or mypy are not installed')
 @pytest.mark.parametrize('config_filename,python_filename,output_filename', cases)
 def test_mypy_results(config_filename, python_filename, output_filename):
     full_config_filename = f'tests/mypy/configs/{config_filename}'
diff --git a/tests/requirements-linting.txt b/tests/requirements-linting.txt
index d8c20ee92..7cb785dc3 100644
--- a/tests/requirements-linting.txt
+++ b/tests/requirements-linting.txt
@@ -1,9 +1,9 @@
 black==20.8b1
 flake8==3.8.4
 flake8-quotes==3.2.0
-hypothesis==5.44.0
+hypothesis==6.3.0
 isort==5.7.0
-mypy==0.800
+mypy==0.812
 pycodestyle==2.6.0
 pyflakes==2.2.0
 twine==3.3.0
diff --git a/tests/requirements-testing.txt b/tests/requirements-testing.txt
index cfd2dd20d..88f939fb2 100644
--- a/tests/requirements-testing.txt
+++ b/tests/requirements-testing.txt
@@ -1,8 +1,8 @@
 coverage==5.4
-hypothesis==5.44.0
+hypothesis==6.3.0
 # pin importlib-metadata as upper versions need typing-extensions to work if on python < 3.8
 importlib-metadata==3.1.0;python_version<"3.8"
-mypy==0.800
+mypy==0.812
 pytest==6.2.2
 pytest-cov==2.11.1
 pytest-mock==3.5.1
diff --git a/tests/test_annotated.py b/tests/test_annotated.py
index 4aff92c77..0e6321fd9 100644
--- a/tests/test_annotated.py
+++ b/tests/test_annotated.py
@@ -7,8 +7,9 @@ from pydantic import BaseModel, Field
 from pydantic.fields import Undefined
 from pydantic.typing import Annotated
 
+pytestmark = pytest.mark.skipif(not Annotated, reason='typing_extensions not installed')
+
 
-@pytest.mark.skipif(not Annotated, reason='typing_extensions not installed')
 @pytest.mark.parametrize(
     ['hint_fn', 'value'],
     [
@@ -55,7 +56,6 @@ def test_annotated(hint_fn, value):
         assert get_type_hints(M)['x'] == hint
 
 
-@pytest.mark.skipif(not Annotated, reason='typing_extensions not installed')
 @pytest.mark.parametrize(
     ['hint_fn', 'value', 'subclass_ctx'],
     [
@@ -93,7 +93,6 @@ def test_annotated_model_exceptions(hint_fn, value, subclass_ctx):
             x: hint = value
 
 
-@pytest.mark.skipif(not Annotated, reason='typing_extensions not installed')
 @pytest.mark.parametrize(
     ['hint_fn', 'value', 'empty_init_ctx'],
     [
@@ -121,7 +120,6 @@ def test_annotated_instance_exceptions(hint_fn, value, empty_init_ctx):
         assert M().x == 5
 
 
-@pytest.mark.skipif(not Annotated, reason='typing_extensions not installed')
 def test_field_reuse():
     field = Field(description='Long description')
 
diff --git a/tests/test_annotated_types.py b/tests/test_annotated_types.py
index 8c3fc087b..b4b84bac8 100644
--- a/tests/test_annotated_types.py
+++ b/tests/test_annotated_types.py
@@ -8,25 +8,20 @@ import sys
 from collections import namedtuple
 from typing import List, NamedTuple, Tuple
 
+import pytest
+from typing_extensions import TypedDict
+
+from pydantic import BaseModel, ValidationError
+
 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')
@@ -128,7 +123,6 @@ def test_namedtuple_right_length():
     ]
 
 
-@pytest.mark.skipif(not TypedDict, reason='typing_extensions not installed')
 def test_typeddict():
     class TD(TypedDict):
         a: int
@@ -153,7 +147,6 @@ def test_typeddict():
     ]
 
 
-@pytest.mark.skipif(not TypedDict, reason='typing_extensions not installed')
 def test_typeddict_non_total():
     class FullMovie(TypedDict, total=True):
         name: str
@@ -183,7 +176,6 @@ def test_typeddict_non_total():
     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
@@ -198,7 +190,7 @@ def test_partial_new_typeddict():
     assert m.user == {'id': 1}
 
 
-@pytest.mark.skipif(not LegacyTypedDict, reason='python 3.9+ is used or typing_extensions is installed')
+@pytest.mark.skipif(not LegacyTypedDict, reason='python 3.9+ is used, no legacy TypedDict')
 def test_partial_legacy_typeddict():
     class OptionalUser(LegacyTypedDict, total=False):
         name: str
@@ -206,26 +198,12 @@ def test_partial_legacy_typeddict():
     class User(OptionalUser):
         id: int
 
-    with pytest.warns(
-        UserWarning,
-        match='You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` for better support!',
-    ):
+    with pytest.raises(TypeError, match='^You should use `typing_extensions.TypedDict` instead of `typing.TypedDict`'):
 
         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
@@ -244,7 +222,6 @@ def test_typeddict_extra():
     ]
 
 
-@pytest.mark.skipif(not TypedDict, reason='typing_extensions not installed')
 def test_typeddict_schema():
     class Data(BaseModel):
         a: int
diff --git a/tests/test_errors.py b/tests/test_errors.py
index fd3098f72..0f901b4ed 100644
--- a/tests/test_errors.py
+++ b/tests/test_errors.py
@@ -4,11 +4,11 @@ from typing import Dict, List, Optional, Union
 from uuid import UUID, uuid4
 
 import pytest
+from typing_extensions import Literal
 
 from pydantic import UUID1, BaseConfig, BaseModel, PydanticTypeError, ValidationError, conint, errors, validator
 from pydantic.error_wrappers import flatten_errors, get_exc_type
 from pydantic.errors import StrRegexError
-from pydantic.typing import Literal
 
 
 def test_pydantic_error():
@@ -35,7 +35,6 @@ def test_pydantic_error_pickable():
     assert error.pattern == 'pika'
 
 
-@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
 def test_interval_validation_error():
     class Foo(BaseModel):
         model_type: Literal['foo']
diff --git a/tests/test_forward_ref.py b/tests/test_forward_ref.py
index ffcac4ec5..95595fcbf 100644
--- a/tests/test_forward_ref.py
+++ b/tests/test_forward_ref.py
@@ -4,7 +4,6 @@ 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')
 
@@ -482,7 +481,6 @@ def test_forward_ref_with_create_model(create_module):
 
 
 @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
diff --git a/tests/test_hypothesis_plugin.py b/tests/test_hypothesis_plugin.py
index d45552045..050dd0978 100644
--- a/tests/test_hypothesis_plugin.py
+++ b/tests/test_hypothesis_plugin.py
@@ -63,7 +63,6 @@ def gen_models():
         nonnegfloat: pydantic.NonNegativeFloat
 
     class JsonModel(pydantic.BaseModel):
-        json_any: pydantic.Json
         json_int: pydantic.Json[int]
         json_float: pydantic.Json[float]
         json_str: pydantic.Json[str]
diff --git a/tests/test_main.py b/tests/test_main.py
index 7046a391d..209473409 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -703,7 +703,6 @@ def test_enum_values():
     assert m.foo == 'foo'
 
 
-@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
 def test_literal_enum_values():
     FooEnum = Enum('FooEnum', {'foo': 'foo_value', 'bar': 'bar_value'})
 
@@ -1551,3 +1550,32 @@ def test_inherited_model_field_untouched():
 
     assert id(image_1) == id(item.images[0])
     assert id(image_2) == id(item.images[1])
+
+
+def test_class_kwargs_config():
+    class Base(BaseModel, extra='forbid', alias_generator=str.upper):
+        a: int
+
+    assert Base.__config__.extra is Extra.forbid
+    assert Base.__config__.alias_generator is str.upper
+    assert Base.__fields__['a'].alias == 'A'
+
+    class Model(Base, extra='allow'):
+        b: int
+
+    assert Model.__config__.extra is Extra.allow  # overwritten as intended
+    assert Model.__config__.alias_generator is str.upper  # inherited as intended
+    assert Model.__fields__['b'].alias == 'B'  # alias_generator still works
+
+
+def test_class_kwargs_config_and_attr_conflict():
+
+    with pytest.raises(
+        TypeError, match='Specifying config in two places is ambiguous, use either Config attribute or class kwargs'
+    ):
+
+        class Model(BaseModel, extra='allow'):
+            b: int
+
+            class Config:
+                extra = 'forbid'
diff --git a/tests/test_schema.py b/tests/test_schema.py
index f7d5d661d..7408f7fc4 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -25,6 +25,7 @@ from typing import (
 from uuid import UUID
 
 import pytest
+from typing_extensions import Literal
 
 from pydantic import BaseModel, Extra, Field, ValidationError, conlist, conset, validator
 from pydantic.color import Color
@@ -74,7 +75,6 @@ from pydantic.types import (
     conint,
     constr,
 )
-from pydantic.typing import Literal
 
 try:
     import email_validator
@@ -1754,7 +1754,6 @@ def test_new_type_schema():
     }
 
 
-@pytest.mark.skipif(not Literal, reason='typing_extensions not installed and python version < 3.8')
 def test_literal_schema():
     class Model(BaseModel):
         a: Literal[1]
diff --git a/tests/test_types.py b/tests/test_types.py
index 66bb6b862..4b6ef7240 100644
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -26,6 +26,7 @@ from typing import (
 from uuid import UUID
 
 import pytest
+from typing_extensions import Literal
 
 from pydantic import (
     UUID1,
@@ -69,7 +70,7 @@ from pydantic import (
     errors,
     validator,
 )
-from pydantic.typing import Literal, NoneType
+from pydantic.typing import NoneType
 
 try:
     import email_validator
@@ -2508,7 +2509,6 @@ def test_generic_without_params_error():
     ]
 
 
-@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
 def test_literal_single():
     class Model(BaseModel):
         a: Literal['a']
@@ -2526,7 +2526,6 @@ def test_literal_single():
     ]
 
 
-@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
 def test_literal_multiple():
     class Model(BaseModel):
         a_or_b: Literal['a', 'b']
@@ -2723,9 +2722,7 @@ def test_deque_json():
     assert Model(v=deque((1, 2, 3))).json() == '{"v": [1, 2, 3]}'
 
 
-none_value_type_cases = (None, type(None), NoneType)
-if Literal:
-    none_value_type_cases += (Literal[None],)
+none_value_type_cases = None, type(None), NoneType, Literal[None]
 
 
 @pytest.mark.parametrize('value_type', none_value_type_cases)
diff --git a/tests/test_utils.py b/tests/test_utils.py
index f7cd16360..dd45ca148 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -9,15 +9,14 @@ from enum import Enum
 from typing import Callable, Dict, List, NewType, Tuple, TypeVar, Union
 
 import pytest
+from typing_extensions import Annotated, Literal
 
 from pydantic import VERSION, BaseModel, ConstrainedList, conlist
 from pydantic.color import Color
 from pydantic.dataclasses import dataclass
 from pydantic.fields import Undefined
 from pydantic.typing import (
-    Annotated,
     ForwardRef,
-    Literal,
     all_literal_values,
     display_as_type,
     get_args,
@@ -366,7 +365,6 @@ def test_class_attribute():
     assert f.attr == 'not foo'
 
 
-@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
 def test_all_literal_values():
     L1 = Literal['1']
     assert all_literal_values(L1) == ('1',)
diff --git a/tests/test_validators.py b/tests/test_validators.py
index d97973a08..c863eda6c 100644
--- a/tests/test_validators.py
+++ b/tests/test_validators.py
@@ -5,10 +5,10 @@ from itertools import product
 from typing import Dict, List, Optional, Tuple
 
 import pytest
+from typing_extensions import Literal
 
 from pydantic import BaseModel, ConfigError, Extra, Field, ValidationError, errors, validator
 from pydantic.class_validators import make_generic_validator, root_validator
-from pydantic.typing import Literal
 
 
 def test_simple():
@@ -1142,7 +1142,6 @@ def test_assignment_validator_cls():
     assert validator_calls == 2
 
 
-@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
 def test_literal_validator():
     class Model(BaseModel):
         a: Literal['foo']
@@ -1161,7 +1160,6 @@ def test_literal_validator():
     ]
 
 
-@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
 def test_literal_validator_str_enum():
     class Bar(str, Enum):
         FIZ = 'fiz'
@@ -1183,7 +1181,6 @@ def test_literal_validator_str_enum():
     assert my_foo.fizfuz is Bar.FUZ
 
 
-@pytest.mark.skipif(not Literal, reason='typing_extensions not installed')
 def test_nested_literal_validator():
     L1 = Literal['foo']
     L2 = Literal['bar']