Repository: pydantic/pydantic

Issue:
<Model>.schema() method handles Enum and IntEnum default field resolution differently
### 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.8.2
            pydantic compiled: True
                 install path: C:\Users\jmartins\.virtualenvs\pydantic_bug_report-NJE4-7fw\Lib\site-packages\pydantic
               python version: 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)]
                     platform: Windows-10-10.0.19042-SP0
     optional deps. installed: ['typing-extensions']
```
<!-- or if you're using pydantic prior to v1.3, manually include: OS, python version and pydantic version -->

<!-- Please read the [docs](https://pydantic-docs.helpmanual.io/) and search through issues to
confirm your bug hasn't already been reported. -->

<!-- Where possible please include a self-contained code snippet describing your bug: -->

Generating a schema with the .schema() method works as expected when resolving default values of Enum type, while it does not resolve default values of IntEnum type the same way. A minimum example follows:

```py
# std lib imports
from enum import Enum, IntEnum

# external imports
from pydantic import BaseModel


class ExampleEnum(Enum):
    A = "a"


class ExampleIntEnum(IntEnum):
    A = 1


class ExampleModel(BaseModel):
    example_enum: ExampleEnum = ExampleEnum.A
    example_int_enum: ExampleIntEnum = ExampleIntEnum.A


generated_schema_properties = ExampleModel.schema().get("properties", {})

example_enum_generated_default = generated_schema_properties.get("example_enum", {}).get("default", None)
example_int_enum_generated_default = generated_schema_properties.get("example_int_enum", {}).get("default", None)

print(example_enum_generated_default is ExampleEnum.A.value)
# -> True

print(example_int_enum_generated_default is ExampleIntEnum.A.value)
# -> False
```

I've tracked the issue down to the `encode_default` function in `schema.py`:

```py
def encode_default(dft: Any) -> Any:
    if isinstance(dft, (int, float, str)):
        return dft
    elif sequence_like(dft):
        t = dft.__class__
        return t(encode_default(v) for v in dft)
    elif isinstance(dft, dict):
        return {encode_default(k): encode_default(v) for k, v in dft.items()}
    elif dft is None:
        return None
    else:
        return pydantic_encoder(dft)
```

When resolving defaults for Enum the else clause is correctly used, but since `isinstance(ExampleIntEnum.A, int)` is truthy it returns ExampleIntEnum.A when using an IntEnum. I would suggest changing the first if to a stricter direct 'primitive' type check like `if type(dft) in (int, float, str):`. 

I can do this myself and open a PR if there is interest and no opposition to a stricter type check.


Discussion:

Resolving patch:
diff --git a/pydantic/schema.py b/pydantic/schema.py
--- a/pydantic/schema.py
+++ b/pydantic/schema.py
@@ -909,7 +909,9 @@ def multitypes_literal_field_for_schema(values: Tuple[Any, ...], field: ModelFie
 
 
 def encode_default(dft: Any) -> Any:
-    if isinstance(dft, (int, float, str)):
+    if isinstance(dft, Enum):
+        return dft.value
+    elif isinstance(dft, (int, float, str)):
         return dft
     elif sequence_like(dft):
         t = dft.__class__


Test patch:
diff --git a/tests/test_schema.py b/tests/test_schema.py
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -1404,6 +1404,26 @@ class UserModel(BaseModel):
     }
 
 
+def test_enum_str_default():
+    class MyEnum(str, Enum):
+        FOO = 'foo'
+
+    class UserModel(BaseModel):
+        friends: MyEnum = MyEnum.FOO
+
+    assert UserModel.schema()['properties']['friends']['default'] is MyEnum.FOO.value
+
+
+def test_enum_int_default():
+    class MyEnum(IntEnum):
+        FOO = 1
+
+    class UserModel(BaseModel):
+        friends: MyEnum = MyEnum.FOO
+
+    assert UserModel.schema()['properties']['friends']['default'] is MyEnum.FOO.value
+
+
 def test_dict_default():
     class UserModel(BaseModel):
         friends: Dict[str, float] = {'a': 1.1, 'b': 2.2}