python/mypy

Export types from a plugin as `pyi`

Opened this issue · 0 comments

Feature

Add a way to export generated types from a plugin (like mypy_django_plugin) as pyi. So they can be used by other type checkers.

Pitch

Few days ago i started an effort to migrate types from mongo-types into core mongoengine, and i need to solve a problem:

Each field can accept and return either T or T | None, depending on the required property.

In the mongo-types it was solved by adding __new__ overloads for each field, and it works well, but clutters the codebase.
Similar problem exists for Django, and django-stubs are solving this by writing a mypy plugin, that changes __set__ and __get__ descriptors for a field, depending on the init argumens.

Can this type information be exported? For example, for a django app/models.py plugin would generate typings/mypy/app/models.pyi with all the added types. I would run it like mypy export-plugin-types or something similar, when my code changes.

Other options

If there's another way to solve this problem, please help me.

My last attempt looks like this (but it returns BaseField, and not the child class like StringField. And when init arguments are changing, i need to redefine those overloads for a field specifically):

from typing import Literal, Optional, TypeVar, Union, overload

from typing import Any, Callable, Generic

_ST = TypeVar("_ST")
_GT = TypeVar("_GT")

class BaseField(Generic[_ST, _GT]):
    @overload
    def __new__(
        cls,
        *args: Any,
        required: Literal[False] = ...,
        default: None = ...,
        **kwargs: Any,
    ) -> BaseField[Optional[_ST], Optional[_GT]]: ...
    # BaseField()
    @overload
    def __new__(
        cls,
        *args: Any,
        required: Literal[False] = ...,
        default: Union[str, Callable[[], str]],
        **kwargs: Any,
    ) -> BaseField[Optional[_ST], _GT]: ...
    # BaseField(required=False, default="foo")
    @overload
    def __new__(
        cls,
        *args: Any,
        **kwargs: Any,
    ) -> BaseField[_ST, _GT]: ...
    # BaseField(required=True)
    def __set__(self, instance: Any, value: _ST) -> None: ...
    def __get__(self, instance: Any, owner: Any) -> _GT: ...