Feature: Generate .pyi files
Opened this issue · 1 comments
This first needs a design, but it should be possible to generate .pyi files for mypy and code completion.
The hard part is how to represent objective protocols, those aren't named objects in the bindings and aren't really types. This might need a mypy plugin...
Mypy doesn't understand metaclasses, and especially not those implemented in C. A good enough solution is to use regular classes in the stub files, with "@classmethod" decorators for class methods. This causes a conflict when a class has both a class and instance method of the same name, but there shouldn't be many of those.
One other possible issue: the generated files will be huge (and will get larger when the annotations get more precise), I have no idea if that will cause problems.
This causes a conflict when a class has both a class and instance method of the same name, but there shouldn't be many of those.
You can avoid this conflict by simply omitting the instance method when a class method of the same name is present; in Python you can happily call class methods on instances, so mypy will see nothing wrong with it.
If there are truly pathological cases where the methods have the same name but different types, it can even be coerced to typecheck that, with a minor hack like this (python, not pyi here, just so that you can see the interaction with the implementation, but the same principle applies):
from __future__ import annotations
from typing import ClassVar, Protocol, overload
class InstanceSignature(Protocol):
def __call__(self, arg: int) -> None:
...
class ClassSignature(Protocol):
def __call__(self, arg: str) -> None:
...
class BothMethod:
@overload
def __get__(self, instance: None, owner=None) -> ClassSignature:
...
@overload
def __get__(self, instance: Foo, owner=None) -> InstanceSignature:
...
def __get__(
self, instance: Foo | None, owner=None
) -> ClassSignature | InstanceSignature:
...
instanceMethod: InstanceSignature
classMethod: ClassSignature
if instance is None:
return instanceMethod
else:
return classMethod
class Foo:
method: ClassVar[BothMethod]
f = Foo()
f.method(3)
Foo.method("name")