From fc23ee64665f299b431beb215e3990b56d4d5872 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 4 Nov 2026 01:23:08 +0100 Subject: [PATCH 1/3] PEP 824: Extend rational for precedence --- pep_sphinx_extensions/lexers/pep824_lexer.py | 28 +++- peps/pep-0824.rst | 129 ++++++++++++++++++- 2 files changed, 147 insertions(+), 10 deletions(-) diff --git a/pep_sphinx_extensions/lexers/pep824_lexer.py b/pep_sphinx_extensions/lexers/pep824_lexer.py index 0f6b5791943..97cec804e02 100644 --- a/pep_sphinx_extensions/lexers/pep824_lexer.py +++ b/pep_sphinx_extensions/lexers/pep824_lexer.py @@ -7,9 +7,13 @@ # If it breaks in the future, it can be safely replaced by plain # Python or text lexers. -from pygments.lexer import inherit -from pygments.lexers.python import PythonLexer -from pygments.token import Operator +from pygments.lexer import DelegatingLexer, inherit +from pygments.lexers.python import ( + PythonLexer, + PythonTracebackLexer, + _PythonConsoleLexerBase, +) +from pygments.token import Operator, Other class Py824Lexer(PythonLexer): @@ -25,5 +29,21 @@ class Py824Lexer(PythonLexer): } +class Py824ConsoleLexer(DelegatingLexer): + name = "py824-console" + + def __init__(self, **options): + pylexer = Py824Lexer + tblexer = PythonTracebackLexer + + class _ReplaceInnerCode(DelegatingLexer): + def __init__(self, **options): + super().__init__( + pylexer, _PythonConsoleLexerBase, Other.Code, **options + ) + + super().__init__(tblexer, _ReplaceInnerCode, Other.Traceback, **options) + + def register(): - return [Py824Lexer] + return [Py824Lexer, Py824ConsoleLexer] diff --git a/peps/pep-0824.rst b/peps/pep-0824.rst index c9989050a19..b638771b224 100644 --- a/peps/pep-0824.rst +++ b/peps/pep-0824.rst @@ -373,9 +373,9 @@ Operator precedence ------------------- The ``??`` operator has a lower precedence than ``or`` and ``and``. -Thus, ``a or b ?? c`` means ``(a or b) ?? c``, and ``a ?? b and c`` -means ``a ?? (b and c)``. To get the opposite grouping, use -``a or (b ?? c)`` or ``(a ?? b) and c``. +Thus, ``a ?? b or c`` means ``a ?? (b or c)``, and ``a and b ?? c`` +means ``(a and b) ?? c``. To get the opposite grouping, use explicit +parentheses. This precedence is a convention. For clarity, we recommend always adding parentheses when mixing ``??`` with ``and`` or ``or``. @@ -548,9 +548,109 @@ left-hand side is evaluated if the values is ``not None``. The conditional operators ``or`` and ``and`` which also short-circuit the expression for truthy and falsy values respectively. -Furthermore, setting the precedence between ``or`` and conditional -expressions matches other languages which have implemented the operator, -like JS [#js_precedence]_ and C# [#csharp_precedence]_. +Due to their similarities, it is even fair to describe ``??`` as just +a specialized variant of ``or``. While ``or`` checks for truthiness, +``??`` only checks for ``None``. For ``a = None`` they will output the +same: + +.. code-block:: py824-console + + >>> a = None + >>> a or 2 + 2 + >>> a ?? 2 + 2 + +Use different precedence for ``??`` +----------------------------------- + +It was discussed what the precedence for ``??`` should be, in particular +in relationship to ``or``. + +Looking at the other modern programming languages which have "``null``" +coalescing operators, two options for the precedence emerge. + +1. A precedence between binary and comparison operators as seen in + both Swift [#swift_precedence]_ and Kotlin [#kotlin_precedence]_ + for their their "Nil-Coalescing" and "Elvis" operators respectively. + +2. A precedence between the logical OR and conditional / ternary + operators, see C# [#csharp_precedence]_, Dart [#dart_precedence]_ and + PHP [#php_precedence]_. JS / TS [#js_precedence]_ is similar though + parentheses are required when mixing ``??`` with either ``||`` or + ``&&``. (See the next `section `_ for more details on that.) + +The relationship of the ``None``-coalescing operator proposed in this +PEP to other programming languages is by design. Therefore, it does not +make sense to choose yet another precedence only for Python. + +With the decision to add ``??`` as a boolean operator, see the rejected +idea `Add ?? as a binary operator`_ for the rational, only a precedence +close to ``and`` and ``or`` makes sense. Hence, option (1) is +rejected. + +The ``??`` operator only handles a subset of values compared to ``or``, +just ``None`` instead of all falsy values. By assigning a precedence +lower than ``or``, option (2), it is guaranteed that a ``not None`` +but falsy result of the more specific check ``??`` is preserved and +does not get overwritten by the broader ``or`` check. + +.. code-block:: py824 + + args = {"user_name": ""} # str or None (if not given as input) + env = {"default_name": "Alice"} + + args["user_name"] ?? env["default_name"] or "Guest" + +In the example above the user explicitly supplied an empty string, +a falsy value, which should be passed on. Assuming a precedence for +``??`` lower than ``or``, the expression would short-circuit and +return ``""`` directly after evaluating ``args["user_name"]`` +because ``"" is not None``. Changing the precedence to be equal or +higher than ``or`` would mean that ``or "Guest"`` is evaluated even +though a valid value was already supplied. + +The altered precedence would break the assumption that ``??`` can +short-circuit an expression containing ``or`` for valid but falsy +values. Thus, a precedence lower than ``or`` is chosen, option (2), +see `Precedence`_ specification. + +Require parentheses when mixing ``??`` with ``and`` and ``or`` +-------------------------------------------------------------- + +As explained `before `_, JavaScript +requires parentheses when mixing ``??`` with ``&&`` or ``||``. It was +suggested that Python could do the same. + +The PEP acknowledges that combining different boolean operators can +be confusing and explicitly recommends the use of parentheses for added +clarity in the `How to teach this `_ section. +The question to answer therefore is not whether parentheses are useful +but rather if they should be enforced by the language grammar. + +This is rejected. For one, the benefit from strict enforcement would +be minimal. Not only will both ``a ?? b or c`` and ``a or b ?? c`` +expressions evaluate as a reader expects without having to think +about grouping or precedence -- they will short-circuit after each +operand if the condition is met -- but developers are likely to add +parentheses on their own anyway just to make it more obvious. +A larger ecosystem trend can also be seen in the increasing +prevalence of linter and auto-formatter rules requiring or outright +adding parentheses for boolean expressions automatically, e.g. for +Ruff RUF021 [#ruf021]_. + +Furthermore, strict enforcement would add a new special case compared +to ``and`` and ``or`` which would need to be thought and remembered +every time instead of the more general recommendation to just always +use parentheses when mixing any boolean operators which already exists. + +Lastly, while JavaScript requires parentheses, the other languages +with similar precedence only do so when it is required by their type +system. Python itself does not require explicit parentheses for any +other operator either. Even for assignment expressions which +almost always require parentheses, it is not a strict enforcement but +rather a necessity created by the low precedence of ``:=``. Add ``??=`` as ``AugAssign`` ---------------------------- @@ -720,23 +820,40 @@ Footnotes (https://dart.dev/language/operators#conditional-expressions) .. [#dart_2] Dart: Assignment operators (https://dart.dev/language/operators#assignment-operators) +.. [#dart_precedence] Dart: Operators + (https://dart.dev/language/operators) .. [#swift] Swift: Nil-Coalescing Operator (https://docs.swift.org/swift-book/documentation/the-swift-programming-language/basicoperators/#Nil-Coalescing-Operator) +.. [#swift_precedence] Swift Operator precedence + (https://www.programiz.com/swift-programming/operator-precedence-associativity) .. [#kotlin] Kotlin: Elvis operator (https://kotlinlang.org/docs/null-safety.html#elvis-operator) +.. [#kotlin_precedence] Kotlin: Precedence + (https://kotlinlang.org/grammar/#expressions) .. [#php_1] PHP: Null Coalesce Operator (https://wiki.php.net/rfc/isset_ternary) .. [#php_2] PHP: Null Coalescing Assignment Operator (https://wiki.php.net/rfc/null_coalesce_equal_operator) +.. [#php_precedence] PHP: Operator precedence + (https://www.php.net/manual/en/language.operators.precedence.php) .. [#ruby] Ruby: Abbreviated Assignment (https://ruby-doc.org/3.2/syntax/assignment_rdoc.html#label-Abbreviated+Assignment) .. [#perl] Perl: Assignment Operators (https://perldoc.perl.org/perlop#Assignment-Operators) +.. [#ruf021] Ruff: parenthesize-chained-operators (RUF021) + (https://docs.astral.sh/ruff/rules/parenthesize-chained-operators/) Change History ============== +* 04-Nov-2026: + + - Updated `Add ?? as a binary operator`_ + and added `Use different precedence for ??`_ + and `Require parentheses when mixing ?? with and and or`_ + as rejected ideas. + * 31-Oct-2026: - Added section on `Chaining `_ to the specification. From be9425b906ca3a711a82881fa7cab96c1335ce4a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 4 Nov 2026 17:05:56 +0100 Subject: [PATCH 2/3] Minor --- peps/pep-0824.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/peps/pep-0824.rst b/peps/pep-0824.rst index b638771b224..141df826ff8 100644 --- a/peps/pep-0824.rst +++ b/peps/pep-0824.rst @@ -549,9 +549,9 @@ conditional operators ``or`` and ``and`` which also short-circuit the expression for truthy and falsy values respectively. Due to their similarities, it is even fair to describe ``??`` as just -a specialized variant of ``or``. While ``or`` checks for truthiness, -``??`` only checks for ``None``. For ``a = None`` they will output the -same: +a specialized variant of ``or``. While ``or`` checks for all falsy +values, ``??`` only checks for the narrower ``None``. For truthy values +and ``None``, they will output the same: .. code-block:: py824-console @@ -567,8 +567,8 @@ Use different precedence for ``??`` It was discussed what the precedence for ``??`` should be, in particular in relationship to ``or``. -Looking at the other modern programming languages which have "``null``" -coalescing operators, two options for the precedence emerge. +Looking at the other modern programming languages which have "``null`` +coalescing" operators, two options for the precedence emerge. 1. A precedence between binary and comparison operators as seen in both Swift [#swift_precedence]_ and Kotlin [#kotlin_precedence]_ @@ -576,7 +576,7 @@ coalescing operators, two options for the precedence emerge. 2. A precedence between the logical OR and conditional / ternary operators, see C# [#csharp_precedence]_, Dart [#dart_precedence]_ and - PHP [#php_precedence]_. JS / TS [#js_precedence]_ is similar though + PHP [#php_precedence]_. JS / TS [#js_precedence]_ is similar, though parentheses are required when mixing ``??`` with either ``||`` or ``&&``. (See the next `section `_ for more details on that.) @@ -587,7 +587,7 @@ make sense to choose yet another precedence only for Python. With the decision to add ``??`` as a boolean operator, see the rejected idea `Add ?? as a binary operator`_ for the rational, only a precedence -close to ``and`` and ``or`` makes sense. Hence, option (1) is +close to ``and`` and ``or`` makes sense. As such, option (1) is rejected. The ``??`` operator only handles a subset of values compared to ``or``, @@ -599,9 +599,9 @@ does not get overwritten by the broader ``or`` check. .. code-block:: py824 args = {"user_name": ""} # str or None (if not given as input) - env = {"default_name": "Alice"} + config = {"default_name": "Alice"} - args["user_name"] ?? env["default_name"] or "Guest" + args["user_name"] ?? config["default_name"] or "Guest" In the example above the user explicitly supplied an empty string, a falsy value, which should be passed on. Assuming a precedence for @@ -612,9 +612,9 @@ higher than ``or`` would mean that ``or "Guest"`` is evaluated even though a valid value was already supplied. The altered precedence would break the assumption that ``??`` can -short-circuit an expression containing ``or`` for valid but falsy -values. Thus, a precedence lower than ``or`` is chosen, option (2), -see `Precedence`_ specification. +short-circuit a boolean expression also containing ``or`` for valid but +falsy values, without explicit parentheses. Thus, a precedence lower +than ``or`` is chosen, option (2), see `Precedence`_ specification. Require parentheses when mixing ``??`` with ``and`` and ``or`` -------------------------------------------------------------- @@ -643,7 +643,7 @@ Ruff RUF021 [#ruf021]_. Furthermore, strict enforcement would add a new special case compared to ``and`` and ``or`` which would need to be thought and remembered every time instead of the more general recommendation to just always -use parentheses when mixing any boolean operators which already exists. +use parentheses when mixing any boolean operators, which already exists. Lastly, while JavaScript requires parentheses, the other languages with similar precedence only do so when it is required by their type From 39cfdb9acee2015196908352e003db9ff8f49666 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 4 Nov 2026 17:16:02 +0100 Subject: [PATCH 3/3] Clarification --- peps/pep-0824.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/peps/pep-0824.rst b/peps/pep-0824.rst index 141df826ff8..dcf16321fda 100644 --- a/peps/pep-0824.rst +++ b/peps/pep-0824.rst @@ -612,9 +612,10 @@ higher than ``or`` would mean that ``or "Guest"`` is evaluated even though a valid value was already supplied. The altered precedence would break the assumption that ``??`` can -short-circuit a boolean expression also containing ``or`` for valid but -falsy values, without explicit parentheses. Thus, a precedence lower -than ``or`` is chosen, option (2), see `Precedence`_ specification. +short-circuit a boolean expression also containing ``or`` for +``not None`` but falsy values, without explicit parentheses. Thus, +a precedence lower than ``or`` is chosen, option (2), see +`Precedence`_ specification. Require parentheses when mixing ``??`` with ``and`` and ``or`` --------------------------------------------------------------