mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	gh-103492: Clarify SyntaxWarning with literal comparison (#103493)
This commit is contained in:
		
							parent
							
								
									79ae019164
								
							
						
					
					
						commit
						ae25855045
					
				
					 4 changed files with 34 additions and 16 deletions
				
			
		| 
						 | 
					@ -277,7 +277,7 @@ def test_filename(self):
 | 
				
			||||||
    def test_warning(self):
 | 
					    def test_warning(self):
 | 
				
			||||||
        # Test that the warning is only returned once.
 | 
					        # Test that the warning is only returned once.
 | 
				
			||||||
        with warnings_helper.check_warnings(
 | 
					        with warnings_helper.check_warnings(
 | 
				
			||||||
                ('"is" with a literal', SyntaxWarning),
 | 
					                ('"is" with \'str\' literal', SyntaxWarning),
 | 
				
			||||||
                ("invalid escape sequence", SyntaxWarning),
 | 
					                ("invalid escape sequence", SyntaxWarning),
 | 
				
			||||||
                ) as w:
 | 
					                ) as w:
 | 
				
			||||||
            compile_command(r"'\e' is 0")
 | 
					            compile_command(r"'\e' is 0")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -236,12 +236,9 @@ def check(test, error=False):
 | 
				
			||||||
            check(f"[{num}for x in ()]")
 | 
					            check(f"[{num}for x in ()]")
 | 
				
			||||||
            check(f"{num}spam", error=True)
 | 
					            check(f"{num}spam", error=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            with warnings.catch_warnings():
 | 
					            with self.assertWarnsRegex(SyntaxWarning, r'invalid \w+ literal'):
 | 
				
			||||||
                warnings.filterwarnings('ignore', '"is" with a literal',
 | 
					 | 
				
			||||||
                                        SyntaxWarning)
 | 
					 | 
				
			||||||
                with self.assertWarnsRegex(SyntaxWarning,
 | 
					 | 
				
			||||||
                            r'invalid \w+ literal'):
 | 
					 | 
				
			||||||
                compile(f"{num}is x", "<testcase>", "eval")
 | 
					                compile(f"{num}is x", "<testcase>", "eval")
 | 
				
			||||||
 | 
					            with warnings.catch_warnings():
 | 
				
			||||||
                warnings.simplefilter('error', SyntaxWarning)
 | 
					                warnings.simplefilter('error', SyntaxWarning)
 | 
				
			||||||
                with self.assertRaisesRegex(SyntaxError,
 | 
					                with self.assertRaisesRegex(SyntaxError,
 | 
				
			||||||
                            r'invalid \w+ literal'):
 | 
					                            r'invalid \w+ literal'):
 | 
				
			||||||
| 
						 | 
					@ -1467,14 +1464,22 @@ def test_comparison(self):
 | 
				
			||||||
        if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in x is x is not x: pass
 | 
					        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 test_comparison_is_literal(self):
 | 
				
			||||||
        def check(test, msg='"is" with a literal'):
 | 
					        def check(test, msg):
 | 
				
			||||||
            self.check_syntax_warning(test, msg)
 | 
					            self.check_syntax_warning(test, msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        check('x is 1')
 | 
					        check('x is 1', '"is" with \'int\' literal')
 | 
				
			||||||
        check('x is "thing"')
 | 
					        check('x is "thing"', '"is" with \'str\' literal')
 | 
				
			||||||
        check('1 is x')
 | 
					        check('1 is x', '"is" with \'int\' literal')
 | 
				
			||||||
        check('x is y is 1')
 | 
					        check('x is y is 1', '"is" with \'int\' literal')
 | 
				
			||||||
        check('x is not 1', '"is not" with a 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():
 | 
					        with warnings.catch_warnings():
 | 
				
			||||||
            warnings.simplefilter('error', SyntaxWarning)
 | 
					            warnings.simplefilter('error', SyntaxWarning)
 | 
				
			||||||
| 
						 | 
					@ -1482,6 +1487,10 @@ def check(test, msg='"is" with a literal'):
 | 
				
			||||||
            compile('x is False', '<testcase>', 'exec')
 | 
					            compile('x is False', '<testcase>', 'exec')
 | 
				
			||||||
            compile('x is True', '<testcase>', 'exec')
 | 
					            compile('x is True', '<testcase>', 'exec')
 | 
				
			||||||
            compile('x is ...', '<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 test_warn_missed_comma(self):
 | 
				
			||||||
        def check(test):
 | 
					        def check(test):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.
 | 
				
			||||||
| 
						 | 
					@ -2269,6 +2269,8 @@ check_is_arg(expr_ty e)
 | 
				
			||||||
         || value == Py_Ellipsis);
 | 
					         || value == Py_Ellipsis);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyTypeObject * infer_type(expr_ty e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Check operands of identity checks ("is" and "is not").
 | 
					/* Check operands of identity checks ("is" and "is not").
 | 
				
			||||||
   Emit a warning if any operand is a constant except named singletons.
 | 
					   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;
 | 
					    Py_ssize_t i, n;
 | 
				
			||||||
    bool left = check_is_arg(e->v.Compare.left);
 | 
					    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);
 | 
					    n = asdl_seq_LEN(e->v.Compare.ops);
 | 
				
			||||||
    for (i = 0; i < n; i++) {
 | 
					    for (i = 0; i < n; i++) {
 | 
				
			||||||
        cmpop_ty op = (cmpop_ty)asdl_seq_GET(e->v.Compare.ops, 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 (op == Is || op == IsNot) {
 | 
				
			||||||
            if (!right || !left) {
 | 
					            if (!right || !left) {
 | 
				
			||||||
                const char *msg = (op == Is)
 | 
					                const char *msg = (op == Is)
 | 
				
			||||||
                        ? "\"is\" with a literal. Did you mean \"==\"?"
 | 
					                        ? "\"is\" with '%.200s' literal. Did you mean \"==\"?"
 | 
				
			||||||
                        : "\"is not\" with a literal. Did you mean \"!=\"?";
 | 
					                        : "\"is not\" with '%.200s' literal. Did you mean \"!=\"?";
 | 
				
			||||||
                return compiler_warn(c, LOC(e), msg);
 | 
					                expr_ty literal = !left ? left_expr : right_expr;
 | 
				
			||||||
 | 
					                return compiler_warn(
 | 
				
			||||||
 | 
					                    c, LOC(e), msg, infer_type(literal)->tp_name
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        left = right;
 | 
					        left = right;
 | 
				
			||||||
 | 
					        left_expr = right_expr;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return SUCCESS;
 | 
					    return SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue