Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

builtins: preparation for having the number types derive from the numbers ABCs #3108

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions stdlib/2/__builtin__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ class int:
def __rtruediv__(self, x: int) -> float: ...
def __rmod__(self, x: int) -> int: ...
def __rdivmod__(self, x: int) -> Tuple[int, int]: ...
def __pow__(self, x: int) -> Any: ... # Return type can be int or float, depending on x.
def __pow__(self, x: int, modulo: Optional[int] = ...) -> Any: ... # Return type can be int or float, depending on x.
Copy link
Member

Choose a reason for hiding this comment

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

The arguments are actually positional-only

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there some special way to encode this?

Copy link
Member

Choose a reason for hiding this comment

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

Start the names with two underscores, like __modulo.

def __rpow__(self, x: int) -> Any: ...
def __and__(self, n: int) -> int: ...
def __or__(self, n: int) -> int: ...
Expand All @@ -185,6 +185,9 @@ class int:
def __invert__(self) -> int: ...
if sys.version_info >= (3,):
def __round__(self, ndigits: Optional[int] = ...) -> int: ...
def __trunc__(self) -> int: ...
def __floor__(self) -> int: ...
def __ceil__(self) -> int: ...
def __getnewargs__(self) -> Tuple[int]: ...

def __eq__(self, x: object) -> bool: ...
Expand Down Expand Up @@ -242,11 +245,12 @@ class float:
def __getnewargs__(self) -> Tuple[float]: ...
if sys.version_info >= (3,):
@overload
def __round__(self) -> int: ...
@overload
def __round__(self, ndigits: None) -> int: ...
def __round__(self, ndigits: None = ...) -> int: ...
@overload
def __round__(self, ndigits: int) -> float: ...
def __trunc__(self) -> int: ...
def __floor__(self) -> int: ...
def __ceil__(self) -> int: ...
Copy link
Collaborator

Choose a reason for hiding this comment

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

The last two don't seem to exist (which surprised me):

Python 3.7.3 (default, Jun  3 2019, 18:27:33) 
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x = 3.0
>>> x.__trunc__()
3
>>> x.__floor__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'float' object has no attribute '__floor__'
>>> x.__ceil__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'float' object has no attribute '__ceil__'

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, should have checked all of them.

I looked into cpython code, and what I understand is that math.floor (same for math.ceil) has a special case for floats, or rather types that can be converted to float (implement __float__()). In these cases, if __floor__ is not defined, it converts to float and does the operation directly.

import math

class X:
    def __float__(self):
        return 5.4

    #def __floor__(self):
    #    return 10

print(math.floor(X()))

Commented it prints 5, uncommented it prints 10.

So I think that, given numbers.Real already requires __float__, we can just diverge from the actual definition of numbers.Real and remove __floor__ and __ceil__ from it in typeshed. WDYT? The numbers.Real doc only requires math.floor to work.


def __eq__(self, x: object) -> bool: ...
def __ne__(self, x: object) -> bool: ...
Expand Down
12 changes: 8 additions & 4 deletions stdlib/2and3/builtins.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ class int:
def __rtruediv__(self, x: int) -> float: ...
def __rmod__(self, x: int) -> int: ...
def __rdivmod__(self, x: int) -> Tuple[int, int]: ...
def __pow__(self, x: int) -> Any: ... # Return type can be int or float, depending on x.
def __pow__(self, x: int, modulo: Optional[int] = ...) -> Any: ... # Return type can be int or float, depending on x.
def __rpow__(self, x: int) -> Any: ...
def __and__(self, n: int) -> int: ...
def __or__(self, n: int) -> int: ...
Expand All @@ -185,6 +185,9 @@ class int:
def __invert__(self) -> int: ...
if sys.version_info >= (3,):
def __round__(self, ndigits: Optional[int] = ...) -> int: ...
def __trunc__(self) -> int: ...
def __floor__(self) -> int: ...
def __ceil__(self) -> int: ...
def __getnewargs__(self) -> Tuple[int]: ...

def __eq__(self, x: object) -> bool: ...
Expand Down Expand Up @@ -242,11 +245,12 @@ class float:
def __getnewargs__(self) -> Tuple[float]: ...
if sys.version_info >= (3,):
@overload
def __round__(self) -> int: ...
@overload
def __round__(self, ndigits: None) -> int: ...
def __round__(self, ndigits: None = ...) -> int: ...
@overload
def __round__(self, ndigits: int) -> float: ...
def __trunc__(self) -> int: ...
def __floor__(self) -> int: ...
def __ceil__(self) -> int: ...

def __eq__(self, x: object) -> bool: ...
def __ne__(self, x: object) -> bool: ...
Expand Down
8 changes: 6 additions & 2 deletions stdlib/2and3/numbers.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Note: these stubs are incomplete. The more complex type
# signatures are currently omitted.

from typing import Any, Optional, SupportsFloat
from typing import Any, Optional, SupportsFloat, overload
from abc import ABCMeta, abstractmethod
import sys

Expand Down Expand Up @@ -70,7 +70,11 @@ class Real(Complex, SupportsFloat):
@abstractmethod
def __ceil__(self) -> int: ...
@abstractmethod
def __round__(self, ndigits: Optional[int] = ...): ...
@overload
def __round__(self, ndigits: None = ...): ...
Copy link
Member

Choose a reason for hiding this comment

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

Not sure why this needs overloads.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The idea is to have the same form as float above. Once this stub is filled the return types of the two overloads will be different, I think.

@abstractmethod
@overload
def __round__(self, ndigits: int): ...
def __divmod__(self, other): ...
def __rdivmod__(self, other): ...
@abstractmethod
Expand Down