Repository: pydantic/pydantic

Issue:
GenericModel - Allow subclasses of concrete generics to be generic
### 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

# Feature Request

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.6.1
            pydantic compiled: False
                 install path: /wayfair/home/ch438l/pydantic/pydantic
               python version: 3.6.8 (default, Aug 10 2019, 06:54:07)  [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
                     platform: Linux-3.10.0-862.3.2.el7.x86_64-x86_64-with-centos-7.5.1804-Core
     optional deps. installed: ['typing-extensions', 'email-validator', 'devtools']
```
I'd like to be able to define a generic model, then create a subclass of that generic model that both fills in the type parameter of the superclass, while introducing new type parameters. Here's a currently failing test case that illustrates the issue:

```py
@skip_36
def test_generic_subclass_of_concrete_generic():
    T = TypeVar("T")
    U = TypeVar("U")

    class GenericBaseModel(GenericModel, Generic[T]):
        data: T

    class GenericSub(GenericBaseModel[int], Generic[U]):
        extra: U

    GenericSub[str]
```

Currently that test case fails with `TypeError: Cannot parameterize a concrete instantiation of a generic model`

This happens because `GenericBaseModel[int].__concrete__` gets set to `True`, and `GenericSub` inherits it.

This can easily be fixed with the following change to `generics.py`:

```py
        if cls.__concrete__:
            raise TypeError('Cannot parameterize a concrete instantiation of a generic model')
```

Should become

```py
        if cls.__concrete__ and typing.Generic not in cls.__bases__:
            raise TypeError('Cannot parameterize a concrete instantiation of a generic model')
```




Comments:

Patch:
diff --git a/docs/examples/models_generics_inheritance_extend.py b/docs/examples/models_generics_inheritance_extend.py
new file mode 100644
--- /dev/null
+++ b/docs/examples/models_generics_inheritance_extend.py
@@ -0,0 +1,19 @@
+from typing import TypeVar, Generic
+from pydantic.generics import GenericModel
+
+TypeX = TypeVar('TypeX')
+TypeY = TypeVar('TypeY')
+TypeZ = TypeVar('TypeZ')
+
+
+class BaseClass(GenericModel, Generic[TypeX, TypeY]):
+    x: TypeX
+    y: TypeY
+
+
+class ChildClass(BaseClass[int, TypeY], Generic[TypeY, TypeZ]):
+    z: TypeZ
+
+
+# Replace TypeY by str
+print(ChildClass[str, int](x=1, y='y', z=3))
diff --git a/pydantic/generics.py b/pydantic/generics.py
--- a/pydantic/generics.py
+++ b/pydantic/generics.py
@@ -6,6 +6,7 @@
     Callable,
     ClassVar,
     Dict,
+    Generic,
     Optional,
     Tuple,
     Type,
@@ -41,7 +42,7 @@ def __class_getitem__(cls: Type[GenericModelT], params: Union[Type[Any], Tuple[T
         cached = _generic_types_cache.get((cls, params))
         if cached is not None:
             return cached
-        if cls.__concrete__:
+        if cls.__concrete__ and Generic not in cls.__bases__:
             raise TypeError('Cannot parameterize a concrete instantiation of a generic model')
         if not isinstance(params, tuple):
             params = (params,)


Test patch:
diff --git a/tests/test_generics.py b/tests/test_generics.py
--- a/tests/test_generics.py
+++ b/tests/test_generics.py
@@ -600,6 +600,28 @@ class Model(GenericModel, Generic[AT, BT]):
     ]
 
 
+@skip_36
+def test_generic_subclass_of_concrete_generic():
+    T = TypeVar('T')
+    U = TypeVar('U')
+
+    class GenericBaseModel(GenericModel, Generic[T]):
+        data: T
+
+    class GenericSub(GenericBaseModel[int], Generic[U]):
+        extra: U
+
+    ConcreteSub = GenericSub[int]
+
+    with pytest.raises(ValidationError):
+        ConcreteSub(data=2, extra='wrong')
+
+    with pytest.raises(ValidationError):
+        ConcreteSub(data='wrong', extra=2)
+
+    ConcreteSub(data=2, extra=3)
+
+
 @skip_36
 def test_generic_model_pickle(create_module):
     # Using create_module because pickle doesn't support