Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ def _check_error(self, code, errtext,
self.fail("SyntaxError is not a %s" % subclass.__name__)
mo = re.search(errtext, str(err))
if mo is None:
self.fail("SyntaxError did not contain '%r'" % (errtext,))
self.fail("SyntaxError did not contain %r" % (errtext,))
self.assertEqual(err.filename, filename)
if lineno is not None:
self.assertEqual(err.lineno, lineno)
Expand All @@ -621,6 +621,21 @@ def _check_error(self, code, errtext,
else:
self.fail("compile() did not raise SyntaxError")

def _check_warning(self, code, warntext,
filename="<testcase>", mode="exec"):
"""Check that compiling code emits SyntaxWarning with warntext.

errtest is a regular expression that must be present in the
test of the exception raised. If subclass is specified it
is the expected subclass of SyntaxError (e.g. IndentationError).
"""
with self.assertWarns(SyntaxWarning) as cm:
compile(code, filename, mode)
warn = cm.warning
mo = re.search(warntext, str(warn))
if mo is None:
self.fail("SyntaxWarning did not contain %r" % (warntext,))

def test_assign_call(self):
self._check_error("f() = 1", "assign")

Expand Down Expand Up @@ -677,6 +692,13 @@ def test_kwargs_last3(self):
"iterable argument unpacking follows "
"keyword argument unpacking")

def test_chained_in(self):
self._check_warning("a in b == c",
"chained `in' is ambiguous")
self._check_warning("a not in b == c",
"chained `not in' is ambiguous")


def test_main():
support.run_unittest(SyntaxTestCase)
from test import test_syntax
Expand Down
17 changes: 17 additions & 0 deletions Python/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,23 @@ ast_for_expr(struct compiling *c, const node *n)
if (!newoperator) {
return NULL;
}
if ((newoperator == In || newoperator == NotIn) &&
NCH(n) > 3)
{
PyObject *msg = PyUnicode_FromString(newoperator == In
? "chained `in' is ambiguous"
: "chained `not in' is ambiguous");
if (msg == NULL)
return NULL;
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning,
msg, c->c_filename, CHILD(n, i)->n_lineno,
NULL, NULL) == -1)
{
Py_DECREF(msg);
return NULL;
}
Py_DECREF(msg);
}

expression = ast_for_expr(c, CHILD(n, i + 1));
if (!expression) {
Expand Down