summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShantanu <12621235+hauntsaninja@users.noreply.github.com>2023-04-24 21:42:57 (GMT)
committerGitHub <noreply@github.com>2023-04-24 21:42:57 (GMT)
commitae25855045e8f19f4715c9b2c02cbcd81e7f6f95 (patch)
treef3530704fce4ac964f45d4ad2623ad3ea62338a7
parent79ae019164eeb6b94118bc17bc1e937405684c75 (diff)
downloadcpython-ae25855045e8f19f4715c9b2c02cbcd81e7f6f95.zip
cpython-ae25855045e8f19f4715c9b2c02cbcd81e7f6f95.tar.gz
cpython-ae25855045e8f19f4715c9b2c02cbcd81e7f6f95.tar.bz2
gh-103492: Clarify SyntaxWarning with literal comparison (#103493)
-rw-r--r--Lib/test/test_codeop.py2
-rw-r--r--Lib/test/test_grammar.py31
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-04-13-00-58-55.gh-issue-103492.P4k0Ay.rst1
-rw-r--r--Python/compile.c16
4 files changed, 34 insertions, 16 deletions
diff --git a/Lib/test/test_codeop.py b/Lib/test/test_codeop.py
index 6966c2f..e3c3822 100644
--- a/Lib/test/test_codeop.py
+++ b/Lib/test/test_codeop.py
@@ -277,7 +277,7 @@ class CodeopTests(unittest.TestCase):
def test_warning(self):
# Test that the warning is only returned once.
with warnings_helper.check_warnings(
- ('"is" with a literal', SyntaxWarning),
+ ('"is" with \'str\' literal', SyntaxWarning),
("invalid escape sequence", SyntaxWarning),
) as w:
compile_command(r"'\e' is 0")
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index ced9000..ee105a3 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -236,12 +236,9 @@ class TokenTests(unittest.TestCase):
check(f"[{num}for x in ()]")
check(f"{num}spam", error=True)
+ with self.assertWarnsRegex(SyntaxWarning, r'invalid \w+ literal'):
+ compile(f"{num}is x", "<testcase>", "eval")
with warnings.catch_warnings():
- warnings.filterwarnings('ignore', '"is" with a literal',
- SyntaxWarning)
- with self.assertWarnsRegex(SyntaxWarning,
- r'invalid \w+ literal'):
- compile(f"{num}is x", "<testcase>", "eval")
warnings.simplefilter('error', SyntaxWarning)
with self.assertRaisesRegex(SyntaxError,
r'invalid \w+ literal'):
@@ -1467,14 +1464,22 @@ class GrammarTests(unittest.TestCase):
if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in x is x is not x: pass
def test_comparison_is_literal(self):
- def check(test, msg='"is" with a literal'):
+ def check(test, msg):
self.check_syntax_warning(test, msg)
- check('x is 1')
- check('x is "thing"')
- check('1 is x')
- check('x is y is 1')
- check('x is not 1', '"is not" with a literal')
+ check('x is 1', '"is" with \'int\' literal')
+ check('x is "thing"', '"is" with \'str\' literal')
+ check('1 is x', '"is" with \'int\' literal')
+ check('x is y is 1', '"is" with \'int\' literal')
+ check('x is not 1', '"is not" with \'int\' literal')
+ check('x is not (1, 2)', '"is not" with \'tuple\' literal')
+ check('(1, 2) is not x', '"is not" with \'tuple\' literal')
+
+ check('None is 1', '"is" with \'int\' literal')
+ check('1 is None', '"is" with \'int\' literal')
+
+ check('x == 3 is y', '"is" with \'int\' literal')
+ check('x == "thing" is y', '"is" with \'str\' literal')
with warnings.catch_warnings():
warnings.simplefilter('error', SyntaxWarning)
@@ -1482,6 +1487,10 @@ class GrammarTests(unittest.TestCase):
compile('x is False', '<testcase>', 'exec')
compile('x is True', '<testcase>', 'exec')
compile('x is ...', '<testcase>', 'exec')
+ compile('None is x', '<testcase>', 'exec')
+ compile('False is x', '<testcase>', 'exec')
+ compile('True is x', '<testcase>', 'exec')
+ compile('... is x', '<testcase>', 'exec')
def test_warn_missed_comma(self):
def check(test):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-04-13-00-58-55.gh-issue-103492.P4k0Ay.rst b/Misc/NEWS.d/next/Core and Builtins/2023-04-13-00-58-55.gh-issue-103492.P4k0Ay.rst
new file mode 100644
index 0000000..92965096
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-04-13-00-58-55.gh-issue-103492.P4k0Ay.rst
@@ -0,0 +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.
diff --git a/Python/compile.c b/Python/compile.c
index 9603269..99ef7c3 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -2269,6 +2269,8 @@ check_is_arg(expr_ty e)
|| value == Py_Ellipsis);
}
+static PyTypeObject * infer_type(expr_ty e);
+
/* Check operands of identity checks ("is" and "is not").
Emit a warning if any operand is a constant except named singletons.
*/
@@ -2277,19 +2279,25 @@ check_compare(struct compiler *c, expr_ty e)
{
Py_ssize_t i, n;
bool left = check_is_arg(e->v.Compare.left);
+ expr_ty left_expr = e->v.Compare.left;
n = asdl_seq_LEN(e->v.Compare.ops);
for (i = 0; i < n; i++) {
cmpop_ty op = (cmpop_ty)asdl_seq_GET(e->v.Compare.ops, i);
- bool right = check_is_arg((expr_ty)asdl_seq_GET(e->v.Compare.comparators, i));
+ expr_ty right_expr = (expr_ty)asdl_seq_GET(e->v.Compare.comparators, i);
+ bool right = check_is_arg(right_expr);
if (op == Is || op == IsNot) {
if (!right || !left) {
const char *msg = (op == Is)
- ? "\"is\" with a literal. Did you mean \"==\"?"
- : "\"is not\" with a literal. Did you mean \"!=\"?";
- return compiler_warn(c, LOC(e), msg);
+ ? "\"is\" with '%.200s' literal. Did you mean \"==\"?"
+ : "\"is not\" with '%.200s' literal. Did you mean \"!=\"?";
+ expr_ty literal = !left ? left_expr : right_expr;
+ return compiler_warn(
+ c, LOC(e), msg, infer_type(literal)->tp_name
+ );
}
}
left = right;
+ left_expr = right_expr;
}
return SUCCESS;
}