classification
Title: float() can return not exact float instance
Type: behavior Stage: commit review
Components: Interpreter Core Versions: Python 3.6
process
Status: closed Resolution: fixed
Dependencies: 26995 Superseder:
Assigned To: serhiy.storchaka Nosy List: SilentGhost, mark.dickinson, python-dev, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2016-05-09 13:46 by serhiy.storchaka, last changed 2016-06-06 10:01 by serhiy.storchaka. This issue is now closed.

Files
File name Uploaded Description Edit
float_exact.patch serhiy.storchaka, 2016-05-10 19:08 review
float_exact2.patch serhiy.storchaka, 2016-05-11 14:54 review
issue26983.diff SilentGhost, 2016-06-06 08:08 review
Messages (15)
msg265191 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-05-09 13:46
The float constructor can return an instance of float subclass.

>>> class FloatSubclass(float):
...     pass
... 
>>> class BadFloat:
...     def __float__(self):
...         return FloatSubclass(1.2)
... 
>>> type(float(BadFloat()))
<class '__main__.FloatSubclass'>

Comparing with other types, complex() always returns complex:

>>> class ComplexSubclass(complex):
...     pass
... 
>>> class BadComplex:
...     def __complex__(self):
...         return ComplexSubclass(1.2, 3.4)
... 
>>> type(complex(BadComplex()))
<class 'complex'>

And int() can return an instance of int subclass, but this behavior is deprecated:

>>> class BadInt:
...     def __int__(self):
...         return True
... 
>>> int(BadInt())
__main__:1: DeprecationWarning: __int__ returned non-int (type bool).  The ability to return an instance of a strict subclass of int is deprecated, and may be removed in a future version of Python.
True

May be we should either deprecate __float__ returning non-float (as for int), or convert the result to exact float (as for complex).

The constructor of float subclass always returns an instance of correct type.

>>> class FloatSubclass2(float):
...     pass
... 
>>> type(FloatSubclass2(BadFloat()))
<class '__main__.FloatSubclass2'>
msg265267 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-05-10 19:08
Proposed patch makes float() always returning exact float.
msg265295 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2016-05-11 08:05
+1 for the idea, and the patch LGTM.

I was initially a bit confused by the wording of the warning: presumably, we're not going to change Python to make returning an instance of a strict float subclass from __float__ illegal (I don't really see how that would be possible); we're just going to check the return type at the internal call sites to __float__ and raise if we get something other than an exact float. That is, I'd still expect this code to work on future versions of Python:

>>> class MyFloat(float): pass
... 
>>> class A:
...     def __float__(self): return MyFloat()
... 
>>> a = A()
>>> a.__float__()
0.0

But these would both become an error in Python 3.7 (say):

>>> float(a)
0.0
>>> math.sqrt(a)
0.0

Does that match your thinking?

We should probably add issues for checking and fixing other places that __float__ is used internally (like the math module).
msg265297 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-05-11 08:59
The warning message is copied from __int__. As I understand, the conclusion of the Python-Dev discussion is that __int__, __float__, __complex__, etc should return exact int, float, complex, not subclasses. I have another patch that adds a warning for __complex__, and I am working on patches for other special methods. If my understanding is wrong, we should remove the deprecation warning for __int__.
msg265298 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2016-05-11 09:45
> As I understand, the conclusion of the Python-Dev discussion is that __int__, __float__, __complex__, etc should return exact int, float, complex, not subclasses.

Agreed; that matches my understanding. I'm only observing that it would be difficult (and likely undesirable) to actually enforce that `__int__` itself always returns an exact int: I'm not sure where that check would/could take place, and I'd expect that a direct user-level call to `my_object.__int__` probably wouldn't be subject to such a check. What we *can* do is check that the result of the `nb_int` call is always an int in the places we use it.

Or we could even go further, and write custom slot_nb_float and slot_nb_int functions in Objects/typeobject.c that incorporates those checks. What I don't think we can do is check that a *direct* call to `__int__` or `__float__` always returns something of the exact type.
msg265299 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-05-11 10:20
Here is updated patch. Differences:

* Deprecation warning is now emitted in functions accepting floats (like math.sqrt).
* Improved error messages. Now reported wrong type name and a type of wrong __float__ method.
* float() now returns the same object for exact float argument.

Could you propose better warning message?
msg265316 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2016-05-11 14:05
Can you please attach the new patch?
msg265318 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-05-11 14:54
Oh, sorry, how could I miss it?
msg267096 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-06-03 12:27
Ping.
msg267101 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2016-06-03 14:11
float_exact2.patch LGTM
msg267129 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2016-06-03 18:43
New changeset 050e5f803999 by Serhiy Storchaka in branch 'default':
Issue #26983: float() now always return an instance of exact float.
https://hg.python.org/cpython/rev/050e5f803999
msg267130 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-06-03 18:44
Thank you for your review Mark.
msg267521 - (view) Author: SilentGhost (SilentGhost) * (Python triager) Date: 2016-06-06 08:08
test_format resulted in semi-failure due to this change. The attached patch fixes the issue.
msg267523 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2016-06-06 10:00
New changeset 6216fb8afa53 by Serhiy Storchaka in branch 'default':
Issue #26983: Fixed test_format failure.
https://hg.python.org/cpython/rev/6216fb8afa53
msg267524 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2016-06-06 10:01
My failure. Thank you for your report and patch SilentGhost.
History
Date User Action Args
2016-06-06 10:01:54serhiy.storchakasetstatus: open -> closed
resolution: fixed
messages: + msg267524
2016-06-06 10:00:28python-devsetmessages: + msg267523
2016-06-06 08:08:04SilentGhostsetstatus: closed -> open
files: + issue26983.diff

assignee: serhiy.storchaka

nosy: + SilentGhost
messages: + msg267521
resolution: fixed -> (no value)
stage: resolved -> commit review
2016-06-03 18:54:26serhiy.storchakasetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2016-06-03 18:44:27serhiy.storchakasetmessages: + msg267130
2016-06-03 18:43:29python-devsetnosy: + python-dev
messages: + msg267129
2016-06-03 14:11:05mark.dickinsonsetmessages: + msg267101
2016-06-03 12:27:52serhiy.storchakasetmessages: + msg267096
2016-05-11 14:54:48serhiy.storchakasetfiles: + float_exact2.patch

messages: + msg265318
2016-05-11 14:05:17mark.dickinsonsetmessages: + msg265316
2016-05-11 10:21:29serhiy.storchakasetdependencies: + Add tests for parsing float and object arguments
2016-05-11 10:20:50serhiy.storchakasetmessages: + msg265299
2016-05-11 09:45:22mark.dickinsonsetmessages: + msg265298
2016-05-11 08:59:35serhiy.storchakasetmessages: + msg265297
2016-05-11 08:05:24mark.dickinsonsetmessages: + msg265295
2016-05-10 19:08:27serhiy.storchakasetfiles: + float_exact.patch
keywords: + patch
messages: + msg265267

stage: patch review
2016-05-09 13:46:33serhiy.storchakacreate