Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fdb3ef8

Browse filesBrowse 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 <jelle.zijlstra@gmail.com> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
1 parent 5b05b01 commit fdb3ef8
Copy full SHA for fdb3ef8

File tree

Expand file treeCollapse file tree

6 files changed

+78
-25
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+78
-25
lines changed

‎Doc/library/functions.rst

Copy file name to clipboardExpand all lines: Doc/library/functions.rst
+1-1Lines 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

Copy file name to clipboardExpand all lines: Doc/library/stdtypes.rst
+33-21Lines 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

Copy file name to clipboardExpand all lines: Doc/whatsnew/3.12.rst
+6Lines 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

Copy file name to clipboardExpand all lines: Lib/test/test_bool.py
+16-2Lines 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)
+5Lines 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

Copy file name to clipboardExpand all lines: Objects/boolobject.c
+17-1Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.