Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fdb3ef8

Browse files
timhoffmJelleZijlstrahauntsaninja
authored
gh-82012: Deprecate bitwise inversion (~) of bool (#103487)
The bitwise inversion operator on bool returns the bitwise inversion of the underlying int value; i.e. `~True == -2` such that `bool(~True) == True`. It's a common pitfall that users mistake `~` as negation operator and actually want `not`. Supporting `~` is an artifact of bool inheriting from int. Since there is no real use-case for the current behavior, let's deprecate `~` on bool and later raise an error. This removes a potential source errors for users. Full reasoning: #82012 (comment) Co-authored-by: Jelle Zijlstra <[email protected]> Co-authored-by: Shantanu <[email protected]>
1 parent 5b05b01 commit fdb3ef8

File tree

6 files changed

+78
-25
lines changed

6 files changed

+78
-25
lines changed

Doc/library/functions.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ are always available. They are listed here in alphabetical order.
147147
or omitted, this returns ``False``; otherwise, it returns ``True``. The
148148
:class:`bool` class is a subclass of :class:`int` (see :ref:`typesnumeric`).
149149
It cannot be subclassed further. Its only instances are ``False`` and
150-
``True`` (see :ref:`bltin-boolean-values`).
150+
``True`` (see :ref:`typebool`).
151151

152152
.. index:: pair: Boolean; type
153153

Doc/library/stdtypes.rst

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,39 @@ number, :class:`float`, or :class:`complex`::
802802
hash_value = -2
803803
return hash_value
804804

805+
.. _typebool:
806+
807+
Boolean Type - :class:`bool`
808+
============================
809+
810+
Booleans represent truth values. The :class:`bool` type has exactly two
811+
constant instances: ``True`` and ``False``.
812+
813+
.. index::
814+
single: False
815+
single: True
816+
pair: Boolean; values
817+
818+
The built-in function :func:`bool` converts any value to a boolean, if the
819+
value can be interpreted as a truth value (see section :ref:`truth` above).
820+
821+
For logical operations, use the :ref:`boolean operators <boolean>` ``and``,
822+
``or`` and ``not``.
823+
When applying the bitwise operators ``&``, ``|``, ``^`` to two booleans, they
824+
return a bool equivalent to the logical operations "and", "or", "xor". However,
825+
the logical operators ``and``, ``or`` and ``!=`` should be preferred
826+
over ``&``, ``|`` and ``^``.
827+
828+
.. deprecated:: 3.12
829+
830+
The use of the bitwise inversion operator ``~`` is deprecated and will
831+
raise an error in Python 3.14.
832+
833+
:class:`bool` is a subclass of :class:`int` (see :ref:`typesnumeric`). In
834+
many numeric contexts, ``False`` and ``True`` behave like the integers 0 and 1, respectively.
835+
However, relying on this is discouraged; explicitly convert using :func:`int`
836+
instead.
837+
805838
.. _typeiter:
806839

807840
Iterator Types
@@ -5394,27 +5427,6 @@ information. There is exactly one ``NotImplemented`` object.
53945427
It is written as ``NotImplemented``.
53955428

53965429

5397-
.. _bltin-boolean-values:
5398-
5399-
Boolean Values
5400-
--------------
5401-
5402-
Boolean values are the two constant objects ``False`` and ``True``. They are
5403-
used to represent truth values (although other values can also be considered
5404-
false or true). In numeric contexts (for example when used as the argument to
5405-
an arithmetic operator), they behave like the integers 0 and 1, respectively.
5406-
The built-in function :func:`bool` can be used to convert any value to a
5407-
Boolean, if the value can be interpreted as a truth value (see section
5408-
:ref:`truth` above).
5409-
5410-
.. index::
5411-
single: False
5412-
single: True
5413-
pair: Boolean; values
5414-
5415-
They are written as ``False`` and ``True``, respectively.
5416-
5417-
54185430
.. _typesinternal:
54195431

54205432
Internal Objects

Doc/whatsnew/3.12.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,12 @@ Deprecated
710710
replaced by :data:`calendar.Month.JANUARY` and :data:`calendar.Month.FEBRUARY`.
711711
(Contributed by Prince Roshan in :gh:`103636`.)
712712

713+
* The bitwise inversion operator (``~``) on bool is deprecated. It will throw an
714+
error in Python 3.14. Use ``not`` for logical negation of bools instead.
715+
In the rare case that you really need the bitwise inversion of the underlying
716+
``int``, convert to int explicitly with ``~int(x)``. (Contributed by Tim Hoffmann
717+
in :gh:`103487`.)
718+
713719
Pending Removal in Python 3.13
714720
------------------------------
715721

Lib/test/test_bool.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,22 @@ def test_math(self):
5858
self.assertEqual(-True, -1)
5959
self.assertEqual(abs(True), 1)
6060
self.assertIsNot(abs(True), True)
61-
self.assertEqual(~False, -1)
62-
self.assertEqual(~True, -2)
61+
with self.assertWarns(DeprecationWarning):
62+
# We need to put the bool in a variable, because the constant
63+
# ~False is evaluated at compile time due to constant folding;
64+
# consequently the DeprecationWarning would be issued during
65+
# module loading and not during test execution.
66+
false = False
67+
self.assertEqual(~false, -1)
68+
with self.assertWarns(DeprecationWarning):
69+
# also check that the warning is issued in case of constant
70+
# folding at compile time
71+
self.assertEqual(eval("~False"), -1)
72+
with self.assertWarns(DeprecationWarning):
73+
true = True
74+
self.assertEqual(~true, -2)
75+
with self.assertWarns(DeprecationWarning):
76+
self.assertEqual(eval("~True"), -2)
6377

6478
self.assertEqual(False+2, 2)
6579
self.assertEqual(True+2, 3)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
The bitwise inversion operator (``~``) on bool is deprecated.
2+
It returns the bitwise inversion of the underlying ``int`` representation such that
3+
``bool(~True) == True``, which can be confusing. Use ``not`` for logical negation
4+
of bools. In the rare case that you really need the bitwise inversion of the underlying ``int``,
5+
convert to int explicitly ``~int(x)``.

Objects/boolobject.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,22 @@ bool_vectorcall(PyObject *type, PyObject * const*args,
7373

7474
/* Arithmetic operations redefined to return bool if both args are bool. */
7575

76+
static PyObject *
77+
bool_invert(PyObject *v)
78+
{
79+
if (PyErr_WarnEx(PyExc_DeprecationWarning,
80+
"Bitwise inversion '~' on bool is deprecated. This "
81+
"returns the bitwise inversion of the underlying int "
82+
"object and is usually not what you expect from negating "
83+
"a bool. Use the 'not' operator for boolean negation or "
84+
"~int(x) if you really want the bitwise inversion of the "
85+
"underlying int.",
86+
1) < 0) {
87+
return NULL;
88+
}
89+
return PyLong_Type.tp_as_number->nb_invert(v);
90+
}
91+
7692
static PyObject *
7793
bool_and(PyObject *a, PyObject *b)
7894
{
@@ -119,7 +135,7 @@ static PyNumberMethods bool_as_number = {
119135
0, /* nb_positive */
120136
0, /* nb_absolute */
121137
0, /* nb_bool */
122-
0, /* nb_invert */
138+
(unaryfunc)bool_invert, /* nb_invert */
123139
0, /* nb_lshift */
124140
0, /* nb_rshift */
125141
bool_and, /* nb_and */

0 commit comments

Comments
 (0)