Skip to content
Closed
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,13 @@ symtable
(Contributed by Serhiy Storchaka in :gh:`153844`.)


tokenize
--------

* Invalid token ``<>`` excluded from :mod:`tokenize` output.
(Contributed by Sergey B Kirpichev in :gh:`151464`.)


tkinter
-------

Expand Down
8 changes: 8 additions & 0 deletions Grammar/python.gram
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,7 @@ compare_op_bitwise_or_pair[CmpopExprPair*]:
| (tok='!=' { _PyPegen_check_barry_as_flufl(p, tok) ? NULL : tok }) a=bitwise_or {
_PyPegen_cmpop_expr_pair(p, NotEq, a) }
| '<=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, LtE, a) }
| invalid_noteq
| '<' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Lt, a) }
| '>=' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, GtE, a) }
| '>' a=bitwise_or { _PyPegen_cmpop_expr_pair(p, Gt, a) }
Expand Down Expand Up @@ -1611,3 +1612,10 @@ invalid_bitwise_or:
? RAISE_SYNTAX_ERROR_KNOWN_RANGE(b, c, "invalid syntax. Maybe you meant 'or' or '|' instead of '||'?")
: NULL
}

invalid_noteq:
| a='<' b='>' {
_PyPegen_tokens_are_adjacent(a, b)
? RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Maybe you meant '!=' instead of '<>'?")
: NULL
}
2 changes: 1 addition & 1 deletion Include/internal/pycore_token.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -3627,6 +3627,31 @@ def test_ifexp_body_stmt_else_stmt(self):
]:
self._check_error(f"x = {lhs_stmt} if 1 else {rhs_stmt}", msg)

def test_diamond_operator(self):
self._check_error(
"1<>2",
r"Maybe you meant '!=' instead of '<>'\?",
lineno=1,
end_lineno=1,
offset=2,
end_offset=4,
)

def test_diamond_operator_barry_as_flufl(self):
# Under barry_as_FLUFL, '<>' is the valid "not equal" operator
compile(
"from __future__ import barry_as_FLUFL\n1<>2",
"<test>", "exec",
)
self._check_error(
"from __future__ import barry_as_FLUFL\na != b",
"with Barry as BDFL, use '<>' instead of '!='",
lineno=2,
end_lineno=2,
offset=3,
end_offset=5,
)

def test_double_ampersand(self):
self._check_error(
"a && b",
Expand Down
14 changes: 14 additions & 0 deletions Lib/test/test_tokenize.py
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,20 @@ def test_multiline_non_ascii_fstring_with_expr(self):
FSTRING_END \'"\' (2, 2) (2, 3)
""")

def test_ineq_tokens(self):
self.check_tokenize("1 != 2", """\
NUMBER '1' (1, 0) (1, 1)
OP '!=' (1, 2) (1, 4)
NUMBER '2' (1, 5) (1, 6)
""")
self.check_tokenize("1 <> 2", """\
NUMBER '1' (1, 0) (1, 1)
OP '<' (1, 2) (1, 3)
OP '>' (1, 3) (1, 4)
NUMBER '2' (1, 5) (1, 6)
""")


class GenerateTokensTest(TokenizeTest):
def check_tokenize(self, s, expected):
# Format the tokens in s in a table format.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Exclude invalid token ``<>`` from :mod:`tokenize` output. :exc:`SyntaxError`
for ``<>`` now suggests ``!=``.
2 changes: 2 additions & 0 deletions Parser/action_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "pegen.h"
#include "string_parser.h" // _PyPegen_decode_string()
#include "lexer/state.h" // tok_state


void *
Expand Down Expand Up @@ -2133,6 +2134,7 @@ _PyPegen_checked_from_import(Parser *p, asdl_seq *dots, expr_ty module_name,
alias_ty alias = asdl_seq_GET(names, i);
if (PyUnicode_CompareWithASCIIString(alias->name, "barry_as_FLUFL") == 0) {
p->flags |= PyPARSE_BARRY_AS_BDFL;
p->tok->barry_as_bdfl = 1;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Parser/lexer/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ _PyLexer_get_normal(struct tok_state *tok, ftstring_state *current, struct token
/* Check for two-character token */
{
int c2 = tok_nextc(tok);
int current_token = _PyToken_TwoChars(c, c2);
int current_token = _PyToken_TwoChars(c, c2, tok->barry_as_bdfl);
if (current_token != OP) {
int c3 = tok_nextc(tok);
int current_token3 = _PyToken_ThreeChars(c, c2, c3);
Expand Down
1 change: 1 addition & 0 deletions Parser/lexer/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ _PyTokenizer_tok_new(void)
#ifdef Py_DEBUG
tok->debug = _Py_GetConfig()->parser_debug;
#endif
tok->barry_as_bdfl = 0;
return tok;
}

Expand Down
1 change: 1 addition & 0 deletions Parser/lexer/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ struct tok_state {
#ifdef Py_DEBUG
int debug;
#endif
int barry_as_bdfl;
};

static inline ftstring_state *
Expand Down
Loading
Loading