Skip to content

Commit ae25855

Browse files
authored
gh-103492: Clarify SyntaxWarning with literal comparison (#103493)
1 parent 79ae019 commit ae25855

File tree

4 files changed

+34
-16
lines changed

4 files changed

+34
-16
lines changed

Lib/test/test_codeop.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def test_filename(self):
277277
def test_warning(self):
278278
# Test that the warning is only returned once.
279279
with warnings_helper.check_warnings(
280-
('"is" with a literal', SyntaxWarning),
280+
('"is" with \'str\' literal', SyntaxWarning),
281281
("invalid escape sequence", SyntaxWarning),
282282
) as w:
283283
compile_command(r"'\e' is 0")

Lib/test/test_grammar.py

+20-11
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,9 @@ def check(test, error=False):
236236
check(f"[{num}for x in ()]")
237237
check(f"{num}spam", error=True)
238238

239+
with self.assertWarnsRegex(SyntaxWarning, r'invalid \w+ literal'):
240+
compile(f"{num}is x", "<testcase>", "eval")
239241
with warnings.catch_warnings():
240-
warnings.filterwarnings('ignore', '"is" with a literal',
241-
SyntaxWarning)
242-
with self.assertWarnsRegex(SyntaxWarning,
243-
r'invalid \w+ literal'):
244-
compile(f"{num}is x", "<testcase>", "eval")
245242
warnings.simplefilter('error', SyntaxWarning)
246243
with self.assertRaisesRegex(SyntaxError,
247244
r'invalid \w+ literal'):
@@ -1467,21 +1464,33 @@ def test_comparison(self):
14671464
if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in x is x is not x: pass
14681465

14691466
def test_comparison_is_literal(self):
1470-
def check(test, msg='"is" with a literal'):
1467+
def check(test, msg):
14711468
self.check_syntax_warning(test, msg)
14721469

1473-
check('x is 1')
1474-
check('x is "thing"')
1475-
check('1 is x')
1476-
check('x is y is 1')
1477-
check('x is not 1', '"is not" with a literal')
1470+
check('x is 1', '"is" with \'int\' literal')
1471+
check('x is "thing"', '"is" with \'str\' literal')
1472+
check('1 is x', '"is" with \'int\' literal')
1473+
check('x is y is 1', '"is" with \'int\' literal')
1474+
check('x is not 1', '"is not" with \'int\' literal')
1475+
check('x is not (1, 2)', '"is not" with \'tuple\' literal')
1476+
check('(1, 2) is not x', '"is not" with \'tuple\' literal')
1477+
1478+
check('None is 1', '"is" with \'int\' literal')
1479+
check('1 is None', '"is" with \'int\' literal')
1480+
1481+
check('x == 3 is y', '"is" with \'int\' literal')
1482+
check('x == "thing" is y', '"is" with \'str\' literal')
14781483

14791484
with warnings.catch_warnings():
14801485
warnings.simplefilter('error', SyntaxWarning)
14811486
compile('x is None', '<testcase>', 'exec')
14821487
compile('x is False', '<testcase>', 'exec')
14831488
compile('x is True', '<testcase>', 'exec')
14841489
compile('x is ...', '<testcase>', 'exec')
1490+
compile('None is x', '<testcase>', 'exec')
1491+
compile('False is x', '<testcase>', 'exec')
1492+
compile('True is x', '<testcase>', 'exec')
1493+
compile('... is x', '<testcase>', 'exec')
14851494

14861495
def test_warn_missed_comma(self):
14871496
def check(test):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Clarify :exc:`SyntaxWarning` with literal ``is`` comparison by specifying which literal is problematic, since comparisons using ``is`` with e.g. None and bool literals are idiomatic.

Python/compile.c

+12-4
Original file line numberDiff line numberDiff line change
@@ -2269,6 +2269,8 @@ check_is_arg(expr_ty e)
22692269
|| value == Py_Ellipsis);
22702270
}
22712271

2272+
static PyTypeObject * infer_type(expr_ty e);
2273+
22722274
/* Check operands of identity checks ("is" and "is not").
22732275
Emit a warning if any operand is a constant except named singletons.
22742276
*/
@@ -2277,19 +2279,25 @@ check_compare(struct compiler *c, expr_ty e)
22772279
{
22782280
Py_ssize_t i, n;
22792281
bool left = check_is_arg(e->v.Compare.left);
2282+
expr_ty left_expr = e->v.Compare.left;
22802283
n = asdl_seq_LEN(e->v.Compare.ops);
22812284
for (i = 0; i < n; i++) {
22822285
cmpop_ty op = (cmpop_ty)asdl_seq_GET(e->v.Compare.ops, i);
2283-
bool right = check_is_arg((expr_ty)asdl_seq_GET(e->v.Compare.comparators, i));
2286+
expr_ty right_expr = (expr_ty)asdl_seq_GET(e->v.Compare.comparators, i);
2287+
bool right = check_is_arg(right_expr);
22842288
if (op == Is || op == IsNot) {
22852289
if (!right || !left) {
22862290
const char *msg = (op == Is)
2287-
? "\"is\" with a literal. Did you mean \"==\"?"
2288-
: "\"is not\" with a literal. Did you mean \"!=\"?";
2289-
return compiler_warn(c, LOC(e), msg);
2291+
? "\"is\" with '%.200s' literal. Did you mean \"==\"?"
2292+
: "\"is not\" with '%.200s' literal. Did you mean \"!=\"?";
2293+
expr_ty literal = !left ? left_expr : right_expr;
2294+
return compiler_warn(
2295+
c, LOC(e), msg, infer_type(literal)->tp_name
2296+
);
22902297
}
22912298
}
22922299
left = right;
2300+
left_expr = right_expr;
22932301
}
22942302
return SUCCESS;
22952303
}

0 commit comments

Comments
 (0)