improve several corner cases related with argument names in parenthesis
- Fix #7362: give a good error message for parenthesized arguments with defaults. - Add a py3k warning for any parenthesized arguments since those are not allowed in Py3. This warning is not given in tuple unpacking, since that incurs the tuple unpacking warning.
This commit is contained in:
parent
b678de8ba6
commit
99a5023c80
|
@ -30,6 +30,18 @@ class TestPy3KWarnings(unittest.TestCase):
|
||||||
exec "`2`" in {}
|
exec "`2`" in {}
|
||||||
self.assertWarning(None, w, expected)
|
self.assertWarning(None, w, expected)
|
||||||
|
|
||||||
|
def test_paren_arg_names(self):
|
||||||
|
expected = 'parenthesized argument names are invalid in 3.x'
|
||||||
|
def check(s):
|
||||||
|
exec s in {}
|
||||||
|
self.assertWarning(None, w, expected)
|
||||||
|
with check_warnings() as w:
|
||||||
|
check("def f((x)): pass")
|
||||||
|
check("def f((((x))), (y)): pass")
|
||||||
|
check("def f((x), (((y))), m=32): pass")
|
||||||
|
# Something like def f((a, (b))): pass will raise the tuple
|
||||||
|
# unpacking warning.
|
||||||
|
|
||||||
def test_forbidden_names(self):
|
def test_forbidden_names(self):
|
||||||
# So we don't screw up our globals
|
# So we don't screw up our globals
|
||||||
def safe_exec(expr):
|
def safe_exec(expr):
|
||||||
|
|
|
@ -493,10 +493,14 @@ class SyntaxTestCase(unittest.TestCase):
|
||||||
self.fail("SyntaxError is not a %s" % subclass.__name__)
|
self.fail("SyntaxError is not a %s" % subclass.__name__)
|
||||||
mo = re.search(errtext, str(err))
|
mo = re.search(errtext, str(err))
|
||||||
if mo is None:
|
if mo is None:
|
||||||
self.fail("SyntaxError did not contain '%r'" % (errtext,))
|
self.fail("%s did not contain '%r'" % (err, errtext,))
|
||||||
else:
|
else:
|
||||||
self.fail("compile() did not raise SyntaxError")
|
self.fail("compile() did not raise SyntaxError")
|
||||||
|
|
||||||
|
def test_paren_arg_with_default(self):
|
||||||
|
self._check_error("def f((x)=23): pass",
|
||||||
|
"parenthesized arg with default")
|
||||||
|
|
||||||
def test_assign_call(self):
|
def test_assign_call(self):
|
||||||
self._check_error("f() = 1", "assign")
|
self._check_error("f() = 1", "assign")
|
||||||
|
|
||||||
|
|
17
Python/ast.c
17
Python/ast.c
|
@ -692,7 +692,8 @@ ast_for_arguments(struct compiling *c, const node *n)
|
||||||
while (i < NCH(n)) {
|
while (i < NCH(n)) {
|
||||||
ch = CHILD(n, i);
|
ch = CHILD(n, i);
|
||||||
switch (TYPE(ch)) {
|
switch (TYPE(ch)) {
|
||||||
case fpdef:
|
case fpdef: {
|
||||||
|
int complex_args = 0, parenthesized = 0;
|
||||||
handle_fpdef:
|
handle_fpdef:
|
||||||
/* XXX Need to worry about checking if TYPE(CHILD(n, i+1)) is
|
/* XXX Need to worry about checking if TYPE(CHILD(n, i+1)) is
|
||||||
anything other than EQUAL or a comma? */
|
anything other than EQUAL or a comma? */
|
||||||
|
@ -707,6 +708,12 @@ ast_for_arguments(struct compiling *c, const node *n)
|
||||||
found_default = 1;
|
found_default = 1;
|
||||||
}
|
}
|
||||||
else if (found_default) {
|
else if (found_default) {
|
||||||
|
/* def f((x)=4): pass should raise an error.
|
||||||
|
def f((x, (y))): pass will just incur the tuple unpacking warning. */
|
||||||
|
if (parenthesized && !complex_args) {
|
||||||
|
ast_error(n, "parenthesized arg with default");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
ast_error(n,
|
ast_error(n,
|
||||||
"non-default argument follows default argument");
|
"non-default argument follows default argument");
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -719,6 +726,7 @@ ast_for_arguments(struct compiling *c, const node *n)
|
||||||
if (Py_Py3kWarningFlag && !ast_warn(c, ch,
|
if (Py_Py3kWarningFlag && !ast_warn(c, ch,
|
||||||
"tuple parameter unpacking has been removed in 3.x"))
|
"tuple parameter unpacking has been removed in 3.x"))
|
||||||
goto error;
|
goto error;
|
||||||
|
complex_args = 1;
|
||||||
asdl_seq_SET(args, k++, compiler_complex_args(c, ch));
|
asdl_seq_SET(args, k++, compiler_complex_args(c, ch));
|
||||||
if (!asdl_seq_GET(args, k-1))
|
if (!asdl_seq_GET(args, k-1))
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -726,6 +734,7 @@ ast_for_arguments(struct compiling *c, const node *n)
|
||||||
/* def foo((x)): setup for checking NAME below. */
|
/* def foo((x)): setup for checking NAME below. */
|
||||||
/* Loop because there can be many parens and tuple
|
/* Loop because there can be many parens and tuple
|
||||||
unpacking mixed in. */
|
unpacking mixed in. */
|
||||||
|
parenthesized = 1;
|
||||||
ch = CHILD(ch, 0);
|
ch = CHILD(ch, 0);
|
||||||
assert(TYPE(ch) == fpdef);
|
assert(TYPE(ch) == fpdef);
|
||||||
goto handle_fpdef;
|
goto handle_fpdef;
|
||||||
|
@ -747,7 +756,13 @@ ast_for_arguments(struct compiling *c, const node *n)
|
||||||
|
|
||||||
}
|
}
|
||||||
i += 2; /* the name and the comma */
|
i += 2; /* the name and the comma */
|
||||||
|
if (parenthesized && Py_Py3kWarningFlag &&
|
||||||
|
!ast_warn(c, ch, "parenthesized argument names "
|
||||||
|
"are invalid in 3.x"))
|
||||||
|
goto error;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case STAR:
|
case STAR:
|
||||||
if (!forbidden_check(c, CHILD(n, i+1), STR(CHILD(n, i+1))))
|
if (!forbidden_check(c, CHILD(n, i+1), STR(CHILD(n, i+1))))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
Loading…
Reference in New Issue