Skip to content
17 changes: 9 additions & 8 deletions stdlib/abc.pyi
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
from typing import Any, Callable, Type, TypeVar
from typing import Any, Callable, Generic, Type, TypeVar

_T = TypeVar("_T")
_FuncT = TypeVar("_FuncT", bound=Callable[..., Any])

# These definitions have special processing in mypy
class ABCMeta(type):
def register(cls: ABCMeta, subclass: Type[_T]) -> Type[_T]: ...

def abstractmethod(funcobj: _FuncT) -> _FuncT: ...

class abstractclassmethod(classmethod[_FuncT], Generic[_FuncT]):
def __init__(self, callable: _FuncT) -> None: ...

class abstractstaticmethod(staticmethod[_FuncT], Generic[_FuncT]):
def __init__(self, callable: _FuncT) -> None: ...
Comment thread
srittau marked this conversation as resolved.
Resolved

class abstractproperty(property): ...

# These two are deprecated and not supported by mypy
def abstractstaticmethod(callable: _FuncT) -> _FuncT: ...
def abstractclassmethod(callable: _FuncT) -> _FuncT: ...
class ABCMeta(type):
def register(cls: ABCMeta, subclass: Type[_T]) -> Type[_T]: ...

class ABC(metaclass=ABCMeta): ...

Expand Down
19 changes: 10 additions & 9 deletions stdlib/builtins.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ from _typeshed import (
SupportsWrite,
)
from ast import AST, mod
from collections.abc import Callable
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOWrapper
from types import CodeType, TracebackType
from typing import (
Expand All @@ -26,7 +27,6 @@ from typing import (
AsyncIterator,
BinaryIO,
ByteString,
Callable,
Dict,
FrozenSet,
Generic,
Expand Down Expand Up @@ -80,6 +80,7 @@ _T4 = TypeVar("_T4")
_T5 = TypeVar("_T5")
_TT = TypeVar("_TT", bound="type")
_TBE = TypeVar("_TBE", bound="BaseException")
_FuncT = TypeVar("_FuncT", bound=Callable[..., Any])

class object:
__doc__: Optional[str]
Expand Down Expand Up @@ -109,19 +110,19 @@ class object:
def __dir__(self) -> Iterable[str]: ...
def __init_subclass__(cls) -> None: ...

class staticmethod(object): # Special, only valid as a decorator.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: These comments should be deleted. They seem misleading and mypy-specific to me.

__func__: Callable[..., Any]
class staticmethod(Generic[_FuncT]): # Special, only valid as a decorator.
__func__: _FuncT
__isabstractmethod__: bool
def __init__(self, f: Callable[..., Any]) -> None: ...
def __init__(self, f: _FuncT) -> None: ...
def __new__(cls: Type[_T], *args: Any, **kwargs: Any) -> _T: ...
def __get__(self, obj: _T, type: Optional[Type[_T]] = ...) -> Callable[..., Any]: ...
def __get__(self, obj: _T, type: Type[_T] | None = ...) -> _FuncT: ...

class classmethod(object): # Special, only valid as a decorator.
__func__: Callable[..., Any]
class classmethod(Generic[_FuncT]): # Special, only valid as a decorator.
__func__: _FuncT
__isabstractmethod__: bool
def __init__(self, f: Callable[..., Any]) -> None: ...
def __init__(self, f: _FuncT) -> None: ...
def __new__(cls: Type[_T], *args: Any, **kwargs: Any) -> _T: ...
def __get__(self, obj: _T, type: Optional[Type[_T]] = ...) -> Callable[..., Any]: ...
def __get__(self, obj: _T, type: Type[_T] | None = ...) -> _FuncT: ...

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem right, because the callable returned from __get__ doesn't take the class as a first argument.

>>> class Foo:
...     @classmethod
...     def bar(cls):
...             print("Bar runs:", cls)
... 
>>> Foo.bar()
Bar runs: <class '__main__.Foo'>
>>> Foo.__dict__['bar'].__func__(Foo)
Bar runs: <class '__main__.Foo'>

Here Foo.__dict__['bar'] is the classmethod object, and for Foo.bar, its __get__ method gets called. The result is a bound method, which basically means that it includes the class similarly to partial:

>>> Foo.bar == Foo.__dict__['bar'].__get__(Foo(), None)
True
>>> Foo.bar
<bound method Foo.bar of <class '__main__.Foo'>>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another problem: I think it calls __get__(None, Foo) when looking up Foo.bar, so None should be allowed for the first argument to __get__.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I tried to express this using ParamSpec, but I'm not sure this is correct. Also, do you know in what circumstances the owner argument can be None or not set?

@Akuli Akuli Jun 28, 2021 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears to be a technicality that doesn't really occur in practice: "Python’s own __getattribute__() implementation always passes in both arguments whether they are required or not." Docs


class type(object):
__base__: type
Expand Down