Repository: pydantic/pydantic

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.


Comments:
I saw this the other day on typed dicts, which python versions support this?

I'm keen if the implementation isn't too complicated.
It's supported in 3.6+ for sure. 

Great, I'll make a PR soon then!

BTW, was this issue closed by accident?

Patch:
diff --git a/docs/examples/model_config_class_kwargs.py b/docs/examples/model_config_class_kwargs.py
new file mode 100644
--- /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/pydantic/main.py b/pydantic/main.py
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -168,18 +168,18 @@ def prepare_field(cls, field: 'ModelField') -> None:
         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 @@ def __new__(mcs, name, bases, namespace, **kwargs):  # noqa C901
                 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)
 


Test patch:
diff --git a/tests/test_main.py b/tests/test_main.py
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1551,3 +1551,32 @@ class Item(BaseModel):
 
     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'