ronaldoussoren/objective.metadata

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.

glyph commented

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")