Repository: pydantic/pydantic

Issue:
Discriminated unions raise TypeError for unions of a single type
### 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 is a bug

<!-- Sorry to sound so draconian, but every second saved replying to issues is time spend improving pydantic :-) -->

# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.9.0
            pydantic compiled: False
                 install path: /home/tom/reinfer/env/lib/python3.8/site-packages/pydantic
               python version: 3.8.10 (default, Nov 26 2021, 20:14:08)  [GCC 9.3.0]
                     platform: Linux-5.11.0-44-generic-x86_64-with-glibc2.29
     optional deps. installed: ['dotenv', 'typing-extensions']
```

When a `Union` with only a single variant it tagged with a field as a discriminated union, a type error is thrown on defining the class. The below is a minimally reproducible example:

```py
# repro.py
from typing import Literal, Union

from pydantic import BaseModel, Field


class DataAModel(BaseModel):
    kind: Literal["a"]


class RequestModel(BaseModel):
    data: Union[DataAModel] = Field(..., discriminator="kind")
```

produces

```bash
$ python repro.py
Traceback (most recent call last):
  File "repro.py", line 10, in <module>
    class RequestModel(BaseModel):
  File "/home/tom/r/env/lib/python3.8/site-packages/pydantic/main.py", line 204, in __new__
    fields[ann_name] = ModelField.infer(
  File "/home/tom/r/env/lib/python3.8/site-packages/pydantic/fields.py", line 488, in infer
    return cls(
  File "/home/tom/r/env/lib/python3.8/site-packages/pydantic/fields.py", line 419, in __init__
    self.prepare()
  File "/home/tom/r/env/lib/python3.8/site-packages/pydantic/fields.py", line 534, in prepare
    self._type_analysis()
  File "/home/tom/r/env/lib/python3.8/site-packages/pydantic/fields.py", line 605, in _type_analysis
    raise TypeError('`discriminator` can only be used with `Union` type')
TypeError: `discriminator` can only be used with `Union` type
```

The following code snippet works as intended. The only difference is I have introduced a second type in the discriminated union field. Note that simply repeating the existing type (such as `Union[DataAModel, DataAModel]`) still fails:

```py
from typing import Literal, Union

from pydantic import BaseModel, Field


class DataAModel(BaseModel):
    kind: Literal["a"]


class DataBModel(BaseModel):
    kind: Literal["b"]


class RequestModel(BaseModel):
    data: Union[DataAModel, DataBModel] = Field(..., discriminator="kind")
```

---

After some poking, it looks like within `_type_analysis`, `self.discriminator_key` is set on the model `DataAModel`, rather than the parent field, `Union[DataAModel]`. I'm guessing there is a simplification step somewhere where singleton unions are simplified to their child type, which is no longer accurate?

Happy to submit a PR if you can point me further in the right approach.


Comments:
Hi @tommilligan 
The thing is we can't do anything about it because python changes `Union[A]` into `A` at interpretation time.
```py
from typing import Union
class A: ...
assert Union[A] is A
```
So at runtime, pydantic has no way to know `A` was supposed to be a `Union`
Ah, that makes sense. I was about to start poking to see if the simplification was internal to `pydantic` or not, but if it's at a higher layer I'll give it up as a lost cause.

Would you accept a PR noting this as an edge case in the documentation/error message?

I suppose the workaround is to add a dummy second type into the union, or just to remove the discriminator until it is required.

For documentation forward compatibility, we were using [openapi-schema-pydantic](https://github.com/kuimono/openapi-schema-pydantic), which now collides with the `discriminator` Field property nicely. But that's not your problem! I'll file a bug over there now.
Documentation PRs are always welcome :)

Patch:
diff --git a/pydantic/fields.py b/pydantic/fields.py
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -600,7 +600,7 @@ def _type_analysis(self) -> None:  # noqa: C901 (ignore complexity)
             return
 
         if self.discriminator_key is not None and not is_union(origin):
-            raise TypeError('`discriminator` can only be used with `Union` type')
+            raise TypeError('`discriminator` can only be used with `Union` type with more than one variant')
 
         # add extra check for `collections.abc.Hashable` for python 3.10+ where origin is not `None`
         if origin is None or origin is CollectionsHashable:


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
@@ -11,12 +11,23 @@
 
 
 def test_discriminated_union_only_union():
-    with pytest.raises(TypeError, match='`discriminator` can only be used with `Union` type'):
+    with pytest.raises(
+        TypeError, match='`discriminator` can only be used with `Union` type with more than one variant'
+    ):
 
         class Model(BaseModel):
             x: str = Field(..., discriminator='qwe')
 
 
+def test_discriminated_union_single_variant():
+    with pytest.raises(
+        TypeError, match='`discriminator` can only be used with `Union` type with more than one variant'
+    ):
+
+        class Model(BaseModel):
+            x: Union[str] = Field(..., discriminator='qwe')
+
+
 def test_discriminated_union_invalid_type():
     with pytest.raises(TypeError, match="Type 'str' is not a valid `BaseModel` or `dataclass`"):