Skip to content

Commit 6d410aa

Browse files
skirpichevpablogsal
authored andcommitted
[3.13] gh-151464: exclude '<>' token from tokenize output (GH-154854)
* gh-151464: exclude '<>' token from tokenize output Was: ``` $ echo '1 <> 2' | python -m tokenize 1,0-1,1: NUMBER '1' 1,2-1,4: OP '<>' 1,5-1,6: NUMBER '2' 1,6-1,7: NEWLINE '\n' 2,0-2,0: ENDMARKER '' ``` Now (regardless on ``__future__.barry_as_FLUFL`` import): ``` $ echo '1 <> 2' | ./python -m tokenize 1,0-1,1: NUMBER '1' 1,2-1,3: OP '<' 1,3-1,4: OP '>' 1,5-1,6: NUMBER '2' 1,6-1,7: NEWLINE '\n' 2,0-2,0: ENDMARKER '' ``` in accordance with the Grammar: https://docs.python.org/3.14/reference/lexical_analysis.html#operators-and-delimiters Also adds a custom error message for ``<>`` ("not equal" in Pascal and Python 2). * +1 * address review: lowercase and move invalid rule * address review: news * address review: move test_guido_as_bdfl_ineq_tokens() * address review: revert _PyTokenizer_From* changes * + revert unrelated change * address review: remove whatsnew entry (cherry picked from commit 198bc76)
1 parent af1be50 commit 6d410aa

13 files changed

Lines changed: 419 additions & 290 deletions

File tree

‎Grammar/python.gram‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,7 @@ compare_op_bitwise_or_pair[CmpopExprPair*]:
748748
| eq_bitwise_or
749749
| noteq_bitwise_or
750750
| lte_bitwise_or
751+
| invalid_noteq
751752
| lt_bitwise_or
752753
| gte_bitwise_or
753754
| gt_bitwise_or
@@ -1439,3 +1440,10 @@ invalid_type_params:
14391440
RAISE_SYNTAX_ERROR_STARTING_FROM(
14401441
token,
14411442
"Type parameter list cannot be empty")}
1443+
1444+
invalid_noteq:
1445+
| a='<' b='>' {
1446+
_PyPegen_tokens_are_adjacent(a, b)
1447+
? RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Maybe you meant '!=' instead of '<>'?")
1448+
: NULL
1449+
}

‎Include/internal/pycore_token.h‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Lib/test/test_syntax.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2728,6 +2728,30 @@ def test_deep_invalid_rule(self):
27282728
with self.assertRaises(SyntaxError):
27292729
compile(source, "<string>", "exec")
27302730

2731+
def test_diamond_operator(self):
2732+
self._check_error(
2733+
"1<>2",
2734+
r"Maybe you meant '!=' instead of '<>'\?",
2735+
lineno=1,
2736+
end_lineno=1,
2737+
offset=2,
2738+
end_offset=4,
2739+
)
2740+
2741+
def test_diamond_operator_barry_as_flufl(self):
2742+
compile(
2743+
"from __future__ import barry_as_FLUFL\n1<>2",
2744+
"<test>", "exec",
2745+
)
2746+
self._check_error(
2747+
"from __future__ import barry_as_FLUFL\na != b",
2748+
"with Barry as BDFL, use '<>' instead of '!='",
2749+
lineno=2,
2750+
end_lineno=2,
2751+
offset=3,
2752+
end_offset=5,
2753+
)
2754+
27312755

27322756
def load_tests(loader, tests, pattern):
27332757
tests.addTest(doctest.DocTestSuite())

‎Lib/test/test_tokenize.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,6 +1229,20 @@ def test_multiline_non_ascii_fstring_with_expr(self):
12291229
FSTRING_END \'"\' (2, 2) (2, 3)
12301230
""")
12311231

1232+
def test_ineq_tokens(self):
1233+
self.check_tokenize("1 != 2", """\
1234+
NUMBER '1' (1, 0) (1, 1)
1235+
OP '!=' (1, 2) (1, 4)
1236+
NUMBER '2' (1, 5) (1, 6)
1237+
""")
1238+
self.check_tokenize("1 <> 2", """\
1239+
NUMBER '1' (1, 0) (1, 1)
1240+
OP '<' (1, 2) (1, 3)
1241+
OP '>' (1, 3) (1, 4)
1242+
NUMBER '2' (1, 5) (1, 6)
1243+
""")
1244+
1245+
12321246
class GenerateTokensTest(TokenizeTest):
12331247
def check_tokenize(self, s, expected):
12341248
# Format the tokens in s in a table format.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Exclude invalid token ``<>`` from :mod:`tokenize` output. :exc:`SyntaxError`
2+
for ``<>`` now suggests ``!=``.

‎Parser/action_helpers.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,6 +1726,7 @@ _PyPegen_checked_future_import(Parser *p, identifier module, asdl_alias_seq * na
17261726
alias_ty alias = asdl_seq_GET(names, i);
17271727
if (PyUnicode_CompareWithASCIIString(alias->name, "barry_as_FLUFL") == 0) {
17281728
p->flags |= PyPARSE_BARRY_AS_BDFL;
1729+
p->tok->barry_as_bdfl = 1;
17291730
}
17301731
}
17311732
}

‎Parser/lexer/lexer.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1143,7 +1143,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
11431143
/* Check for two-character token */
11441144
{
11451145
int c2 = tok_nextc(tok);
1146-
int current_token = _PyToken_TwoChars(c, c2);
1146+
int current_token = _PyToken_TwoChars(c, c2, tok->barry_as_bdfl);
11471147
if (current_token != OP) {
11481148
int c3 = tok_nextc(tok);
11491149
int current_token3 = _PyToken_ThreeChars(c, c2, c3);

‎Parser/lexer/state.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ struct tok_state {
131131
#ifdef Py_DEBUG
132132
int debug;
133133
#endif
134+
int barry_as_bdfl;
134135
};
135136

136137
int _PyLexer_type_comment_token_setup(struct tok_state *tok, struct token *token, int type, int col_offset,

0 commit comments

Comments
 (0)