Skip to content

Commit 55d1e08

Browse files
committed
Update grouping spec
1 parent 031d1a9 commit 55d1e08

File tree

1 file changed

+52
-54
lines changed

1 file changed

+52
-54
lines changed

peps/pep-0999.rst

Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -455,17 +455,24 @@ is broken once a (soft-) keyword is reached.
455455
Grouping
456456
********
457457

458-
Grouping is an implicit property of the `Short-circuiting`_ behavior.
459-
If a group contains a non short-circuiting expression, i.e. one that
460-
is not either a name, attribute access, subscript, their ``None``-aware
461-
counterparts or a call expression, the short-circuiting chain will be
462-
broken. The rule of thumb still applies: short-circuiting is broken once
463-
a (soft-) keyword is reached.
464-
465-
In the example below the group contains a ``BoolOp`` (``or``) expression
466-
which breaks the short-circuiting chain into two: ``a.b?.c`` inside
467-
the group which is evaluated first and ``(...).e?.func()`` on the
468-
outside.
458+
Using ``?.`` and ``?[ ]`` inside groups is possible. Short-circuiting
459+
will be broken either by the rules laid out in the `Short-circuiting`_
460+
section or at the end of a group. For example the expression ``(a?.b).c``
461+
will raise an ``AttributeError`` on ``.c`` if ``a = None``. This is
462+
conceptually identical to extracting the group contents and storing the
463+
result in a temporary variable before substituting it back into the
464+
original expression.
465+
466+
::
467+
468+
# (a?.b).c
469+
470+
_t = a?.b
471+
_t.c
472+
473+
Common use cases for ``None``-aware access operators in groups are
474+
boolean or conditional expressions which can provide a fallback value
475+
in case the first part evaluates to ``None``.
469476

470477
::
471478

@@ -480,16 +487,6 @@ outside.
480487
# (...).e?.func()
481488
_t4.func() if ((_t4 := _t3.e) is not None) else None
482489

483-
In contrast, the example below only consists of a name, one attribute
484-
access and two ``None``-aware attribute access expressions. As such the
485-
grouping does not break the short-circuiting chain. The brackets can
486-
safely be removed.
487-
488-
::
489-
490-
# Trivial groups
491-
(a?.b).c?.d == a?.b.c?.d
492-
493490
Assignments
494491
***********
495492

@@ -540,7 +537,9 @@ Two new AST nodes are added ``NoneAwareAttribute`` and ``NoneAwareSubscript``.
540537
They are the counterparts to the existing ``Attribute`` and ``Subscript``
541538
nodes. Notably there is no ``expr_context`` attribute because the new nodes
542539
do not support assignments themselves and thus the context will always be
543-
``Load``.
540+
``Load``. Furthermore, an optional ``group`` attribute is added for all
541+
expression nodes. It is set to ``1`` if the expression is the topmost
542+
node in a group, ``0`` otherwise.
544543

545544
::
546545

@@ -551,6 +550,9 @@ do not support assignments themselves and thus the context will always be
551550
| NoneAwareAttribute(expr value, identifier attr)
552551
| NoneAwareSubscript(expr value, expr slice)
553552

553+
attributes (int? group, int lineno, int col_offset,
554+
int? end_lineno, int? end_col_offset)
555+
554556
Grammar changes
555557
---------------
556558

@@ -924,40 +926,36 @@ for lists and tuples and as such would be a valuable addition to the
924926
language itself, it doesn't remove the need for arbitrary objects which
925927
implement a custom ``__getitem__`` method.
926928

927-
Limit scope of short-circuiting with grouping
928-
---------------------------------------------
929+
Ignore groups for short-circuiting
930+
----------------------------------
931+
932+
An earlier version of this PEP suggested the short-circuiting
933+
behavior should be indifferent towards grouping. It was assumed that
934+
short-circuiting would be broken already for more complex group
935+
expressions like ``(a?.b or c).d`` by the behavior outline in the
936+
`Short-circuiting`_ section. While for simpler ones like ``(a?.b).c``
937+
the grouping was considered trivial and the expression would be equal to
938+
``a?.b.c``. The advantage being that developers would not have to
939+
look for groupings when evaluating simpler expressions. As long as
940+
any ``None``-aware access operator was used and the expression was not
941+
broken by a (soft-) keyword, it would return ``None`` instead of raising
942+
an ``AttributeError`` or ``TypeError``.
943+
944+
This suggestion was rejected in favor of the specification outline in
945+
the `Grouping`_ section since it violates the substitution principle.
946+
An expression ``(a?.b).c`` should behave the same whether or not ``a?.b``
947+
is written inline inside a group or defined as a separate variable.
929948

930-
Some languages like JS [#js_short_circuiting]_ and C# [#csharp]_ limit the
931-
scope of the `Short-circuiting`_ via explicit grouping::
949+
::
932950

933-
a = None
934-
x = (a?.b).c
935-
# ^^^^^^
936-
937-
In the example above short-circuiting would be limited to just ``a?.b``,
938-
thus with ``a = None`` the expression would raise an ``AttributeError``
939-
instead of setting ``x`` to ``None``.
940-
941-
Even though other languages have implemented it that way, this kind of
942-
explicit grouping for short-circuiting does have its disadvantages.
943-
The ``None``-aware access operators are explicitly designed to return
944-
``None`` at some point. Directly limiting the scope of the
945-
short-circuiting behavior almost guarantees that the code will raise
946-
an ``AttributeError`` or ``TypeError`` at some point. Type checkers
947-
would also have to raise an error for trying to access an attribute
948-
or subscript on an ``optional`` variable again.
949-
950-
As such breaking the short-circuiting chain does only make sense if a
951-
fallback value is provided at the same time. For example::
952-
953-
(a?.b.c or fallback).e.func()
954-
955-
In case it is known that ``a`` will always be a not ``None`` value,
956-
and it is just still typed as optional, better options include adding
957-
an ``assert a is not None`` or if it is ever proposed a ``Not-None``
958-
assertion operator ``a!`` (out of scope for this PEP). Developers also
959-
always have the option of splitting the expression up again like they do
960-
today.
951+
(a?.b).c
952+
953+
_t = a?.b
954+
_t.c
955+
956+
Furthermore, defining the short-circuiting behavior that way would have
957+
been a deviation from the already established behavior in
958+
languages like JS [#js_short_circuiting]_ and C# [#csharp]_.
961959

962960

963961
Common objections

0 commit comments

Comments
 (0)