Skip to content

type builtin function should return an instance of typing.Type? #1758

@Michael0x2a

Description

@Michael0x2a

I've run into a snippit of code which essentially boils down into this:

class SomeObject(object):
    @classmethod
    def test_class_method(cls) -> None:
        print('Test class method')

    @staticmethod
    def test_static_method() -> None:
        print('Test static method')

s = SomeObject()
t = type(s)

# Doesn't typecheck
t.test_static_method()
t.test_class_method()

The behavior I was expecting was for t to be of type Type[SomeObject] -- instead, it's of type type, due to how type is annotated in Typeshed:

class type:
    __bases__ = ...  # type: Tuple[type, ...]
    __name__ = ...  # type: str
    __qualname__ = ...  # type: str
    __module__ = ...  # type: str
    __dict__ = ...  # type: Dict[str, Any]
    __mro__ = ...  # type: Tuple[type, ...]

    @overload
    def __init__(self, o: object) -> None: ...
    @overload
    def __init__(self, name: str, bases: Tuple[type, ...], dict: Dict[str, Any]) -> None: ...
    @overload
    def __new__(cls, o: object) -> type: ...
    @overload
    def __new__(cls, name: str, bases: Tuple[type, ...], namespace: Dict[str, Any]) -> type: ...
    def __call__(self, *args: Any, **kwds: Any) -> Any: ...
    def __subclasses__(self) -> List[type]: ...
    # Note: the documentation doesn't specify what the return type is, the standard
    # implementation seems to be returning a list.
    def mro(self) -> List[type]: ...

I attempted modifying the definition in Typeshed by first changing __new__ to have the signature def __new__(cls, o: _T) -> Type[_T], but that had no effect. I then tried commenting out both __init__s entirely in case mypy was defaulting to looking at those, but that also did nothing. I'm not entirely sure why the change to __new__ isn't working, though it may be related to issue 1020?

I did find a workaround for this particular case by doing:

from typing import cast, Type

# ...snip...

s = SomeObject()
t2 = cast(Type[SomeObject], type(s))

# typechecks
t2.test_static_method()
t2.test_class_method()

...but it would be nice if type could be changed so this casting is unnecessary, though I'm not entirely sure how this would be done.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions