From 7d892d328753fe7153b17b4072020a9f57e28c3c Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 10 Sep 2025 17:59:30 +0200 Subject: [PATCH 01/12] Start on Subscriptions and slicing Co-authored-by: Blaise Pabon --- Doc/reference/datamodel.rst | 62 ++++++ Doc/reference/expressions.rst | 215 +++++++++++-------- Parser/parser.c | 380 ++++++++++++++++------------------ 3 files changed, 364 insertions(+), 293 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index da04cfde3bd587..d87f3b2c058ede 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -3202,6 +3202,66 @@ through the object's keys; for sequences, it should iterate through the values. .. method:: object.__getitem__(self, key) +<<<< + +For built-in objects, there are two types of objects that support subscription +via :meth:`~object.__getitem__`: + +1. Mappings. If the primary is a :term:`mapping`, the expression list must + evaluate to an object whose value is one of the keys of the mapping, and the + subscription selects the value in the mapping that corresponds to that key. + An example of a builtin mapping class is the :class:`dict` class. +2. Sequences. If the primary is a :term:`sequence`, the expression list must + evaluate to an :class:`int` or a :class:`slice` (as discussed in the + following section). Examples of builtin sequence classes include the + :class:`str`, :class:`list` and :class:`tuple` classes. + +The formal syntax makes no special provision for negative indices in +:term:`sequences `. However, built-in sequences all provide a :meth:`~object.__getitem__` +method that interprets negative indices by adding the length of the sequence +to the index so that, for example, ``x[-1]`` selects the last item of ``x``. The +resulting value must be a nonnegative integer less than the number of items in +the sequence, and the subscription selects the item whose index is that value +(counting from zero). Since the support for negative indices and slicing +occurs in the object's :meth:`~object.__getitem__` method, subclasses overriding +this method will need to explicitly add that support. + +.. index:: + single: character + pair: string; item + +A :class:`string ` is a special kind of sequence whose items are +*characters*. A character is not a separate data type but a +string of exactly one character. + +... + + +A slicing selects a range of items in a sequence object (e.g., a string, tuple +or list). Slicings may be used as expressions or as targets in assignment or +:keyword:`del` statements. The syntax for a slicing: + + +.. index:: + single: start (slice object attribute) + single: stop (slice object attribute) + single: step (slice object attribute) + +The semantics for a slicing are as follows. The primary is indexed (using the +same :meth:`~object.__getitem__` method as +normal subscription) with a key that is constructed from the slice list, as +follows. If the slice list contains at least one comma, the key is a tuple +containing the conversion of the slice items; otherwise, the conversion of the +lone slice item is the key. The conversion of a slice item that is an +expression is that expression. The conversion of a proper slice is a slice +object (see section :ref:`types`) whose :attr:`~slice.start`, +:attr:`~slice.stop` and :attr:`~slice.step` attributes are the values of the +expressions given as lower bound, upper bound and stride, respectively, +substituting ``None`` for missing expressions. + + +==== + Called to implement evaluation of ``self[key]``. For :term:`sequence` types, the accepted keys should be integers. Optionally, they may support :class:`slice` objects as well. Negative index support is also optional. @@ -3212,6 +3272,8 @@ through the object's keys; for sequences, it should iterate through the values. :term:`mapping` types, if *key* is missing (not in the container), :exc:`KeyError` should be raised. +>>>>>> + .. note:: :keyword:`for` loops expect that an :exc:`IndexError` will be raised for diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 9aca25e3214a16..3b4e097e8003b7 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -893,7 +893,7 @@ Primaries represent the most tightly bound operations of the language. Their syntax is: .. productionlist:: python-grammar - primary: `atom` | `attributeref` | `subscription` | `slicing` | `call` + primary: `atom` | `attributeref` | `subscription` | `call` .. _attribute-references: @@ -931,9 +931,10 @@ If an :exc:`AttributeError` is raised and the object has a :meth:`!__getattr__` method, that method is called as a fallback. .. _subscriptions: +.. _slicings: -Subscriptions -------------- +Subscriptions and slicing +------------------------- .. index:: single: subscription @@ -949,62 +950,138 @@ Subscriptions pair: sequence; item The subscription of an instance of a :ref:`container class ` -will generally select an element from the container. The subscription of a -:term:`generic class ` will generally return a -:ref:`GenericAlias ` object. +will generally select an element from the container. +Subscripting a mapping will select a value using a key:: -.. productionlist:: python-grammar - subscription: `primary` "[" `flexible_expression_list` "]" + >>> digits_by_name = {'one': 1, 'two': 2} + >>> digits_by_name['two'] + 2 -When an object is subscripted, the interpreter will evaluate the primary and -the expression list. +Subscripting a sequence will select an item using an index:: + + >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five'] + >>> number_names[2] + 'two' + >>> number_names[-2] + 'four' + +The subscription of a :term:`generic class ` will generally +return a :ref:`GenericAlias ` object:: + + >>> list[str] + list[str] + +At the syntax level, all of these operations are equivalent. +The object being subscribed must be a :ref:`primary `. +When it is subscripted, the interpreter will evaluate the primary and +the the contents of the square brackets. + +The primary must evaluate to an object that supports subscription. +An object may support subscription through defining one or both of +:meth:`~object.__getitem__` and :meth:`~object.__class_getitem__`. +For more details on when ``__class_getitem__`` is called instead of +``__getitem__``, see :ref:`classgetitem-versus-getitem`. + +In its simplest form, square brackets contain a single expression. +The evaluated result of this expression will be passed to the +:meth:`~object.__getitem__` or :meth:`~object.__getitem__` method. + +The square brackets can also contain up to three expressions separated by +colons:: + + >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five'] + >>> number_names[1:3] + ['one', 'two'] + +This form of subscription is called :term:`slicing`. +Any of the expressions may be omitted, but at least one colon is necessary +for this form:: + + >>> number_names[1:] + ['one', two', 'three', 'four', 'five'] + >>> number_names[:3] + ['zero', 'one', 'two'] + >>> number_names[:] + ['zero', 'one', 'two'] + >>> number_names[::2] + ['zero', 'two', 'four'] + +When such a :term:`slice` is evaluated, the interpreter constructs a +:class:`slice` objects whose :attr:`~slice.start`, :attr:`~slice.stop` and +:attr:`~slice.step` attributes, respectively, are the results of the +expressions between the colons. +Any missing expression evaluates to :const:`None`. +This :class:`!slice` object is then passed to the :meth:`~object.__getitem__` +or :meth:`~object.__getitem__` method, as above. + +For clarity, here is a custom object that prints out the key it was subscripted +with:: + + >>> class SubscriptionDemo: + ... def __getitem__(self, key): + ... print(f'subscripted with: {key!r}') + ... + >>> demo = SubscriptionDemo() + >>> demo[1] + subscripted with: 1 + >>> demo[2:3] + subscripted with: slice(2, 3, None) + >>> demo[::'spam'] + subscripted with slice(None, None, 'spam') -The primary must evaluate to an object that supports subscription. An object -may support subscription through defining one or both of -:meth:`~object.__getitem__` and :meth:`~object.__class_getitem__`. When the -primary is subscripted, the evaluated result of the expression list will be -passed to one of these methods. For more details on when ``__class_getitem__`` -is called instead of ``__getitem__``, see :ref:`classgetitem-versus-getitem`. +The square brackets used for slicing can also contain two or more +comma-separated expressions or slices, or one expression or slice followed +by a comma. +In this case, the interpreter constructs a tuple of the results of the +expressions or slices, and passes this tuple to the :meth:`~object.__getitem__` +or :meth:`~object.__getitem__` method, as above:: -If the expression list contains at least one comma, or if any of the expressions -are starred, the expression list will evaluate to a :class:`tuple` containing -the items of the expression list. Otherwise, the expression list will evaluate -to the value of the list's sole member. + >>> demo[1, 2, 3] + subscripted with (1, 2, 3) + >>> demo['spam',] + subscripted with ('spam',) + +The square brackets can also contain a starred expression. +In this case, the interpreter unpacks the result into a tuple, and passes +this tuple to :meth:`~object.__getitem__` or :meth:`~object.__getitem__`:: + + >>> demo[*range(10)] + subscripted with (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) -.. versionchanged:: 3.11 - Expressions in an expression list may be starred. See :pep:`646`. - -For built-in objects, there are two types of objects that support subscription -via :meth:`~object.__getitem__`: - -1. Mappings. If the primary is a :term:`mapping`, the expression list must - evaluate to an object whose value is one of the keys of the mapping, and the - subscription selects the value in the mapping that corresponds to that key. - An example of a builtin mapping class is the :class:`dict` class. -2. Sequences. If the primary is a :term:`sequence`, the expression list must - evaluate to an :class:`int` or a :class:`slice` (as discussed in the - following section). Examples of builtin sequence classes include the - :class:`str`, :class:`list` and :class:`tuple` classes. - -The formal syntax makes no special provision for negative indices in -:term:`sequences `. However, built-in sequences all provide a :meth:`~object.__getitem__` -method that interprets negative indices by adding the length of the sequence -to the index so that, for example, ``x[-1]`` selects the last item of ``x``. The -resulting value must be a nonnegative integer less than the number of items in -the sequence, and the subscription selects the item whose index is that value -(counting from zero). Since the support for negative indices and slicing -occurs in the object's :meth:`~object.__getitem__` method, subclasses overriding -this method will need to explicitly add that support. -.. index:: - single: character - pair: string; item -A :class:`string ` is a special kind of sequence whose items are -*characters*. A character is not a separate data type but a -string of exactly one character. +.. grammar-snippet:: + :group: python-grammar + + subscription: `primary` '[' `slices` ']' + slices: `slice` | `tuple_slices` + tuple_slices: ','.(slice | starred_expression)+ [','] + + slice: + | proper_slice + | assignment_expression + +If *slices* contains ony one unstarred *slice* without a trailing comma, +it will evaluate to the value of that *slice*. +Otherwise, *slices* will evaluate to a :class:`tuple` containing +the items of *slices* +.. grammar-snippet:: + :group: python-grammar + + proper_slice: [`lower_bound`] ":" [`upper_bound`] [ ":" [`stride`] ] + lower_bound: `expression` + upper_bound: `expression` + stride: `expression` + +.. versionchanged:: 3.11 + Expressions in *tuple_slices* may be starred. See :pep:`646`. + +See :meth:`~object.__getitem__` documentation for how built-in types handle +subscription, including support for negative indices. + +<<< .. _slicings: Slicings @@ -1022,43 +1099,7 @@ Slicings pair: object; tuple pair: object; list -A slicing selects a range of items in a sequence object (e.g., a string, tuple -or list). Slicings may be used as expressions or as targets in assignment or -:keyword:`del` statements. The syntax for a slicing: - -.. productionlist:: python-grammar - slicing: `primary` "[" `slice_list` "]" - slice_list: `slice_item` ("," `slice_item`)* [","] - slice_item: `expression` | `proper_slice` - proper_slice: [`lower_bound`] ":" [`upper_bound`] [ ":" [`stride`] ] - lower_bound: `expression` - upper_bound: `expression` - stride: `expression` - -There is ambiguity in the formal syntax here: anything that looks like an -expression list also looks like a slice list, so any subscription can be -interpreted as a slicing. Rather than further complicating the syntax, this is -disambiguated by defining that in this case the interpretation as a subscription -takes priority over the interpretation as a slicing (this is the case if the -slice list contains no proper slice). - -.. index:: - single: start (slice object attribute) - single: stop (slice object attribute) - single: step (slice object attribute) - -The semantics for a slicing are as follows. The primary is indexed (using the -same :meth:`~object.__getitem__` method as -normal subscription) with a key that is constructed from the slice list, as -follows. If the slice list contains at least one comma, the key is a tuple -containing the conversion of the slice items; otherwise, the conversion of the -lone slice item is the key. The conversion of a slice item that is an -expression is that expression. The conversion of a proper slice is a slice -object (see section :ref:`types`) whose :attr:`~slice.start`, -:attr:`~slice.stop` and :attr:`~slice.step` attributes are the values of the -expressions given as lower bound, upper bound and stride, respectively, -substituting ``None`` for missing expressions. - +>>> .. index:: pair: object; callable diff --git a/Parser/parser.c b/Parser/parser.c index c20c368a089b33..b684e441da27cd 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -522,13 +522,12 @@ static char *soft_keywords[] = { #define _tmp_167_type 1435 #define _tmp_168_type 1436 #define _tmp_169_type 1437 -#define _tmp_170_type 1438 -#define _loop0_171_type 1439 +#define _loop0_170_type 1438 +#define _tmp_171_type 1439 #define _tmp_172_type 1440 #define _tmp_173_type 1441 #define _tmp_174_type 1442 #define _tmp_175_type 1443 -#define _tmp_176_type 1444 static mod_ty file_rule(Parser *p); static mod_ty interactive_rule(Parser *p); @@ -968,13 +967,12 @@ static void *_tmp_166_rule(Parser *p); static void *_tmp_167_rule(Parser *p); static void *_tmp_168_rule(Parser *p); static void *_tmp_169_rule(Parser *p); -static void *_tmp_170_rule(Parser *p); -static asdl_seq *_loop0_171_rule(Parser *p); +static asdl_seq *_loop0_170_rule(Parser *p); +static void *_tmp_171_rule(Parser *p); static void *_tmp_172_rule(Parser *p); static void *_tmp_173_rule(Parser *p); static void *_tmp_174_rule(Parser *p); static void *_tmp_175_rule(Parser *p); -static void *_tmp_176_rule(Parser *p); // file: statements? $ @@ -14772,7 +14770,7 @@ primary_raw(Parser *p) return _res; } -// slices: slice !',' | ','.(slice | starred_expression)+ ','? +// slices: slice !',' | ','.slice+ ','? static expr_ty slices_rule(Parser *p) { @@ -14820,22 +14818,22 @@ slices_rule(Parser *p) D(fprintf(stderr, "%*c%s slices[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slice !','")); } - { // ','.(slice | starred_expression)+ ','? + { // ','.slice+ ','? if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> slices[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(slice | starred_expression)+ ','?")); + D(fprintf(stderr, "%*c> slices[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.slice+ ','?")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings asdl_expr_seq* a; if ( - (a = (asdl_expr_seq*)_gather_64_rule(p)) // ','.(slice | starred_expression)+ + (a = (asdl_expr_seq*)_gather_64_rule(p)) // ','.slice+ && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) { - D(fprintf(stderr, "%*c+ slices[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(slice | starred_expression)+ ','?")); + D(fprintf(stderr, "%*c+ slices[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.slice+ ','?")); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p); if (_token == NULL) { p->level--; @@ -14855,7 +14853,7 @@ slices_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s slices[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.(slice | starred_expression)+ ','?")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.slice+ ','?")); } _res = NULL; done: @@ -14863,7 +14861,10 @@ slices_rule(Parser *p) return _res; } -// slice: expression? ':' expression? [':' expression?] | named_expression +// slice: +// | expression? ':' expression? [':' expression?] +// | named_expression +// | starred_expression static expr_ty slice_rule(Parser *p) { @@ -14951,6 +14952,30 @@ slice_rule(Parser *p) D(fprintf(stderr, "%*c%s slice[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "named_expression")); } + { // starred_expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> slice[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + expr_ty a; + if ( + (a = starred_expression_rule(p)) // starred_expression + ) + { + D(fprintf(stderr, "%*c+ slice[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + _res = a; + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s slice[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); + } _res = NULL; done: p->level--; @@ -31269,7 +31294,7 @@ _tmp_62_rule(Parser *p) return _res; } -// _loop0_63: ',' (slice | starred_expression) +// _loop0_63: ',' slice static asdl_seq * _loop0_63_rule(Parser *p) { @@ -31291,18 +31316,18 @@ _loop0_63_rule(Parser *p) } Py_ssize_t _children_capacity = 1; Py_ssize_t _n = 0; - { // ',' (slice | starred_expression) + { // ',' slice if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_63[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (slice | starred_expression)")); + D(fprintf(stderr, "%*c> _loop0_63[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' slice")); Token * _literal; - void *elem; + expr_ty elem; while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_162_rule(p)) // slice | starred_expression + (elem = slice_rule(p)) // slice ) { _res = elem; @@ -31329,7 +31354,7 @@ _loop0_63_rule(Parser *p) } p->mark = _mark; D(fprintf(stderr, "%*c%s _loop0_63[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' (slice | starred_expression)")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' slice")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); if (!_seq) { @@ -31345,7 +31370,7 @@ _loop0_63_rule(Parser *p) return _seq; } -// _gather_64: (slice | starred_expression) _loop0_63 +// _gather_64: slice _loop0_63 static asdl_seq * _gather_64_rule(Parser *p) { @@ -31358,27 +31383,27 @@ _gather_64_rule(Parser *p) } asdl_seq * _res = NULL; int _mark = p->mark; - { // (slice | starred_expression) _loop0_63 + { // slice _loop0_63 if (p->error_indicator) { p->level--; return NULL; } - D(fprintf(stderr, "%*c> _gather_64[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(slice | starred_expression) _loop0_63")); - void *elem; + D(fprintf(stderr, "%*c> _gather_64[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice _loop0_63")); + expr_ty elem; asdl_seq * seq; if ( - (elem = _tmp_162_rule(p)) // slice | starred_expression + (elem = slice_rule(p)) // slice && (seq = _loop0_63_rule(p)) // _loop0_63 ) { - D(fprintf(stderr, "%*c+ _gather_64[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(slice | starred_expression) _loop0_63")); + D(fprintf(stderr, "%*c+ _gather_64[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice _loop0_63")); _res = _PyPegen_seq_insert_in_front(p, elem, seq); goto done; } p->mark = _mark; D(fprintf(stderr, "%*c%s _gather_64[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(slice | starred_expression) _loop0_63")); + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slice _loop0_63")); } _res = NULL; done: @@ -32812,12 +32837,12 @@ _loop0_86_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_86[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "('if' disjunction)")); - void *_tmp_163_var; + void *_tmp_162_var; while ( - (_tmp_163_var = _tmp_163_rule(p)) // 'if' disjunction + (_tmp_162_var = _tmp_162_rule(p)) // 'if' disjunction ) { - _res = _tmp_163_var; + _res = _tmp_162_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -32943,7 +32968,7 @@ _loop0_88_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_164_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_163_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' ) { _res = elem; @@ -33009,7 +33034,7 @@ _gather_89_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_164_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' + (elem = _tmp_163_rule(p)) // starred_expression | (assignment_expression | expression !':=') !'=' && (seq = _loop0_88_rule(p)) // _loop0_88 ) @@ -33336,12 +33361,12 @@ _loop0_95_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop0_95[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_165_var; + void *_tmp_164_var; while ( - (_tmp_165_var = _tmp_165_rule(p)) // ',' star_target + (_tmp_164_var = _tmp_164_rule(p)) // ',' star_target ) { - _res = _tmp_165_var; + _res = _tmp_164_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33520,12 +33545,12 @@ _loop1_98_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_98[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' star_target)")); - void *_tmp_165_var; + void *_tmp_164_var; while ( - (_tmp_165_var = _tmp_165_rule(p)) // ',' star_target + (_tmp_164_var = _tmp_164_rule(p)) // ',' star_target ) { - _res = _tmp_165_var; + _res = _tmp_164_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -33900,13 +33925,13 @@ _tmp_105_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _tmp_105[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); - void *_tmp_166_var; + void *_tmp_165_var; if ( - (_tmp_166_var = _tmp_166_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs + (_tmp_165_var = _tmp_165_rule(p)) // ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs ) { D(fprintf(stderr, "%*c+ _tmp_105[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs)")); - _res = _tmp_166_var; + _res = _tmp_165_var; goto done; } p->mark = _mark; @@ -33971,7 +33996,7 @@ _loop0_106_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_167_rule(p)) // starred_expression !'=' + (elem = _tmp_166_rule(p)) // starred_expression !'=' ) { _res = elem; @@ -34036,7 +34061,7 @@ _gather_107_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_167_rule(p)) // starred_expression !'=' + (elem = _tmp_166_rule(p)) // starred_expression !'=' && (seq = _loop0_106_rule(p)) // _loop0_106 ) @@ -34358,12 +34383,12 @@ _loop1_113_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _loop1_113[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(!STRING expression_without_invalid)")); - void *_tmp_168_var; + void *_tmp_167_var; while ( - (_tmp_168_var = _tmp_168_rule(p)) // !STRING expression_without_invalid + (_tmp_167_var = _tmp_167_rule(p)) // !STRING expression_without_invalid ) { - _res = _tmp_168_var; + _res = _tmp_167_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -35262,15 +35287,15 @@ _tmp_126_rule(Parser *p) } D(fprintf(stderr, "%*c> _tmp_126[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); Token * _literal; - void *_tmp_169_var; + void *_tmp_168_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_169_var = _tmp_169_rule(p)) // ')' | '**' + (_tmp_168_var = _tmp_168_rule(p)) // ')' | '**' ) { D(fprintf(stderr, "%*c+ _tmp_126[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (')' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_169_var); + _res = _PyPegen_dummy_name(p, _literal, _tmp_168_var); goto done; } p->mark = _mark; @@ -35686,15 +35711,15 @@ _tmp_133_rule(Parser *p) } D(fprintf(stderr, "%*c> _tmp_133[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); Token * _literal; - void *_tmp_170_var; + void *_tmp_169_var; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (_tmp_170_var = _tmp_170_rule(p)) // ':' | '**' + (_tmp_169_var = _tmp_169_rule(p)) // ':' | '**' ) { D(fprintf(stderr, "%*c+ _tmp_133[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' (':' | '**')")); - _res = _PyPegen_dummy_name(p, _literal, _tmp_170_var); + _res = _PyPegen_dummy_name(p, _literal, _tmp_169_var); goto done; } p->mark = _mark; @@ -35783,20 +35808,20 @@ _tmp_135_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _tmp_135[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); - asdl_seq * _loop0_171_var; + asdl_seq * _loop0_170_var; void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty bitwise_or_var; if ( (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or && - (_loop0_171_var = _loop0_171_rule(p)) // ((',' bitwise_or))* + (_loop0_170_var = _loop0_170_rule(p)) // ((',' bitwise_or))* && (_opt_var = _PyPegen_expect_token(p, 12), !p->error_indicator) // ','? ) { D(fprintf(stderr, "%*c+ _tmp_135[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "bitwise_or ((',' bitwise_or))* ','?")); - _res = _PyPegen_dummy_name(p, bitwise_or_var, _loop0_171_var, _opt_var); + _res = _PyPegen_dummy_name(p, bitwise_or_var, _loop0_170_var, _opt_var); goto done; } p->mark = _mark; @@ -35945,16 +35970,16 @@ _tmp_138_rule(Parser *p) return NULL; } D(fprintf(stderr, "%*c> _tmp_138[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME (',' | ')' | NEWLINE)")); - void *_tmp_172_var; + void *_tmp_171_var; expr_ty name_var; if ( (name_var = _PyPegen_name_token(p)) // NAME && - (_tmp_172_var = _tmp_172_rule(p)) // ',' | ')' | NEWLINE + (_tmp_171_var = _tmp_171_rule(p)) // ',' | ')' | NEWLINE ) { D(fprintf(stderr, "%*c+ _tmp_138[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME (',' | ')' | NEWLINE)")); - _res = _PyPegen_dummy_name(p, name_var, _tmp_172_var); + _res = _PyPegen_dummy_name(p, name_var, _tmp_171_var); goto done; } p->mark = _mark; @@ -36000,7 +36025,7 @@ _loop0_139_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_173_rule(p)) // expression ['as' star_target] + (elem = _tmp_172_rule(p)) // expression ['as' star_target] ) { _res = elem; @@ -36065,7 +36090,7 @@ _gather_140_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_173_rule(p)) // expression ['as' star_target] + (elem = _tmp_172_rule(p)) // expression ['as' star_target] && (seq = _loop0_139_rule(p)) // _loop0_139 ) @@ -36117,7 +36142,7 @@ _loop0_141_rule(Parser *p) while ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' && - (elem = _tmp_174_rule(p)) // expressions ['as' star_target] + (elem = _tmp_173_rule(p)) // expressions ['as' star_target] ) { _res = elem; @@ -36182,7 +36207,7 @@ _gather_142_rule(Parser *p) void *elem; asdl_seq * seq; if ( - (elem = _tmp_174_rule(p)) // expressions ['as' star_target] + (elem = _tmp_173_rule(p)) // expressions ['as' star_target] && (seq = _loop0_141_rule(p)) // _loop0_141 ) @@ -37366,66 +37391,9 @@ _tmp_161_rule(Parser *p) return _res; } -// _tmp_162: slice | starred_expression +// _tmp_162: 'if' disjunction static void * _tmp_162_rule(Parser *p) -{ - if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { - _Pypegen_stack_overflow(p); - } - if (p->error_indicator) { - p->level--; - return NULL; - } - void * _res = NULL; - int _mark = p->mark; - { // slice - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "slice")); - expr_ty slice_var; - if ( - (slice_var = slice_rule(p)) // slice - ) - { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "slice")); - _res = slice_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "slice")); - } - { // starred_expression - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); - expr_ty starred_expression_var; - if ( - (starred_expression_var = starred_expression_rule(p)) // starred_expression - ) - { - D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); - _res = starred_expression_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); - } - _res = NULL; - done: - p->level--; - return _res; -} - -// _tmp_163: 'if' disjunction -static void * -_tmp_163_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37441,7 +37409,7 @@ _tmp_163_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c> _tmp_162[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); Token * _keyword; expr_ty z; if ( @@ -37450,7 +37418,7 @@ _tmp_163_rule(Parser *p) (z = disjunction_rule(p)) // disjunction ) { - D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); + D(fprintf(stderr, "%*c+ _tmp_162[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'if' disjunction")); _res = z; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37460,7 +37428,7 @@ _tmp_163_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_162[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'if' disjunction")); } _res = NULL; @@ -37469,9 +37437,9 @@ _tmp_163_rule(Parser *p) return _res; } -// _tmp_164: starred_expression | (assignment_expression | expression !':=') !'=' +// _tmp_163: starred_expression | (assignment_expression | expression !':=') !'=' static void * -_tmp_164_rule(Parser *p) +_tmp_163_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37487,18 +37455,18 @@ _tmp_164_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression ) { - D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); + D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression")); } { // (assignment_expression | expression !':=') !'=' @@ -37506,7 +37474,7 @@ _tmp_164_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + D(fprintf(stderr, "%*c> _tmp_163[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); void *_tmp_87_var; if ( (_tmp_87_var = _tmp_87_rule(p)) // assignment_expression | expression !':=' @@ -37514,12 +37482,12 @@ _tmp_164_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); + D(fprintf(stderr, "%*c+ _tmp_163[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "(assignment_expression | expression !':=') !'='")); _res = _tmp_87_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_163[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(assignment_expression | expression !':=') !'='")); } _res = NULL; @@ -37528,9 +37496,9 @@ _tmp_164_rule(Parser *p) return _res; } -// _tmp_165: ',' star_target +// _tmp_164: ',' star_target static void * -_tmp_165_rule(Parser *p) +_tmp_164_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37546,7 +37514,7 @@ _tmp_165_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c> _tmp_164[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' star_target")); Token * _literal; expr_ty c; if ( @@ -37555,7 +37523,7 @@ _tmp_165_rule(Parser *p) (c = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); + D(fprintf(stderr, "%*c+ _tmp_164[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' star_target")); _res = c; if (_res == NULL && PyErr_Occurred()) { p->error_indicator = 1; @@ -37565,7 +37533,7 @@ _tmp_165_rule(Parser *p) goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_164[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' star_target")); } _res = NULL; @@ -37574,10 +37542,10 @@ _tmp_165_rule(Parser *p) return _res; } -// _tmp_166: +// _tmp_165: // | ','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs static void * -_tmp_166_rule(Parser *p) +_tmp_165_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37593,7 +37561,7 @@ _tmp_166_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + D(fprintf(stderr, "%*c> _tmp_165[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); asdl_seq * _gather_89_var; Token * _literal; asdl_seq* kwargs_var; @@ -37605,12 +37573,12 @@ _tmp_166_rule(Parser *p) (kwargs_var = kwargs_rule(p)) // kwargs ) { - D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); + D(fprintf(stderr, "%*c+ _tmp_165[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); _res = _PyPegen_dummy_name(p, _gather_89_var, _literal, kwargs_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_165[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','.(starred_expression | (assignment_expression | expression !':=') !'=')+ ',' kwargs")); } _res = NULL; @@ -37619,9 +37587,9 @@ _tmp_166_rule(Parser *p) return _res; } -// _tmp_167: starred_expression !'=' +// _tmp_166: starred_expression !'=' static void * -_tmp_167_rule(Parser *p) +_tmp_166_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37637,7 +37605,7 @@ _tmp_167_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); + D(fprintf(stderr, "%*c> _tmp_166[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); expr_ty starred_expression_var; if ( (starred_expression_var = starred_expression_rule(p)) // starred_expression @@ -37645,12 +37613,12 @@ _tmp_167_rule(Parser *p) _PyPegen_lookahead_with_int(0, _PyPegen_expect_token, p, 22) // token='=' ) { - D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); + D(fprintf(stderr, "%*c+ _tmp_166[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "starred_expression !'='")); _res = starred_expression_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_166[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "starred_expression !'='")); } _res = NULL; @@ -37659,9 +37627,9 @@ _tmp_167_rule(Parser *p) return _res; } -// _tmp_168: !STRING expression_without_invalid +// _tmp_167: !STRING expression_without_invalid static void * -_tmp_168_rule(Parser *p) +_tmp_167_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37677,7 +37645,7 @@ _tmp_168_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); + D(fprintf(stderr, "%*c> _tmp_167[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); expr_ty expression_without_invalid_var; if ( _PyPegen_lookahead(0, _PyPegen_string_token, p) @@ -37685,12 +37653,12 @@ _tmp_168_rule(Parser *p) (expression_without_invalid_var = expression_without_invalid_rule(p)) // expression_without_invalid ) { - D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); + D(fprintf(stderr, "%*c+ _tmp_167[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "!STRING expression_without_invalid")); _res = expression_without_invalid_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_167[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "!STRING expression_without_invalid")); } _res = NULL; @@ -37699,9 +37667,9 @@ _tmp_168_rule(Parser *p) return _res; } -// _tmp_169: ')' | '**' +// _tmp_168: ')' | '**' static void * -_tmp_169_rule(Parser *p) +_tmp_168_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37717,18 +37685,18 @@ _tmp_169_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // '**' @@ -37736,18 +37704,18 @@ _tmp_169_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_168[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_168[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_168[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } _res = NULL; @@ -37756,9 +37724,9 @@ _tmp_169_rule(Parser *p) return _res; } -// _tmp_170: ':' | '**' +// _tmp_169: ':' | '**' static void * -_tmp_170_rule(Parser *p) +_tmp_169_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37774,18 +37742,18 @@ _tmp_170_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "':'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 11)) // token=':' ) { - D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); + D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "':'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "':'")); } { // '**' @@ -37793,18 +37761,18 @@ _tmp_170_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c> _tmp_169[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'**'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 35)) // token='**' ) { - D(fprintf(stderr, "%*c+ _tmp_170[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); + D(fprintf(stderr, "%*c+ _tmp_169[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'**'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_170[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_169[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'**'")); } _res = NULL; @@ -37813,9 +37781,9 @@ _tmp_170_rule(Parser *p) return _res; } -// _loop0_171: (',' bitwise_or) +// _loop0_170: (',' bitwise_or) static asdl_seq * -_loop0_171_rule(Parser *p) +_loop0_170_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37840,13 +37808,13 @@ _loop0_171_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _loop0_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' bitwise_or)")); - void *_tmp_175_var; + D(fprintf(stderr, "%*c> _loop0_170[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "(',' bitwise_or)")); + void *_tmp_174_var; while ( - (_tmp_175_var = _tmp_175_rule(p)) // ',' bitwise_or + (_tmp_174_var = _tmp_174_rule(p)) // ',' bitwise_or ) { - _res = _tmp_175_var; + _res = _tmp_174_var; if (_n == _children_capacity) { _children_capacity *= 2; void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *)); @@ -37863,7 +37831,7 @@ _loop0_171_rule(Parser *p) _mark = p->mark; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _loop0_171[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _loop0_170[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "(',' bitwise_or)")); } asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena); @@ -37880,9 +37848,9 @@ _loop0_171_rule(Parser *p) return _seq; } -// _tmp_172: ',' | ')' | NEWLINE +// _tmp_171: ',' | ')' | NEWLINE static void * -_tmp_172_rule(Parser *p) +_tmp_171_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37898,18 +37866,18 @@ _tmp_172_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "','")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 12)) // token=',' ) { - D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); + D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "','")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "','")); } { // ')' @@ -37917,18 +37885,18 @@ _tmp_172_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "')'")); Token * _literal; if ( (_literal = _PyPegen_expect_token(p, 8)) // token=')' ) { - D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); + D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "')'")); _res = _literal; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "')'")); } { // NEWLINE @@ -37936,18 +37904,18 @@ _tmp_172_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c> _tmp_171[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NEWLINE")); Token * newline_var; if ( (newline_var = _PyPegen_expect_token(p, NEWLINE)) // token='NEWLINE' ) { - D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); + D(fprintf(stderr, "%*c+ _tmp_171[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NEWLINE")); _res = newline_var; goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_171[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NEWLINE")); } _res = NULL; @@ -37956,9 +37924,9 @@ _tmp_172_rule(Parser *p) return _res; } -// _tmp_173: expression ['as' star_target] +// _tmp_172: expression ['as' star_target] static void * -_tmp_173_rule(Parser *p) +_tmp_172_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -37974,22 +37942,22 @@ _tmp_173_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_172[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expression_var; if ( (expression_var = expression_rule(p)) // expression && - (_opt_var = _tmp_176_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_175_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_172[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression ['as' star_target]")); _res = _PyPegen_dummy_name(p, expression_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_172[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ['as' star_target]")); } _res = NULL; @@ -37998,9 +37966,9 @@ _tmp_173_rule(Parser *p) return _res; } -// _tmp_174: expressions ['as' star_target] +// _tmp_173: expressions ['as' star_target] static void * -_tmp_174_rule(Parser *p) +_tmp_173_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38016,22 +37984,22 @@ _tmp_174_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c> _tmp_173[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); void *_opt_var; UNUSED(_opt_var); // Silence compiler warnings expr_ty expressions_var; if ( (expressions_var = expressions_rule(p)) // expressions && - (_opt_var = _tmp_176_rule(p), !p->error_indicator) // ['as' star_target] + (_opt_var = _tmp_175_rule(p), !p->error_indicator) // ['as' star_target] ) { - D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); + D(fprintf(stderr, "%*c+ _tmp_173[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expressions ['as' star_target]")); _res = _PyPegen_dummy_name(p, expressions_var, _opt_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_173[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expressions ['as' star_target]")); } _res = NULL; @@ -38040,9 +38008,9 @@ _tmp_174_rule(Parser *p) return _res; } -// _tmp_175: ',' bitwise_or +// _tmp_174: ',' bitwise_or static void * -_tmp_175_rule(Parser *p) +_tmp_174_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38058,7 +38026,7 @@ _tmp_175_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); + D(fprintf(stderr, "%*c> _tmp_174[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); Token * _literal; expr_ty bitwise_or_var; if ( @@ -38067,12 +38035,12 @@ _tmp_175_rule(Parser *p) (bitwise_or_var = bitwise_or_rule(p)) // bitwise_or ) { - D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); + D(fprintf(stderr, "%*c+ _tmp_174[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "',' bitwise_or")); _res = _PyPegen_dummy_name(p, _literal, bitwise_or_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_174[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "',' bitwise_or")); } _res = NULL; @@ -38081,9 +38049,9 @@ _tmp_175_rule(Parser *p) return _res; } -// _tmp_176: 'as' star_target +// _tmp_175: 'as' star_target static void * -_tmp_176_rule(Parser *p) +_tmp_175_rule(Parser *p) { if (p->level++ == MAXSTACK || _Py_ReachedRecursionLimitWithMargin(PyThreadState_Get(), 1)) { _Pypegen_stack_overflow(p); @@ -38099,7 +38067,7 @@ _tmp_176_rule(Parser *p) p->level--; return NULL; } - D(fprintf(stderr, "%*c> _tmp_176[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c> _tmp_175[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'as' star_target")); Token * _keyword; expr_ty star_target_var; if ( @@ -38108,12 +38076,12 @@ _tmp_176_rule(Parser *p) (star_target_var = star_target_rule(p)) // star_target ) { - D(fprintf(stderr, "%*c+ _tmp_176[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); + D(fprintf(stderr, "%*c+ _tmp_175[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'as' star_target")); _res = _PyPegen_dummy_name(p, _keyword, star_target_var); goto done; } p->mark = _mark; - D(fprintf(stderr, "%*c%s _tmp_176[%d-%d]: %s failed!\n", p->level, ' ', + D(fprintf(stderr, "%*c%s _tmp_175[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'as' star_target")); } _res = NULL; From bb7070737de222d429cb51b1b09c2854bac06b4f Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 24 Sep 2025 18:04:23 +0200 Subject: [PATCH 02/12] Continue Subscriptions and slicing Co-authored-by: Blaise Pabon --- Doc/reference/datamodel.rst | 3 + Doc/reference/expressions.rst | 101 +++++++++++++++++++--------------- 2 files changed, 59 insertions(+), 45 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index d87f3b2c058ede..7134ccf4e18ee8 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -294,6 +294,7 @@ floating-point numbers. The same caveats apply as for floating-point numbers. The real and imaginary parts of a complex number ``z`` can be retrieved through the read-only attributes ``z.real`` and ``z.imag``. +.. _datamodel-sequences: Sequences --------- @@ -465,6 +466,8 @@ Frozen sets a dictionary key. +.. _datamodel-mappings: + Mappings -------- diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 3b4e097e8003b7..8f4f9593a9e197 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -949,54 +949,67 @@ Subscriptions and slicing pair: object; dictionary pair: sequence; item -The subscription of an instance of a :ref:`container class ` -will generally select an element from the container. -Subscripting a mapping will select a value using a key:: +:dfn:`Subscription` is an operation of selecting an element from a +:ref:`container `. +The subscription syntax uses square brackets, which contain a :dfn:`key`, +:dfn:`index` or :dfn:`slice` -- that is, an expression by which the requested +element is looked up. +For example, subscription is used to get a value from a +:ref:`mapping ` using a key, or to get an item from a +:ref:`sequence ` using an index:: >>> digits_by_name = {'one': 1, 'two': 2} >>> digits_by_name['two'] 2 -Subscripting a sequence will select an item using an index:: - >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five'] >>> number_names[2] 'two' >>> number_names[-2] 'four' -The subscription of a :term:`generic class ` will generally -return a :ref:`GenericAlias ` object:: +The subscription syntax is also used to provide type arguments for +:term:`generic types `:: - >>> list[str] - list[str] + >>> seq: list[str] -At the syntax level, all of these operations are equivalent. -The object being subscribed must be a :ref:`primary `. -When it is subscripted, the interpreter will evaluate the primary and -the the contents of the square brackets. +Syntactically, the object being subscribed is a :ref:`primary `. +At runtime, the interpreter will evaluate the primary and +the contents of the square brackets, and call the primary's +:meth:`~object.__getitem__` or :meth:`~object.__class_getitem__` method. +For more details on which of these methods is called, see +:ref:`classgetitem-versus-getitem`. -The primary must evaluate to an object that supports subscription. -An object may support subscription through defining one or both of -:meth:`~object.__getitem__` and :meth:`~object.__class_getitem__`. -For more details on when ``__class_getitem__`` is called instead of -``__getitem__``, see :ref:`classgetitem-versus-getitem`. +To show how subscription works, we can define a custom object that +implements :meth:`~object.__getitem__` and prints out the key it +was subscripted with:: -In its simplest form, square brackets contain a single expression. + >>> class SubscriptionDemo: + ... def __getitem__(self, key): + ... print(f'subscripted with: {key!r}') + ... + >>> demo = SubscriptionDemo() + >>> demo[1] + subscripted with: 1 + +In the simplest form of subscription, the square brackets contain a single +expression. The evaluated result of this expression will be passed to the -:meth:`~object.__getitem__` or :meth:`~object.__getitem__` method. +:meth:`~object.__getitem__` or :meth:`~object.__class_getitem__` method:: -The square brackets can also contain up to three expressions separated by -colons:: + >>> demo[1 + 2] + subscripted with: 3 + +A more advanced form of subscription, :dfn:`slicing`, is commonly used +to extract a portion of a :ref:`sequence `. +In this form, the square brackets contain a :term:`slice`: up to three +expressions separated by colons. +Any of the expressions may be omitted, but a slice must contain at least one +colon:: >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five'] >>> number_names[1:3] ['one', 'two'] - -This form of subscription is called :term:`slicing`. -Any of the expressions may be omitted, but at least one colon is necessary -for this form:: - >>> number_names[1:] ['one', two', 'three', 'four', 'five'] >>> number_names[:3] @@ -1006,35 +1019,27 @@ for this form:: >>> number_names[::2] ['zero', 'two', 'four'] -When such a :term:`slice` is evaluated, the interpreter constructs a -:class:`slice` objects whose :attr:`~slice.start`, :attr:`~slice.stop` and +When a slice is evaluated, the interpreter constructs a :class:`slice` object +whose :attr:`~slice.start`, :attr:`~slice.stop` and :attr:`~slice.step` attributes, respectively, are the results of the expressions between the colons. Any missing expression evaluates to :const:`None`. This :class:`!slice` object is then passed to the :meth:`~object.__getitem__` -or :meth:`~object.__getitem__` method, as above. - -For clarity, here is a custom object that prints out the key it was subscripted -with:: +or :meth:`~object.__class_getitem__` method, as above. :: - >>> class SubscriptionDemo: - ... def __getitem__(self, key): - ... print(f'subscripted with: {key!r}') - ... - >>> demo = SubscriptionDemo() - >>> demo[1] - subscripted with: 1 >>> demo[2:3] subscripted with: slice(2, 3, None) >>> demo[::'spam'] - subscripted with slice(None, None, 'spam') + subscripted with: slice(None, None, 'spam') -The square brackets used for slicing can also contain two or more +The square brackets used for subscription can also contain two or more comma-separated expressions or slices, or one expression or slice followed by a comma. -In this case, the interpreter constructs a tuple of the results of the +This form is commonly used with numerical libraries for slicing +multi-dimensional data. +In this case, the interpreter constructs a :class:`tuple` of the results of the expressions or slices, and passes this tuple to the :meth:`~object.__getitem__` -or :meth:`~object.__getitem__` method, as above:: +or :meth:`~object.__class_getitem__` method, as above:: >>> demo[1, 2, 3] subscripted with (1, 2, 3) @@ -1043,11 +1048,17 @@ or :meth:`~object.__getitem__` method, as above:: The square brackets can also contain a starred expression. In this case, the interpreter unpacks the result into a tuple, and passes -this tuple to :meth:`~object.__getitem__` or :meth:`~object.__getitem__`:: +this tuple to :meth:`~object.__getitem__` or :meth:`~object.__class_getitem__`:: >>> demo[*range(10)] subscripted with (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) +Starred expressions may be combined with comma-separated expressions +and slices::: + + >>> demo['a', 'b', *range(3), 'c'] + subscripted with: ('a', 'b', 0, 1, 2, 'c') + .. grammar-snippet:: From 0cd76e6f84135c53bfceabac70ac015489afa4a6 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 29 Oct 2025 17:12:12 +0100 Subject: [PATCH 03/12] Reorganize, add sections Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> --- Doc/reference/expressions.rst | 143 ++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 67 deletions(-) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 8f4f9593a9e197..0de6502b88779c 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -949,34 +949,45 @@ Subscriptions and slicing pair: object; dictionary pair: sequence; item -:dfn:`Subscription` is an operation of selecting an element from a -:ref:`container `. -The subscription syntax uses square brackets, which contain a :dfn:`key`, -:dfn:`index` or :dfn:`slice` -- that is, an expression by which the requested -element is looked up. -For example, subscription is used to get a value from a -:ref:`mapping ` using a key, or to get an item from a -:ref:`sequence ` using an index:: +The :dfn:`subscription` syntax is usually used for selecting an element from a +:ref:`container ` -- for example, to get a value from +a :class:`dict`:: >>> digits_by_name = {'one': 1, 'two': 2} - >>> digits_by_name['two'] + >>> digits_by_name['two'] # Subscripting a dictionary using the key 'two' 2 +In the subscription syntax, the object being subscribed -- a +:ref:`primary ` -- is followed by square brackets. +In the simplest case, the brackets contain a single expression. + +When getting a value from a :ref:`mapping `, like in the +:class:`~dict` example above, the expression in brackets is called a *key*. +When when getting an item from a :ref:`sequence `, +the expression is called an *index*:: + >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five'] - >>> number_names[2] + >>> number_names[2] # Subscripting a list using the index 2 'two' >>> number_names[-2] 'four' +Syntactically, there's no difference between a *key* and an *index*. + The subscription syntax is also used to provide type arguments for -:term:`generic types `:: +:term:`generic types `, even though types are not (necessarily) +sequences:: - >>> seq: list[str] + >>> list[str] # Parameterizing `list` using the type argument `str` + list[str] + +Syntactically, there's also no difference between a *type argument* and a +key or index. -Syntactically, the object being subscribed is a :ref:`primary `. At runtime, the interpreter will evaluate the primary and -the contents of the square brackets, and call the primary's -:meth:`~object.__getitem__` or :meth:`~object.__class_getitem__` method. +the expression in the square brackets, and call the primary's +:meth:`~object.__getitem__` or :meth:`~object.__class_getitem__` method +with the contents of the square brackets as argument. For more details on which of these methods is called, see :ref:`classgetitem-versus-getitem`. @@ -991,14 +1002,29 @@ was subscripted with:: >>> demo = SubscriptionDemo() >>> demo[1] subscripted with: 1 + >>> demo['a' * 3] + subscripted with: 'aaa' -In the simplest form of subscription, the square brackets contain a single -expression. -The evaluated result of this expression will be passed to the -:meth:`~object.__getitem__` or :meth:`~object.__class_getitem__` method:: +See :meth:`~object.__getitem__` documentation for how built-in types handle +subscription, including support for negative indices. - >>> demo[1 + 2] - subscripted with: 3 + +.. index:: + single: slicing + single: slice + single: : (colon); slicing + single: , (comma); slicing + +.. index:: + pair: object; sequence + pair: object; string + pair: object; tuple + pair: object; list + +.. _slicings: + +Slicing +^^^^^^^ A more advanced form of subscription, :dfn:`slicing`, is commonly used to extract a portion of a :ref:`sequence `. @@ -1032,20 +1058,35 @@ or :meth:`~object.__class_getitem__` method, as above. :: >>> demo[::'spam'] subscripted with: slice(None, None, 'spam') + +Tuple subscription +^^^^^^^^^^^^^^^^^^ + The square brackets used for subscription can also contain two or more -comma-separated expressions or slices, or one expression or slice followed -by a comma. +comma-separated expressions or slices:: + + >>> demo[1, 2, 3] + subscripted with (1, 2, 3) + This form is commonly used with numerical libraries for slicing multi-dimensional data. In this case, the interpreter constructs a :class:`tuple` of the results of the expressions or slices, and passes this tuple to the :meth:`~object.__getitem__` -or :meth:`~object.__class_getitem__` method, as above:: +or :meth:`~object.__class_getitem__` method, as above. + +The square brackets may also contain one expression or slice followed +by a comma, to specify a one-element tuple:: - >>> demo[1, 2, 3] - subscripted with (1, 2, 3) >>> demo['spam',] subscripted with ('spam',) + +"Starred" subscription +^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.11 + Expressions in *tuple_slices* may be starred. See :pep:`646`. + The square brackets can also contain a starred expression. In this case, the interpreter unpacks the result into a tuple, and passes this tuple to :meth:`~object.__getitem__` or :meth:`~object.__class_getitem__`:: @@ -1060,57 +1101,25 @@ and slices::: subscripted with: ('a', 'b', 0, 1, 2, 'c') +Formal subscription grammar +^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. grammar-snippet:: :group: python-grammar subscription: `primary` '[' `slices` ']' slices: `slice` | `tuple_slices` - tuple_slices: ','.(slice | starred_expression)+ [','] - - slice: - | proper_slice - | assignment_expression - + tuple_slices: ','.(`slice` | `starred_expression`)+ [','] + slice: `proper_slice` | `assignment_expression` + proper_slice: [`lower_bound`] ":" [`upper_bound`] [ ":" [`stride`] ] + lower_bound: `expression` + upper_bound: `expression` + stride: `expression` If *slices* contains ony one unstarred *slice* without a trailing comma, it will evaluate to the value of that *slice*. Otherwise, *slices* will evaluate to a :class:`tuple` containing -the items of *slices* - -.. grammar-snippet:: - :group: python-grammar - - proper_slice: [`lower_bound`] ":" [`upper_bound`] [ ":" [`stride`] ] - lower_bound: `expression` - upper_bound: `expression` - stride: `expression` - -.. versionchanged:: 3.11 - Expressions in *tuple_slices* may be starred. See :pep:`646`. - -See :meth:`~object.__getitem__` documentation for how built-in types handle -subscription, including support for negative indices. - -<<< -.. _slicings: - -Slicings --------- - -.. index:: - single: slicing - single: slice - single: : (colon); slicing - single: , (comma); slicing - -.. index:: - pair: object; sequence - pair: object; string - pair: object; tuple - pair: object; list - ->>> +the items of *slices*. .. index:: pair: object; callable From c8d0f9ef8437f79658252a71edfc5eaecec5d359 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 29 Oct 2025 18:12:48 +0100 Subject: [PATCH 04/12] Continue rewording --- Doc/library/functions.rst | 4 +-- Doc/reference/datamodel.rst | 60 +++++++++++++++++++---------------- Doc/reference/expressions.rst | 58 ++++++++++++++------------------- 3 files changed, 59 insertions(+), 63 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 61799e303a1639..ce4db63fa1a385 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1822,8 +1822,8 @@ are always available. They are listed here in alphabetical order. .. attribute:: slice.stop .. attribute:: slice.step - Slice objects are also generated when extended indexing syntax is used. For - example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See + Slice objects are also generated when :ref:`slicing syntax ` + is used. For example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See :func:`itertools.islice` for an alternate version that returns an :term:`iterator`. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 7134ccf4e18ee8..0abb115cd16244 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -314,12 +314,22 @@ including built-in sequences, interpret negative subscripts by adding the sequence length. For example, ``a[-2]`` equals ``a[n-2]``, the second to last item of sequence a with length ``n``. -.. index:: single: slicing +.. index:: + single: slicing + single: start (slice object attribute) + single: stop (slice object attribute) + single: step (slice object attribute) -Sequences also support slicing: ``a[i:j]`` selects all items with index *k* such -that *i* ``<=`` *k* ``<`` *j*. When used as an expression, a slice is a -sequence of the same type. The comment above about negative indexes also applies +Sequences also support slicing: ``a[start:stop]`` selects all items with index *k* such +that *start* ``<=`` *k* ``<`` *stop*. When used as an expression, a slice is a +sequence of the same type. The comment above about negative subscripts also applies to negative slice positions. +Note that no error is raised if a slice positions is larger than the length +of the sequence. + +If *start* is missing or ``None``, slicing behaves as if *start* was zero. +If *stop* is missing or ``None``, slicing behaves as if *stop* was equal to +the length of the sequence. Some sequences also support "extended slicing" with a third "step" parameter: ``a[i:j:k]`` selects all items of *a* with index *x* where ``x = i + n*k``, *n* @@ -3203,21 +3213,28 @@ through the object's keys; for sequences, it should iterate through the values. and so forth. Missing slice items are always filled in with ``None``. -.. method:: object.__getitem__(self, key) +.. method:: object.__getitem__(self, subscript) -<<<< + Called to implement *subscription*, that is, ``self[subscript]``. + See :ref:`subscriptions` for details on the syntax. -For built-in objects, there are two types of objects that support subscription -via :meth:`~object.__getitem__`: + There are two types of built-in objects that support subscription + via :meth:`~object.__getitem__`: + - **sequences**, where *subscript* (also called + *index*) should be an integer or a :class:`slice` object. + See :ref:`sequence documentation ` for the expected + behavior, including handling :class:`slice` objects and negative indices. + - **mappings**, where *subscript* is also called the *key*. + See :ref:`mapping documentation ` for the expected + behavior. -1. Mappings. If the primary is a :term:`mapping`, the expression list must - evaluate to an object whose value is one of the keys of the mapping, and the - subscription selects the value in the mapping that corresponds to that key. - An example of a builtin mapping class is the :class:`dict` class. -2. Sequences. If the primary is a :term:`sequence`, the expression list must - evaluate to an :class:`int` or a :class:`slice` (as discussed in the - following section). Examples of builtin sequence classes include the - :class:`str`, :class:`list` and :class:`tuple` classes. + If *subscript* is of an inappropriate type, :meth:`~object.__getitem__` + should raise :exc:`TypeError`. + If *subscript* has an inappropriate value, :meth:`~object.__getitem__` + should raise an :exc:`LookupError` or one of its subclasses + (:exc:`IndexError` for sequences; :exc:`KeyError` for mappings). + +<<<< The formal syntax makes no special provision for negative indices in :term:`sequences `. However, built-in sequences all provide a :meth:`~object.__getitem__` @@ -3263,17 +3280,6 @@ expressions given as lower bound, upper bound and stride, respectively, substituting ``None`` for missing expressions. -==== - - Called to implement evaluation of ``self[key]``. For :term:`sequence` types, - the accepted keys should be integers. Optionally, they may support - :class:`slice` objects as well. Negative index support is also optional. - If *key* is - of an inappropriate type, :exc:`TypeError` may be raised; if *key* is a value - outside the set of indexes for the sequence (after any special - interpretation of negative values), :exc:`IndexError` should be raised. For - :term:`mapping` types, if *key* is missing (not in the container), - :exc:`KeyError` should be raised. >>>>>> diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 0de6502b88779c..60f90a66263175 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -931,7 +931,6 @@ If an :exc:`AttributeError` is raised and the object has a :meth:`!__getattr__` method, that method is called as a fallback. .. _subscriptions: -.. _slicings: Subscriptions and slicing ------------------------- @@ -958,42 +957,31 @@ a :class:`dict`:: 2 In the subscription syntax, the object being subscribed -- a -:ref:`primary ` -- is followed by square brackets. -In the simplest case, the brackets contain a single expression. +:ref:`primary ` -- is followed by a :dfn:`subscript` in +square brackets. +In the simplest case, the subscript is single expression. -When getting a value from a :ref:`mapping `, like in the -:class:`~dict` example above, the expression in brackets is called a *key*. -When when getting an item from a :ref:`sequence `, -the expression is called an *index*:: +Depending on the type of the object being subscribed, the subscript is +sometimes called a *key* (for mappings), *index* (for sequences), +or *type argument* (for :term:`generic types `). +Syntacticall, these are all equivalent:: >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five'] >>> number_names[2] # Subscripting a list using the index 2 'two' - >>> number_names[-2] - 'four' - -Syntactically, there's no difference between a *key* and an *index*. - -The subscription syntax is also used to provide type arguments for -:term:`generic types `, even though types are not (necessarily) -sequences:: >>> list[str] # Parameterizing `list` using the type argument `str` list[str] -Syntactically, there's also no difference between a *type argument* and a -key or index. - At runtime, the interpreter will evaluate the primary and -the expression in the square brackets, and call the primary's -:meth:`~object.__getitem__` or :meth:`~object.__class_getitem__` method -with the contents of the square brackets as argument. +the subscript, and call the primary's :meth:`~object.__getitem__` or +:meth:`~object.__class_getitem__` method with the subscript as argument. For more details on which of these methods is called, see :ref:`classgetitem-versus-getitem`. To show how subscription works, we can define a custom object that -implements :meth:`~object.__getitem__` and prints out the key it -was subscripted with:: +implements :meth:`~object.__getitem__` and prints out the value of +the subscript:: >>> class SubscriptionDemo: ... def __getitem__(self, key): @@ -1006,7 +994,7 @@ was subscripted with:: subscripted with: 'aaa' See :meth:`~object.__getitem__` documentation for how built-in types handle -subscription, including support for negative indices. +subscription, including support for negative subscripts. .. index:: @@ -1028,7 +1016,7 @@ Slicing A more advanced form of subscription, :dfn:`slicing`, is commonly used to extract a portion of a :ref:`sequence `. -In this form, the square brackets contain a :term:`slice`: up to three +In this form, the subscript is a :term:`slice`: up to three expressions separated by colons. Any of the expressions may be omitted, but a slice must contain at least one colon:: @@ -1062,11 +1050,13 @@ or :meth:`~object.__class_getitem__` method, as above. :: Tuple subscription ^^^^^^^^^^^^^^^^^^ -The square brackets used for subscription can also contain two or more -comma-separated expressions or slices:: +The subscript can also be given as two or more comma-separated expressions +or slices:: >>> demo[1, 2, 3] subscripted with (1, 2, 3) + >>> demo[1:2, 3] + subscripted with (slice(1, 2, None), 3) This form is commonly used with numerical libraries for slicing multi-dimensional data. @@ -1074,7 +1064,7 @@ In this case, the interpreter constructs a :class:`tuple` of the results of the expressions or slices, and passes this tuple to the :meth:`~object.__getitem__` or :meth:`~object.__class_getitem__` method, as above. -The square brackets may also contain one expression or slice followed +The subscript may also be given as a single expression or slice followed by a comma, to specify a one-element tuple:: >>> demo['spam',] @@ -1087,7 +1077,7 @@ by a comma, to specify a one-element tuple:: .. versionadded:: 3.11 Expressions in *tuple_slices* may be starred. See :pep:`646`. -The square brackets can also contain a starred expression. +The subscript can also contain a starred expression. In this case, the interpreter unpacks the result into a tuple, and passes this tuple to :meth:`~object.__getitem__` or :meth:`~object.__class_getitem__`:: @@ -1107,8 +1097,8 @@ Formal subscription grammar .. grammar-snippet:: :group: python-grammar - subscription: `primary` '[' `slices` ']' - slices: `slice` | `tuple_slices` + subscription: `primary` '[' `subscript` ']' + subscript: `slice` | `tuple_slices` tuple_slices: ','.(`slice` | `starred_expression`)+ [','] slice: `proper_slice` | `assignment_expression` proper_slice: [`lower_bound`] ":" [`upper_bound`] [ ":" [`stride`] ] @@ -1116,10 +1106,10 @@ Formal subscription grammar upper_bound: `expression` stride: `expression` -If *slices* contains ony one unstarred *slice* without a trailing comma, +If *subscript* contains ony one unstarred *slice* without a trailing comma, it will evaluate to the value of that *slice*. -Otherwise, *slices* will evaluate to a :class:`tuple` containing -the items of *slices*. +Otherwise, *subscript* will evaluate to a :class:`tuple` containing +the items of *subscript*. .. index:: pair: object; callable From e755b5d5d7c5ea503abb05ff8730e3b64fbbc61a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 26 Nov 2025 17:23:12 +0100 Subject: [PATCH 05/12] More work here Co-authored-by: Blaise Pabon --- Doc/library/functions.rst | 20 ++++---- Doc/reference/datamodel.rst | 93 ++++++++++------------------------- Doc/reference/expressions.rst | 34 ++++++++++--- 3 files changed, 64 insertions(+), 83 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 0e51ef18d5a29c..35421e97541673 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1822,19 +1822,19 @@ are always available. They are listed here in alphabetical order. ``range(start, stop, step)``. The *start* and *step* arguments default to ``None``. - Slice objects have read-only data attributes :attr:`!start`, - :attr:`!stop`, and :attr:`!step` which merely return the argument - values (or their default). They have no other explicit functionality; - however, they are used by NumPy and other third-party packages. + Slice objects are also generated when :ref:`slicing syntax ` + is used. For example: ``a[start:stop:step]`` or ``a[start:stop, i]``. + + See :func:`itertools.islice` for an alternate version that returns an + :term:`iterator`. .. attribute:: slice.start - .. attribute:: slice.stop - .. attribute:: slice.step + slice.stop + slice.step - Slice objects are also generated when :ref:`slicing syntax ` - is used. For example: ``a[start:stop:step]`` or ``a[start:stop, i]``. See - :func:`itertools.islice` for an alternate version that returns an - :term:`iterator`. + These read-only attributes are set to the argument values + (or their default). They have no other explicit functionality; + however, they are used by NumPy and other third-party packages. .. versionchanged:: 3.12 Slice objects are now :term:`hashable` (provided :attr:`~slice.start`, diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 539d8d7f9d5f3f..a58bd74b99d8dd 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -310,6 +310,9 @@ including built-in sequences, interpret negative subscripts by adding the sequence length. For example, ``a[-2]`` equals ``a[n-2]``, the second to last item of sequence a with length ``n``. +The resulting value must be a nonnegative integer less than the number of items +in the sequence. If it is not, an :exc:`IndexError` is raised. + .. index:: single: slicing single: start (slice object attribute) @@ -356,17 +359,22 @@ Strings pair: built-in function; chr pair: built-in function; ord single: character - single: integer + pair: string; item single: Unicode - A string is a sequence of values that represent Unicode code points. - All the code points in the range ``U+0000 - U+10FFFF`` can be - represented in a string. Python doesn't have a :c:expr:`char` type; - instead, every code point in the string is represented as a string - object with length ``1``. The built-in function :func:`ord` + A string (:class:`str`) is a sequence of values that represent + :dfn:`characters`, or more formally, *Unicode code points*. + All the code points in the range ``0`` to ``0x10FFFF`` can be + represented in a string. + + Python doesn't have a dedicated *character* type. + Instead, every code point in the string is represented as a string + object with length ``1``. + + The built-in function :func:`ord` converts a code point from its string form to an integer in the - range ``0 - 10FFFF``; :func:`chr` converts an integer in the range - ``0 - 10FFFF`` to the corresponding length ``1`` string object. + range ``0`` to ``0x10FFFF``; :func:`chr` converts an integer in the range + ``0`` to ``0x10FFFF`` to the corresponding length ``1`` string object. :meth:`str.encode` can be used to convert a :class:`str` to :class:`bytes` using the given text encoding, and :meth:`bytes.decode` can be used to achieve the opposite. @@ -377,7 +385,7 @@ Tuples pair: singleton; tuple pair: empty; tuple - The items of a tuple are arbitrary Python objects. Tuples of two or + The items of a :class:`tuple` are arbitrary Python objects. Tuples of two or more items are formed by comma-separated lists of expressions. A tuple of one item (a 'singleton') can be formed by affixing a comma to an expression (an expression by itself does not create a tuple, since @@ -387,7 +395,7 @@ Tuples Bytes .. index:: bytes, byte - A bytes object is an immutable array. The items are 8-bit bytes, + A :class:`bytes` object is an immutable array. The items are 8-bit bytes, represented by integers in the range 0 <= x < 256. Bytes literals (like ``b'abc'``) and the built-in :func:`bytes` constructor can be used to create bytes objects. Also, bytes objects can be @@ -3238,7 +3246,8 @@ through the object's keys; for sequences, it should iterate through the values. See :ref:`subscriptions` for details on the syntax. There are two types of built-in objects that support subscription - via :meth:`~object.__getitem__`: + via :meth:`!__getitem__`: + - **sequences**, where *subscript* (also called *index*) should be an integer or a :class:`slice` object. See :ref:`sequence documentation ` for the expected @@ -3247,71 +3256,23 @@ through the object's keys; for sequences, it should iterate through the values. See :ref:`mapping documentation ` for the expected behavior. - If *subscript* is of an inappropriate type, :meth:`~object.__getitem__` + If *subscript* is of an inappropriate type, :meth:`!__getitem__` should raise :exc:`TypeError`. - If *subscript* has an inappropriate value, :meth:`~object.__getitem__` + If *subscript* has an inappropriate value, :meth:`!__getitem__` should raise an :exc:`LookupError` or one of its subclasses (:exc:`IndexError` for sequences; :exc:`KeyError` for mappings). -<<<< - -The formal syntax makes no special provision for negative indices in -:term:`sequences `. However, built-in sequences all provide a :meth:`~object.__getitem__` -method that interprets negative indices by adding the length of the sequence -to the index so that, for example, ``x[-1]`` selects the last item of ``x``. The -resulting value must be a nonnegative integer less than the number of items in -the sequence, and the subscription selects the item whose index is that value -(counting from zero). Since the support for negative indices and slicing -occurs in the object's :meth:`~object.__getitem__` method, subclasses overriding -this method will need to explicitly add that support. - -.. index:: - single: character - pair: string; item - -A :class:`string ` is a special kind of sequence whose items are -*characters*. A character is not a separate data type but a -string of exactly one character. - -... - - -A slicing selects a range of items in a sequence object (e.g., a string, tuple -or list). Slicings may be used as expressions or as targets in assignment or -:keyword:`del` statements. The syntax for a slicing: - - -.. index:: - single: start (slice object attribute) - single: stop (slice object attribute) - single: step (slice object attribute) - -The semantics for a slicing are as follows. The primary is indexed (using the -same :meth:`~object.__getitem__` method as -normal subscription) with a key that is constructed from the slice list, as -follows. If the slice list contains at least one comma, the key is a tuple -containing the conversion of the slice items; otherwise, the conversion of the -lone slice item is the key. The conversion of a slice item that is an -expression is that expression. The conversion of a proper slice is a slice -object (see section :ref:`types`) whose :attr:`~slice.start`, -:attr:`~slice.stop` and :attr:`~slice.step` attributes are the values of the -expressions given as lower bound, upper bound and stride, respectively, -substituting ``None`` for missing expressions. - - - ->>>>>> - .. note:: - :keyword:`for` loops expect that an :exc:`IndexError` will be raised for - illegal indexes to allow proper detection of the end of the sequence. + The sequence iteration protocol (used, for example, in :keyword:`for` + loops), expects that an :exc:`IndexError` will be raised for illegal + indexes to allow proper detection of the end of a sequence. .. note:: - When :ref:`subscripting` a *class*, the special + When :ref:`subscripting ` a *class*, the special class method :meth:`~object.__class_getitem__` may be called instead of - ``__getitem__()``. See :ref:`classgetitem-versus-getitem` for more + :meth:`!__getitem__`. See :ref:`classgetitem-versus-getitem` for more details. diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 6586fc229b1a42..8346260f602276 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -964,13 +964,13 @@ In the simplest case, the subscript is single expression. Depending on the type of the object being subscribed, the subscript is sometimes called a *key* (for mappings), *index* (for sequences), or *type argument* (for :term:`generic types `). -Syntacticall, these are all equivalent:: +Syntactically, these are all equivalent:: - >>> number_names = ['zero', 'one', 'two', 'three', 'four', 'five'] - >>> number_names[2] # Subscripting a list using the index 2 - 'two' + >>> colors = ['red', 'blue', 'green', 'black'] + >>> colors[3] # Subscripting a list using the index 3 + 'black' - >>> list[str] # Parameterizing `list` using the type argument `str` + >>> list[str] # Parameterizing the list type using the type argument str list[str] At runtime, the interpreter will evaluate the primary and @@ -994,7 +994,26 @@ the subscript:: subscripted with: 'aaa' See :meth:`~object.__getitem__` documentation for how built-in types handle -subscription, including support for negative subscripts. +subscription. + +Subscriptions may also be used as targets in :ref:`assignment ` or +:ref:`deletion ` statements. +In these cases, the interpreter will call the subscripted object's +:meth:`~object.__setitem__` or :meth:`~object.__delitem__` method, +respectively, instead of :meth:`~object.__getitem__`. + +.. code-block:: + + >>> colors = ['red', 'blue', 'green', 'black'] + >>> colors[3] = 'white' # Setting item at index + >>> colors + ['red', 'blue', 'green', 'white'] + >>> del colors[3] # Deleting item at index 3 + >>> colors + ['red', 'blue', 'green'] + +All advanced forms of *subscript* documented in the following sections +are also usable for assignment and deletion. .. index:: @@ -1106,10 +1125,11 @@ Formal subscription grammar upper_bound: `expression` stride: `expression` +There is a semantic difference between the alternatives for *subscript*. If *subscript* contains ony one unstarred *slice* without a trailing comma, it will evaluate to the value of that *slice*. Otherwise, *subscript* will evaluate to a :class:`tuple` containing -the items of *subscript*. +the items of *tuple_slices*. .. index:: pair: object; callable From 0aa0256e507d7f95a2606a3510bd61dfa1f8884a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 26 Nov 2025 17:42:12 +0100 Subject: [PATCH 06/12] A correction and a simplification --- Doc/reference/datamodel.rst | 4 ++-- Doc/reference/expressions.rst | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index a58bd74b99d8dd..4ddf2c6b658d44 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -323,8 +323,8 @@ Sequences also support slicing: ``a[start:stop]`` selects all items with index * that *start* ``<=`` *k* ``<`` *stop*. When used as an expression, a slice is a sequence of the same type. The comment above about negative subscripts also applies to negative slice positions. -Note that no error is raised if a slice positions is larger than the length -of the sequence. +Note that no error is raised if a slice position is less than zero or larger +than the length of the sequence. If *start* is missing or ``None``, slicing behaves as if *start* was zero. If *stop* is missing or ``None``, slicing behaves as if *stop* was equal to diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 8346260f602276..76fe63f5545512 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1120,10 +1120,7 @@ Formal subscription grammar subscript: `slice` | `tuple_slices` tuple_slices: ','.(`slice` | `starred_expression`)+ [','] slice: `proper_slice` | `assignment_expression` - proper_slice: [`lower_bound`] ":" [`upper_bound`] [ ":" [`stride`] ] - lower_bound: `expression` - upper_bound: `expression` - stride: `expression` + proper_slice: [`expression`] ":" [`expression`] [ ":" [`expression`] ] There is a semantic difference between the alternatives for *subscript*. If *subscript* contains ony one unstarred *slice* without a trailing comma, From ec68594de7ca306c63549aa55ff98b521507af5a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 3 Dec 2025 16:27:37 +0100 Subject: [PATCH 07/12] Apply suggestions from code review Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> --- Doc/reference/datamodel.rst | 2 +- Doc/reference/expressions.rst | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 4ddf2c6b658d44..62f14008f18fcc 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -3250,7 +3250,7 @@ through the object's keys; for sequences, it should iterate through the values. - **sequences**, where *subscript* (also called *index*) should be an integer or a :class:`slice` object. - See :ref:`sequence documentation ` for the expected + See the :ref:`sequence documentation ` for the expected behavior, including handling :class:`slice` objects and negative indices. - **mappings**, where *subscript* is also called the *key*. See :ref:`mapping documentation ` for the expected diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 76fe63f5545512..b113c332c35133 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -1044,11 +1044,11 @@ colon:: >>> number_names[1:3] ['one', 'two'] >>> number_names[1:] - ['one', two', 'three', 'four', 'five'] + ['one', 'two', 'three', 'four', 'five'] >>> number_names[:3] ['zero', 'one', 'two'] >>> number_names[:] - ['zero', 'one', 'two'] + ['zero', 'one', 'two', 'three', 'four', 'five'] >>> number_names[::2] ['zero', 'two', 'four'] @@ -1101,13 +1101,13 @@ In this case, the interpreter unpacks the result into a tuple, and passes this tuple to :meth:`~object.__getitem__` or :meth:`~object.__class_getitem__`:: >>> demo[*range(10)] - subscripted with (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + subscripted with: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) Starred expressions may be combined with comma-separated expressions -and slices::: +and slices:: >>> demo['a', 'b', *range(3), 'c'] - subscripted with: ('a', 'b', 0, 1, 2, 'c') + subscripted with: ('a', 'b', 0, 1, 2, 'c') Formal subscription grammar @@ -1123,7 +1123,7 @@ Formal subscription grammar proper_slice: [`expression`] ":" [`expression`] [ ":" [`expression`] ] There is a semantic difference between the alternatives for *subscript*. -If *subscript* contains ony one unstarred *slice* without a trailing comma, +If *subscript* contains only one unstarred *slice* without a trailing comma, it will evaluate to the value of that *slice*. Otherwise, *subscript* will evaluate to a :class:`tuple` containing the items of *tuple_slices*. From 3495d74c282ec0b58fccd1e921c6994dc388732f Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 27 Nov 2025 11:15:42 +0100 Subject: [PATCH 08/12] Start on glossary entries --- Doc/glossary.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index a4066d42927f64..5e48299a2adb1d 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -799,6 +799,10 @@ Glossary And also please note that the free-threading CPython does not guarantee the thread-safety of iterator operations. + key + + A value that "names" an entry in a :term:`mapping`, + See also :term:`subscript`. key function A key function or collation function is a callable that returns a value @@ -1340,6 +1344,13 @@ Glossary See also :term:`borrowed reference`. + subscript + + The expression in square brackets of a :ref:`subscription ` + expression, usually used to select an element of a container. + Also called a :term:`key` (when subscipting a :term:`mapping`) or + an :term:`index` (when subscipting a :term:`sequence`). + t-string t-strings String literals prefixed with ``t`` or ``T`` are commonly called From 9e8aafb553aa4444d594ce2044667b4cb2a0f105 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 3 Dec 2025 16:44:56 +0100 Subject: [PATCH 09/12] Merge subscriptions and slicings in simple_stmts.rst --- Doc/reference/simple_stmts.rst | 42 +++++++++++++++------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index 9c022570e7e847..5f9c76f4fd3e66 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -91,11 +91,10 @@ attributes or items of mutable objects: : | "[" [`target_list`] "]" : | `attributeref` : | `subscription` - : | `slicing` : | "*" `target` -(See section :ref:`primaries` for the syntax definitions for *attributeref*, -*subscription*, and *slicing*.) +(See section :ref:`primaries` for the syntax definitions for *attributeref* +and *subscription*.) An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and @@ -107,8 +106,8 @@ right. pair: target; list Assignment is defined recursively depending on the form of the target (list). -When a target is part of a mutable object (an attribute reference, subscription -or slicing), the mutable object must ultimately perform the assignment and +When a target is part of a mutable object (an attribute reference or +subscription), the mutable object must ultimately perform the assignment and decide about its validity, and may raise an exception if the assignment is unacceptable. The rules observed by various types and the exceptions raised are given with the definition of the object types (see section :ref:`types`). @@ -189,9 +188,14 @@ Assignment of an object to a single target is recursively defined as follows. pair: object; mutable * If the target is a subscription: The primary expression in the reference is - evaluated. It should yield either a mutable sequence object (such as a list) - or a mapping object (such as a dictionary). Next, the subscript expression is evaluated. + Next, the subscript expression is evaluated. + Then, the primary's :meth:`~object.__setitem__` method is called with + two arguments: the subscript and the assigned object. + + Typically, :meth:`~object.__setitem__` is defined on mutable sequence objects + (such as lists) and mapping objects (such as dictionaries), and behaves as + follows. .. index:: pair: object; sequence @@ -214,16 +218,14 @@ Assignment of an object to a single target is recursively defined as follows. object. This can either replace an existing key/value pair with the same key value, or insert a new key/value pair (if no key with the same value existed). - For user-defined objects, the :meth:`~object.__setitem__` method is called with - appropriate arguments. - .. index:: pair: slicing; assignment -* If the target is a slicing: The primary expression in the reference is - evaluated. It should yield a mutable sequence object (such as a list). The - assigned object should be a sequence object of the same type. Next, the lower - and upper bound expressions are evaluated, insofar they are present; defaults - are zero and the sequence's length. The bounds should evaluate to integers. + If the target is a slicing: The primary expression should evaluate to + a mutable sequence object (such as a list). + The assigned object should be :term:`iterable`. + The slicing's lower and upper bounds should be integers; if they are ``None`` + (or not present), the defaults are zero and the sequence's length. + The bounds should evaluate to integers. If either bound is negative, the sequence's length is added to it. The resulting bounds are clipped to lie between zero and the sequence's length, inclusive. Finally, the sequence object is asked to replace the slice with @@ -231,12 +233,6 @@ Assignment of an object to a single target is recursively defined as follows. from the length of the assigned sequence, thus changing the length of the target sequence, if the target sequence allows it. -.. impl-detail:: - - In the current implementation, the syntax for targets is taken to be the same - as for expressions, and invalid syntax is rejected during the code generation - phase, causing less detailed error messages. - Although the definition of assignment implies that overlaps between the left-hand side and the right-hand side are 'simultaneous' (for example ``a, b = b, a`` swaps two variables), overlaps *within* the collection of assigned-to @@ -281,7 +277,7 @@ operation and an assignment statement: .. productionlist:: python-grammar augmented_assignment_stmt: `augtarget` `augop` (`expression_list` | `yield_expression`) - augtarget: `identifier` | `attributeref` | `subscription` | `slicing` + augtarget: `identifier` | `attributeref` | `subscription` augop: "+=" | "-=" | "*=" | "@=" | "/=" | "//=" | "%=" | "**=" : | ">>=" | "<<=" | "&=" | "^=" | "|=" @@ -470,7 +466,7 @@ in the same code block. Trying to delete an unbound name raises a .. index:: pair: attribute; deletion -Deletion of attribute references, subscriptions and slicings is passed to the +Deletion of attribute references and subscriptions is passed to the primary object involved; deletion of a slicing is in general equivalent to assignment of an empty slice of the right type (but even this is determined by the sliced object). From bb3f44187de9348f62137ce440d2b561c538df00 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 3 Dec 2025 16:56:52 +0100 Subject: [PATCH 10/12] Continue on glossary --- Doc/glossary.rst | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 5e48299a2adb1d..183a7b6e5d5af8 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -723,6 +723,14 @@ Glossary An object that both finds and loads a module; both a :term:`finder` and :term:`loader` object. + index + A numeric value that represents the position of an entry in + a :term:`sequence`. + In some contexts, Python allows negative indexes for counting from the + end of a sequence, and indexing using :term:`slices `. + + See also :term:`subscript`. + interactive Python has an interactive interpreter which means you can enter statements and expressions at the interpreter prompt, immediately @@ -800,8 +808,7 @@ Glossary the thread-safety of iterator operations. key - - A value that "names" an entry in a :term:`mapping`, + A value that identifies an entry in a :term:`mapping`. See also :term:`subscript`. key function @@ -1283,10 +1290,11 @@ Glossary chosen based on the type of a single argument. slice - An object usually containing a portion of a :term:`sequence`. A slice is - created using the subscript notation, ``[]`` with colons between numbers - when several are given, such as in ``variable_name[1:3:5]``. The bracket - (subscript) notation uses :class:`slice` objects internally. + An object of type :class:`slice`, used to describe a portion of + a :term:`sequence`. + A slice object is created when using the :ref:`slicing ` form + of :ref:`subscript notation `, with colons inside square + brackets, such as in ``variable_name[1:3:5]``. soft deprecated A soft deprecated API should not be used in new code, @@ -1345,11 +1353,11 @@ Glossary See also :term:`borrowed reference`. subscript - - The expression in square brackets of a :ref:`subscription ` - expression, usually used to select an element of a container. - Also called a :term:`key` (when subscipting a :term:`mapping`) or - an :term:`index` (when subscipting a :term:`sequence`). + The expression in square brackets of a + :ref:`subscription expression `, usually used to select + an element of a container. + Also called a :term:`key` when subscipting a :term:`mapping`, + or :term:`index` when subscipting a :term:`sequence`. t-string t-strings From dadc1f301f6463ab723185f3b870d61769aa85f7 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 3 Dec 2025 17:01:57 +0100 Subject: [PATCH 11/12] Plural section names, priority table clarification --- Doc/reference/expressions.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index b113c332c35133..e1489633e38501 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -932,8 +932,8 @@ method, that method is called as a fallback. .. _subscriptions: -Subscriptions and slicing -------------------------- +Subscriptions and slicings +-------------------------- .. index:: single: subscription @@ -1030,8 +1030,8 @@ are also usable for assignment and deletion. .. _slicings: -Slicing -^^^^^^^ +Slicings +^^^^^^^^ A more advanced form of subscription, :dfn:`slicing`, is commonly used to extract a portion of a :ref:`sequence `. @@ -1066,8 +1066,8 @@ or :meth:`~object.__class_getitem__` method, as above. :: subscripted with: slice(None, None, 'spam') -Tuple subscription -^^^^^^^^^^^^^^^^^^ +Comma-separated subscripts +^^^^^^^^^^^^^^^^^^^^^^^^^^ The subscript can also be given as two or more comma-separated expressions or slices:: @@ -1090,8 +1090,8 @@ by a comma, to specify a one-element tuple:: subscripted with ('spam',) -"Starred" subscription -^^^^^^^^^^^^^^^^^^^^^^ +"Starred" subscriptions +^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 3.11 Expressions in *tuple_slices* may be starred. See :pep:`646`. @@ -2144,7 +2144,7 @@ precedence and have a left-to-right chaining feature as described in the | ``{key: value...}``, | dictionary display, | | ``{expressions...}`` | set display | +-----------------------------------------------+-------------------------------------+ -| ``x[index]``, ``x[index:index]``, | Subscription, slicing, | +| ``x[index]``, ``x[index:index]`` | Subscription (including slicing), | | ``x(arguments...)``, ``x.attribute`` | call, attribute reference | +-----------------------------------------------+-------------------------------------+ | :keyword:`await x ` | Await expression | From 64920072664f586f2b19dc4bfe5964503de7fcfa Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 3 Dec 2025 17:14:30 +0100 Subject: [PATCH 12/12] Improve glossary entries --- Doc/glossary.rst | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 183a7b6e5d5af8..86c72d97e1f9f1 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -724,8 +724,13 @@ Glossary :term:`finder` and :term:`loader` object. index - A numeric value that represents the position of an entry in + A numeric value that represents the position of an element in a :term:`sequence`. + + In Python, indexing starts at zero. + For example, ``things[0]`` names the *first* element of ``things``; + ``things[1]`` names the second one. + In some contexts, Python allows negative indexes for counting from the end of a sequence, and indexing using :term:`slices `. @@ -1354,10 +1359,11 @@ Glossary subscript The expression in square brackets of a - :ref:`subscription expression `, usually used to select - an element of a container. - Also called a :term:`key` when subscipting a :term:`mapping`, - or :term:`index` when subscipting a :term:`sequence`. + :ref:`subscription expression `, for example, + the ``3`` in ``items[3]``. + Usually used to select an element of a container. + Also called a :term:`key` when subscripting a :term:`mapping`, + or :term:`index` when subscripting a :term:`sequence`. t-string t-strings