Repository: pydantic/pydantic

Issue:
Mypy plugin crashes when encountering `from_orm` method on user's classes
### Initial Checks

- [X] I have searched GitHub for a duplicate issue and I'm sure this is something new
- [X] I have searched Google & StackOverflow for a solution and couldn't find anything
- [X] I have read and followed [the docs](https://docs.pydantic.dev) and still think this is a bug
- [X] I am confident that the issue is with pydantic (not my code, or another library in the ecosystem like [FastAPI](https://fastapi.tiangolo.com) or [mypy](https://mypy.readthedocs.io/en/stable))


### Description

I have a `OrmMixin` (provided in example code) to help with mapping `sqlalchemy` models into `strawberry-graphql` models.

Mypy plugin seems  to have problems with `from_orm` method and works fine if it's renamed, for example into `from_orm_`.

```sh
$ mypy main.py
main.py:19: error: The pydantic mypy plugin ran into unexpected behavior: ctx.type: Type[main.OrmMixin[_TModel`1, _TType`2]] (of type TypeType)
Please consider reporting this bug at https://github.com/pydantic/pydantic/issues/new/choose so we can try to fix it!  [pydantic-unexpected]       
main.py:23: error: The pydantic mypy plugin ran into unexpected behavior: ctx.type: Type[main.OrmMixin[_TModel`1, _TType`2]] (of type TypeType)
Please consider reporting this bug at https://github.com/pydantic/pydantic/issues/new/choose so we can try to fix it!  [pydantic-unexpected]       
Found 2 errors in 1 file (checked 1 source file)
```

### Example Code

```Python
import abc
from collections.abc import Iterable
from typing import Generic, TypeVar

_TModel = TypeVar("_TModel")
_TType = TypeVar("_TType")


class OrmMixin(Generic[_TModel, _TType]):
    @classmethod
    @abc.abstractmethod
    def from_orm(cls, model: _TModel) -> _TType:
        raise NotImplementedError

    @classmethod
    def from_orm_optional(cls, model: _TModel | None) -> _TType | None:
        if model is None:
            return None
        return cls.from_orm(model)

    @classmethod
    def from_orm_list(cls, models: Iterable[_TModel]) -> list[_TType]:
        return [cls.from_orm(model) for model in models]
```


### Python, Pydantic & OS Version

```Text
pydantic version: 1.10.6
pydantic compiled: True
install path: .venv\Lib\site-packages\pydantic
python version: 3.11.1 (tags/v3.11.1:a7a450f, Dec  6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)]
platform: Windows-10-10.0.19044-SP0
optional deps. installed: ['typing-extensions']
```


### Affected Components

- [ ] [Compatibility between releases](https://docs.pydantic.dev/changelog/)
- [ ] [Data validation/parsing](https://docs.pydantic.dev/usage/models/#basic-model-usage)
- [ ] [Data serialization](https://docs.pydantic.dev/usage/exporting_models/) - `.dict()` and `.json()`
- [ ] [JSON Schema](https://docs.pydantic.dev/usage/schema/)
- [ ] [Dataclasses](https://docs.pydantic.dev/usage/dataclasses/)
- [ ] [Model Config](https://docs.pydantic.dev/usage/model_config/)
- [ ] [Field Types](https://docs.pydantic.dev/usage/types/) - adding or changing a particular data type
- [ ] [Function validation decorator](https://docs.pydantic.dev/usage/validation_decorator/)
- [ ] [Generic Models](https://docs.pydantic.dev/usage/models/#generic-models)
- [ ] [Other Model behaviour](https://docs.pydantic.dev/usage/models/) - `construct()`, pickling, private attributes, ORM mode
- [X] [Plugins](https://docs.pydantic.dev/) and integration with other tools - mypy, FastAPI, python-devtools, Hypothesis, VS Code, PyCharm, etc.


Discussion:
I'm having to just `# type: ignore` these lines for now, which seems to stop the crash at least. Thanks for reporting!
Can you share some code showing how you are using it to produce this error?
I don't get this error running mypy over the code in the snippet you've shared; however, I looked into it a bit and I see where it needs handling, and I think I it should be straightforward to fix if you can give me some code that shows how to reproduce the issue.
Oh also — could you share what version of mypy you are using? (You can find this by running `mypy --version`)
```sh
$ mypy --version
mypy 1.1.1 (compiled: yes)
```
It errors out on provided code:
```py
import abc
from collections.abc import Iterable
from typing import Generic, TypeVar

_TModel = TypeVar("_TModel")
_TType = TypeVar("_TType")


class OrmMixin(Generic[_TModel, _TType]):
    @classmethod
    @abc.abstractmethod
    def from_orm(cls, model: _TModel) -> _TType:
        raise NotImplementedError

    @classmethod
    def from_orm_optional(cls, model: _TModel | None) -> _TType | None:
        if model is None:
            return None
        return cls.from_orm(model)

    @classmethod
    def from_orm_list(cls, models: Iterable[_TModel]) -> list[_TType]:
        return [cls.from_orm(model) for model in models]
```
Mypy config:
```toml
[tool.mypy]
python_version = "3.11"
plugins = [
    "pydantic.mypy",
]

check_untyped_defs = true
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
disallow_any_generics = true
no_implicit_optional = true

warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
warn_unreachable = true

strict_optional = true
strict_equality = true
```
```toml
# pyproject.toml
dependencies = [
    "mypy>=1.1.1",
    "pydantic>=1.10.6",
]
```
```sh
$ pip freeze
mypy==1.1.1
mypy-extensions==1.0.0
pydantic==1.10.6
typing_extensions==4.5.0
```
see #5192
Same behavior with 1.0.1

Resolving patch:
diff --git a/pydantic/mypy.py b/pydantic/mypy.py
--- a/pydantic/mypy.py
+++ b/pydantic/mypy.py
@@ -261,12 +261,15 @@ def from_orm_callback(ctx: MethodContext) -> Type:
     Raise an error if orm_mode is not enabled
     """
     model_type: Instance
-    if isinstance(ctx.type, CallableType) and isinstance(ctx.type.ret_type, Instance):
-        model_type = ctx.type.ret_type  # called on the class
-    elif isinstance(ctx.type, Instance):
-        model_type = ctx.type  # called on an instance (unusual, but still valid)
+    ctx_type = ctx.type
+    if isinstance(ctx_type, TypeType):
+        ctx_type = ctx_type.item
+    if isinstance(ctx_type, CallableType) and isinstance(ctx_type.ret_type, Instance):
+        model_type = ctx_type.ret_type  # called on the class
+    elif isinstance(ctx_type, Instance):
+        model_type = ctx_type  # called on an instance (unusual, but still valid)
     else:  # pragma: no cover
-        detail = f'ctx.type: {ctx.type} (of type {ctx.type.__class__.__name__})'
+        detail = f'ctx.type: {ctx_type} (of type {ctx_type.__class__.__name__})'
         error_unexpected_behavior(detail, ctx.api, ctx.context)
         return ctx.default_return_type
     pydantic_metadata = model_type.type.metadata.get(METADATA_KEY)


Test patch:
diff --git a/tests/mypy/modules/plugin_success.py b/tests/mypy/modules/plugin_success.py
--- a/tests/mypy/modules/plugin_success.py
+++ b/tests/mypy/modules/plugin_success.py
@@ -237,3 +237,19 @@ class MyDataClass:
 
 
 MyDataClass(foo='foo', bar='bar')
+
+
+_TModel = TypeVar('_TModel')
+_TType = TypeVar('_TType')
+
+
+class OrmMixin(Generic[_TModel, _TType]):
+    @classmethod
+    def from_orm(cls, model: _TModel) -> _TType:
+        raise NotImplementedError
+
+    @classmethod
+    def from_orm_optional(cls, model: Optional[_TModel]) -> Optional[_TType]:
+        if model is None:
+            return None
+        return cls.from_orm(model)