Repository: pydantic/pydantic

Issue:
Field with discriminator on generic causes `AssertionError`. (1.9.0a1)

# Bug

When defining a Generic, with a `Field` that has descriminator, filling in the type variable of the generic class fails with an ``AssertionError``.


Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.9.0a1
            pydantic compiled: True
                 install path: /home/jonathan/.pyenv/versions/3.9.5/envs/lazy-maestro/lib/python3.9/site-packages/pydantic
               python version: 3.9.5 (default, Jun  3 2021, 16:26:08)  [GCC 9.3.0]
                     platform: Linux-5.4.0-91-generic-x86_64-with-glibc2.31
     optional deps. installed: ['dotenv', 'typing-extensions']
```

```py
from typing import Generic, TypeVar, Union

from pydantic import Field
from pydantic.generics import BaseModel, GenericModel
from typing_extensions import Literal

_T = TypeVar("_T")

class Success(GenericModel, Generic[_T]):
    type: Literal["Success"] = "Success"

    data: _T

class Failure(BaseModel):
    type: Literal["Failure"] = "Failure"
    error_message: str

class Container(GenericModel, Generic[_T]):
    result: Union[Success[_T], Failure] = Field(discriminator="type")

# It's the following expresison that raises the traceback below:
Container[str]
```

The error:
```
Traceback (most recent call last):
  File "/tmp/test.py", line 25, in <module>
    Container[str]
  File "/home/jonathan/.pyenv/versions/lazy-maestro/lib/python3.9/site-packages/pydantic/generics.py", line 95, in __class_getitem__
    create_model(
  File "pydantic/main.py", line 959, in pydantic.main.create_model
  File "pydantic/main.py", line 291, in pydantic.main.ModelMetaclass.__new__
  File "pydantic/main.py", line 762, in pydantic.main.BaseModel.__try_update_forward_refs__
  File "pydantic/typing.py", line 477, in pydantic.typing.update_model_forward_refs
    literal (or one of several literals):
  File "pydantic/typing.py", line 456, in pydantic.typing.update_field_forward_refs
    parameters = tuple(_type_check(p, msg) for p in parameters)
  File "pydantic/fields.py", line 735, in pydantic.fields.ModelField.prepare_discriminated_union_sub_fields
AssertionError
```



Comments:

Patch:
diff --git a/pydantic/fields.py b/pydantic/fields.py
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -732,6 +732,10 @@ def prepare_discriminated_union_sub_fields(self) -> None:
         Note that this process can be aborted if a `ForwardRef` is encountered
         """
         assert self.discriminator_key is not None
+
+        if self.type_.__class__ is DeferredType:
+            return
+
         assert self.sub_fields is not None
         sub_fields_mapping: Dict[str, 'ModelField'] = {}
         all_aliases: Set[str] = set()


Test patch:
diff --git a/tests/test_discrimated_union.py b/tests/test_discrimated_union.py
--- a/tests/test_discrimated_union.py
+++ b/tests/test_discrimated_union.py
@@ -1,12 +1,14 @@
 import re
+import sys
 from enum import Enum
-from typing import Union
+from typing import Generic, TypeVar, Union
 
 import pytest
 from typing_extensions import Annotated, Literal
 
 from pydantic import BaseModel, Field, ValidationError
 from pydantic.errors import ConfigError
+from pydantic.generics import GenericModel
 
 
 def test_discriminated_union_only_union():
@@ -361,3 +363,36 @@ class Model(BaseModel):
         n: int
 
     assert isinstance(Model(**{'pet': {'pet_type': 'dog', 'name': 'Milou'}, 'n': 5}).pet, Dog)
+
+
+@pytest.mark.skipif(sys.version_info < (3, 7), reason='generics only supported for python 3.7 and above')
+def test_generic():
+    T = TypeVar('T')
+
+    class Success(GenericModel, Generic[T]):
+        type: Literal['Success'] = 'Success'
+        data: T
+
+    class Failure(BaseModel):
+        type: Literal['Failure'] = 'Failure'
+        error_message: str
+
+    class Container(GenericModel, Generic[T]):
+        result: Union[Success[T], Failure] = Field(discriminator='type')
+
+    with pytest.raises(ValidationError, match="Discriminator 'type' is missing in value"):
+        Container[str].parse_obj({'result': {}})
+
+    with pytest.raises(
+        ValidationError,
+        match=re.escape("No match for discriminator 'type' and value 'Other' (allowed values: 'Success', 'Failure')"),
+    ):
+        Container[str].parse_obj({'result': {'type': 'Other'}})
+
+    with pytest.raises(
+        ValidationError, match=re.escape('Container[str]\nresult -> Success[str] -> data\n  field required')
+    ):
+        Container[str].parse_obj({'result': {'type': 'Success'}})
+
+    # coercion is done properly
+    assert Container[str].parse_obj({'result': {'type': 'Success', 'data': 1}}).result.data == '1'