Repository: pydantic/pydantic

Issue:
support for built-in `dataclass` breaks pickle
### 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.7.2
            pydantic compiled: True
                 install path: /Users/aimestereo/.pyenv/versions/3.7.6/envs/feed/lib/python3.7/site-packages/pydantic
               python version: 3.7.6 (default, Jan 30 2020, 16:07:03)  [Clang 11.0.0 (clang-1100.0.33.17)]
                     platform: Darwin-19.6.0-x86_64-i386-64bit
     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: -->

thanks to @PrettyWood 1.7 Introduced cool [support of built-in dataclass](https://github.com/samuelcolvin/pydantic/pull/1817). But in our project this broke pickle

```py
import pickle
import dataclasses

import pydantic


@dataclasses.dataclass
class BuiltInDataclass:
    value: int


class PydanticModel(pydantic.BaseModel):
    built_in_dataclass: BuiltInDataclass


print(pickle.dumps(
    PydanticModel(built_in_dataclass=BuiltInDataclass(value=0))
))
```

output:
```
---------------------------------------------------------------------------
PicklingError                             Traceback (most recent call last)
<ipython-input-14-7d67a44cf353> in <module>
     15
     16 print(pickle.dumps(
---> 17     PydanticModel(built_in_dataclass=BuiltInDataclass(value=0))
     18 ))

PicklingError: Can't pickle <class '__main__.BuiltInDataclass'>: it's not the same object as __main__.BuiltInDataclass
```

---

For better understanding here's an issue of pickle outside of pydantic context. This is an old pickle problem, when different classes have the same name, simple way to reproduce:
```
import pickle


class A: pass


# this is an analogy of how pydantic convert built-in dataclass
B = type(A.__name__, (A,), {})


print(A)
# <class '__main__.A'>

print(B)
# <class '__main__.A'>

pickle.dumps(A)
# b'\x80\x03c__main__\nA\nq\x00.'

pickle.dumps(B)
# error
```
output
```
---------------------------------------------------------------------------
PicklingError                             Traceback (most recent call last)
<ipython-input-2-b97d2c67736c> in <module>
     10
     11 print(pickle.dumps(A))
---> 12 print(pickle.dumps(B))

PicklingError: Can't pickle <class '__main__.A'>: it's not the same object as __main__.A
```

---

As a result we can't use this feature, we're stick to intermediate classes that converts buit-in dataclasses to pydantic:

```
class SerializedDataclass(pydantic.BaseModel):
	# copy/paste of all fields from built-in dataclass
    value: float

```
Required optional field has a default value of None after v1.7.0
### 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.7.2
            pydantic compiled: False
                 install path: /home/leovp/Projects/pydantic/pydantic
               python version: 3.6.12 (default, Sep  5 2020, 11:22:16)  [GCC 7.5.0]
                     platform: Linux-4.15.0-54-generic-x86_64-with-debian-buster-sid
     optional deps. installed: []
```
<!-- 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: -->

```py
from typing import Optional

from pydantic import BaseModel


class Model(BaseModel):
    foo: int
    bar: Optional[int] = ...


m = Model(foo=1, bar=None)  # or Model(foo=1)
Model.parse_obj(m.dict(exclude_defaults=True))  # fails on pydantic v1.7.0+
```

It seems that `pydantic` thinks `bar` (a "required optional" field) has a default value of None and serializes the model without this key, which makes it non-deserializable. This was not the case on versions 1.5.x and 1.6.x, so I think it's a regression. (Or maybe it was never a guarantee).

According to `git bisect` the potential regression was introduced in commit 4bc4230df6294c62667fc3779328019a28912147.
Extra keyword arguments passed to fields based on `Enum` are not propagated to the schema.
### 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.7.2
            pydantic compiled: True
                 install path: /usr/local/lib/python3.7/site-packages/pydantic
               python version: 3.7.9 (default, Oct 13 2020, 20:59:51)  [GCC 8.3.0]
                     platform: Linux-4.15.0-122-generic-x86_64-with-debian-10.6
     optional deps. installed: ['typing-extensions', 'email-validator']
```
<!-- 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: -->

## Detail

The bug is a regression, demonstrated with the following script.

```py
from enum import Enum
from typing import List
from pydantic import BaseModel, Field


class FooEnum(str, Enum):
    foo = "foo"
    bar = "bar"
    baz = "baz"


class FoosModel(BaseModel):
    foos: List[FooEnum] = Field(examples=[["foo", "bar"]])


if __name__ == "__main__":
    print(FoosModel.schema())
```

This is the difference between outputs between `pydantic==1.6.1` and `pydantic==1.7.2` (the problem was introduced in `pydantic==1.7.0`). 

```diff
{
    "title": "FoosModel",
    "type": "object",
    "properties": {
        "foos": {
+           "title": "Foos",
+           "examples": [["foo", "bar"]],
            "type": "array",
            "items": {"$ref": "#/definitions/FooEnum"},
        }
    },
    "required": ["foos"],
    "definitions": {
        "FooEnum": {
            "title": "FooEnum",
            "description": "An enumeration.",
            "enum": ["foo", "bar", "baz"],
            "type": "string",
        }
    },
}
```

The problem arises because the function which adds the extra fields to the field schema now drops out early for Enum fields: https://github.com/samuelcolvin/pydantic/blob/31bc2435d7968fdf8d1f0c5c67f0f851c1bef54e/pydantic/schema.py#L262-L264 and therefore never runs the later code which attaches the extra fields:
https://github.com/samuelcolvin/pydantic/blob/31bc2435d7968fdf8d1f0c5c67f0f851c1bef54e/pydantic/schema.py#L278-L279

I'm preparing a merge request which will fix this.


Discussion:

Resolving patch:
diff --git a/pydantic/utils.py b/pydantic/utils.py
--- a/pydantic/utils.py
+++ b/pydantic/utils.py
@@ -646,7 +646,8 @@ def is_valid_private_name(name: str) -> bool:
 
 
 def all_identical(left: Iterable[Any], right: Iterable[Any]) -> bool:
-    """Check that the items of `left` are the same objects as those in `right`.
+    """
+    Check that the items of `left` are the same objects as those in `right`.
 
     >>> a, b = object(), object()
     >>> all_identical([a, b, a], [a, b, a])
diff --git a/pydantic/version.py b/pydantic/version.py
--- a/pydantic/version.py
+++ b/pydantic/version.py
@@ -1,6 +1,6 @@
 __all__ = 'VERSION', 'version_info'
 
-VERSION = '1.7.2'
+VERSION = '1.7.3'
 
 
 def version_info() -> str:


Test patch: