Repository: pydantic/pydantic

Issue:
Regression in OpenAPI schema generation with multiple Enums having the same name but different enum members
# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.6.1
            pydantic compiled: True
                 install path: /Users/philipp/Downloads/env/lib/python3.7/site-packages/pydantic
               python version: 3.7.6 (default, Dec 30 2019, 19:38:28)  [Clang 11.0.0 (clang-1100.0.33.16)]
                     platform: Darwin-19.3.0-x86_64-i386-64bit
     optional deps. installed: []
```

At first, thank you all for this incredible piece of software!

As of #1432 enums are included as separate models and and are referenced in all locations. In the case of multiple enums with the same name but different enum members, located in different modules, only one of these enums is included in the OpenAPI schema.

File `other.py`:

```py
from enum import Enum

from pydantic import BaseModel


class SomeEnum(str, Enum):
    d = "d"
    e = "e"
    f = "f"


class SomeOtherModel(BaseModel):
    some_enum: SomeEnum
```

File `main.py`:

```py
from enum import Enum

from pydantic import BaseModel

from other import SomeOtherModel


class SomeEnum(str, Enum):
    a = "a"
    b = "b"
    c = "c"


class MainModel(BaseModel):
    some_enum: SomeEnum
    some_other_model: SomeOtherModel


if __name__ == "__main__":
    print(MainModel.schema_json())
```

Executing `main.py` produces the following schema:

```
{
  "title": "MainModel",
  "type": "object",
  "properties": {
    "some_enum": {
      "$ref": "#/definitions/SomeEnum"
    },
    "some_other_model": {
      "$ref": "#/definitions/SomeOtherModel"
    }
  },
  "required": [
    "some_enum",
    "some_other_model"
  ],
  "definitions": {
    "SomeEnum": {
      "title": "SomeEnum",
      "description": "An enumeration.",
      "enum": [
        "d",
        "e",
        "f"
      ],
      "type": "string"
    },
    "SomeOtherModel": {
      "title": "SomeOtherModel",
      "type": "object",
      "properties": {
        "some_enum": {
          "$ref": "#/definitions/SomeEnum"
        }
      },
      "required": [
        "some_enum"
      ]
    }
  }
}
```

Only the Enum from `other.py` is included and the schema now contains invalid supported enum values for `MainModel.some_enum`.

Before #1432 this works as expected because the enum values are included as-is. Ideally `pydantic` should handle this case by prefixing different enums with the same name in the OpenAPI schema. I've applied a workaround to my application code by changing the name of conflicting enums but I only detected this new behaviour by having a CI Pipeline step that checks for breaking OpenAPI changes.


Discussion:

Resolving patch:
diff --git a/pydantic/schema.py b/pydantic/schema.py
--- a/pydantic/schema.py
+++ b/pydantic/schema.py
@@ -781,7 +781,7 @@ def field_singleton_schema(  # noqa: C901 (ignore complexity)
         f_schema['const'] = literal_value
 
     if lenient_issubclass(field_type, Enum):
-        enum_name = normalize_name(field_type.__name__)
+        enum_name = model_name_map[field_type]
         f_schema, schema_overrides = get_field_info_schema(field)
         f_schema.update(get_schema_ref(enum_name, ref_prefix, ref_template, schema_overrides))
         definitions[enum_name] = enum_process_schema(field_type)


Test patch:
diff --git a/tests/test_schema.py b/tests/test_schema.py
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -2097,3 +2097,55 @@ class Model(BaseModel):
         'properties': {'a': {'title': 'A', 'type': 'string'}},
         'required': ['a'],
     }
+
+
+def test_multiple_enums_with_same_name(create_module):
+    module_1 = create_module(
+        # language=Python
+        """
+from enum import Enum
+
+from pydantic import BaseModel
+
+
+class MyEnum(str, Enum):
+    a = 'a'
+    b = 'b'
+    c = 'c'
+
+
+class MyModel(BaseModel):
+    my_enum_1: MyEnum
+        """
+    )
+
+    module_2 = create_module(
+        # language=Python
+        """
+from enum import Enum
+
+from pydantic import BaseModel
+
+
+class MyEnum(str, Enum):
+    d = 'd'
+    e = 'e'
+    f = 'f'
+
+
+class MyModel(BaseModel):
+    my_enum_2: MyEnum
+        """
+    )
+
+    class Model(BaseModel):
+        my_model_1: module_1.MyModel
+        my_model_2: module_2.MyModel
+
+    assert len(Model.schema()['definitions']) == 4
+    assert set(Model.schema()['definitions']) == {
+        f'{module_1.__name__}__MyEnum',
+        f'{module_1.__name__}__MyModel',
+        f'{module_2.__name__}__MyEnum',
+        f'{module_2.__name__}__MyModel',
+    }