classification
Title: sorted, list.sort reject non-boolean objects with __bool__() as `reverse` parameter
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.9
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: mark.dickinson, nodakai, rhettinger, serhiy.storchaka, terry.reedy, tim.peters
Priority: normal Keywords:

Created on 2019-06-27 09:51 by nodakai, last changed 2019-08-29 17:04 by mark.dickinson. This issue is now closed.

Messages (6)
msg346722 - (view) Author: NODA, Kai (nodakai) Date: 2019-06-27 09:51
class X:
    def __bool__(self):
        return True

x = [1, 2, 3]
x.sort(reverse=X())
print(x)
print(sorted([1, 2, 3], reverse=X()))

TypeError: an integer is required (got type X)

We can always still do

x.sort(reverse=bool(X()))
print(sorted([1, 2, 3], reverse=bool(X())))

but that isn't cool
msg346730 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-06-27 12:23
I haven't looked at the source yet, but from experimentation I'm finding it rather hard to guess what the rules are for what works and what doesn't:

Python 3.7.3 (default, Mar 30 2019, 03:37:43) 
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import decimal, fractions, numpy
>>> sorted([1, 2, 3], reverse=0.5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: integer argument expected, got float
>>> sorted([1, 2, 3], reverse=decimal.Decimal(0.5))
[1, 2, 3]
>>> sorted([1, 2, 3], reverse=fractions.Fraction(0.5))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: an integer is required (got type Fraction)
>>> sorted([1, 2, 3], reverse=numpy.int64(1))
[3, 2, 1]
>>> sorted([1, 2, 3], reverse=numpy.bool_(True))
[3, 2, 1]
msg346731 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-06-27 12:32
> I'm finding it rather hard to guess what the rules are for what works and what doesn't

So now that I've looked at the source: anything with an `__int__` method works, except that `float` instances are explicitly excluded. So this explains for example:

>>> sorted([1, 2, 3], reverse=numpy.float64(0.5))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: integer argument expected, got float
>>> sorted([1, 2, 3], reverse=numpy.float32(0.5))
[1, 2, 3]
msg346742 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-06-27 15:11
See issue15999.
msg346870 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2019-06-28 22:04
Not a bug.  Both function docs say "reverse is a boolean value."
https://docs.python.org/3/library/stdtypes.html#boolean-values
says "Boolean values are the two constant objects False and True."  These should be used in new code.

For compatibility with old code, CPython generally allows 0 and 1.  In this case, it allows any 'integral value', but I would not necessarily expect this of other implementations.

[In general, the *Python language* docs do not document extra latitude allowed by the *CPython implementation*.  So the lack thereof here is also not a bug.]

All objects without fancy trickery have a __bool__ method, either inherited from *object* or overridden.  This in no way makes all such objects, including Xs, into boolean values.
msg350808 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-08-29 17:04
See also #37980. I'd support a change to have the `reversed` argument to `sort` and `sorted` be automatically interpreted in boolean context.
History
Date User Action Args
2019-08-29 17:04:27mark.dickinsonsetmessages: + msg350808
2019-06-28 22:04:26terry.reedysetstatus: open -> closed

title: sorted() and list.sort() don't accept non-boolean objects with __bool__() as `reverse` parameter -> sorted, list.sort reject non-boolean objects with __bool__() as `reverse` parameter
nosy: + terry.reedy

messages: + msg346870
resolution: not a bug
stage: resolved
2019-06-27 15:11:25serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg346742
2019-06-27 12:32:08mark.dickinsonsetmessages: + msg346731
2019-06-27 12:25:13mark.dickinsonsetnosy: + tim.peters
2019-06-27 12:23:18mark.dickinsonsetnosy: + mark.dickinson
messages: + msg346730
2019-06-27 10:10:40xtreaksetnosy: + rhettinger
2019-06-27 09:51:45nodakaicreate