Skip to content

Commit

Permalink
Merge python#10
Browse files Browse the repository at this point in the history
10: Warn for listcomp r=ltratt a=nanjekyejoannah

Warnings for list comprehension, which include:
- explicit tuple/parentheses for list items
- leaking iterator variable.

Co-authored-by: Joannah Nanjekye <jnanjekye@python.org>
  • Loading branch information
bors[bot] and nanjekyejoannah authored Nov 20, 2022
2 parents 8deb7c5 + e22a3b7 commit 4b248a8
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Lib/test/test_gdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ def get_gdb_repr(self, source,

def assertEndsWith(self, actual, exp_end):
'''Ensure that the given "actual" string ends with "exp_end"'''
self.assertTrue(actual.endswith(exp_end),
self.assertFalse(actual.endswith(exp_end),
msg='%r did not end with %r' % (actual, exp_end))

def assertMultilineMatches(self, actual, pattern):
Expand Down
26 changes: 22 additions & 4 deletions Lib/test/test_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ def test_plain_integers(self):
self.assertEqual(str(w.message), "using just a '0' prefix for octal literals is not supported in 3.x: " \
"use the '0o' prefix for octal integers")
self.assertEqual(len(w), 2)
self.assertIn("using just a '0' prefix for octal literals is not supported in 3.x:: \n"
self.assertIn("using just a '0' prefix for octal literals is not supported in 3.x:: \n"
"use the '0o' prefix for octal integers",
str(oct.exception))

def test_long_integers(self):
x = 0L
x = 0l
Expand Down Expand Up @@ -472,7 +472,7 @@ def test_print_py3k_warnings(self):
print >> sys.stdout
for warning in w:
self.assertTrue(Py3xWarning is w.category)
self.assertEqual(str(w.message), "print must be called as a function, not a statement in 3.x",
self.assertEqual(str(w.message), "print must be called as a function, not a statement in 3.x",
"You can fix this now by using parentheses for arguments to 'print'")

def test_del_stmt(self):
Expand Down Expand Up @@ -1057,6 +1057,24 @@ def test_dictcomps(self):
nums = [1, 2, 3]
self.assertEqual({i:i+1 for i in nums}, {1: 2, 2: 3, 3: 4})

def test_listcomp_py3k_paren(self):
[x for x in [1, 2, 2]]
expected = "list comp without parenthesis is invalid in 3.x: use parenthesis for list items more than one"
with check_py3k_warnings((expected, SyntaxWarning)):
[x for x in 1, 2, 3]

def test_listcomps_py3k_scope(self):
def foo(): print([x for x in [1, 2, 2]])
expected = "This listcomp does not leak a variable in 3.x: assign the variable before use"
with check_py3k_warnings((expected, SyntaxWarning)):
def foo(): x = 0; [x for x in [1, 2, 2]]; print(x)
def foo(): x = 0; print(x); [x for x in [1, 2, 2]]
def foo():
x = 0
if x > 0:
[x for x in [1, 2, 2]]
print(x)

def test_listcomps(self):
# list comprehension tests
nums = [1, 2, 3, 4, 5]
Expand Down Expand Up @@ -1247,7 +1265,7 @@ def test_py3x_unicode_warnings_ur(self):
self.assertEqual(ur'foo', u'foo')
for warning in w:
self.assertTrue(Py3xWarning is w.category)
self.assertEqual(str(w.message), "the 'ur' prefix in string literals is not supported in 3.x: ",
self.assertEqual(str(w.message), "the 'ur' prefix in string literals is not supported in 3.x: ",
"use a 'u' and two backslashes for a literal backslash")


Expand Down
39 changes: 37 additions & 2 deletions Python/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -903,13 +903,23 @@ ast_for_decorators(struct compiling *c, const node *n)
return decorator_seq;
}

static int
compiler_islistcomp(stmt_ty s)
{
if (s->kind != Expr_kind)
return 0;
return s->v.Expr.value->kind == ListComp_kind;
}

static stmt_ty
ast_for_funcdef(struct compiling *c, const node *n, asdl_seq *decorator_seq)
{
/* funcdef: 'def' NAME parameters ':' suite */
identifier name;
arguments_ty args;
asdl_seq *body;
asdl_seq *body, *lstcomp, *var_of_int;
int nc, i, y, islistcomp;
stmt_ty st, var_st;
int name_i = 1;

REQ(n, funcdef);
Expand All @@ -925,6 +935,26 @@ ast_for_funcdef(struct compiling *c, const node *n, asdl_seq *decorator_seq)
body = ast_for_suite(c, CHILD(n, name_i + 3));
if (!body)
return NULL;

if (Py_Py3kWarningFlag) {
nc = asdl_seq_LEN(body);
for (i = 0; i < nc; i++) {
st = (stmt_ty)asdl_seq_GET(body, i);
islistcomp = compiler_islistcomp(st);
if (islistcomp) {
lstcomp = asdl_seq_GET(body, i);
var_of_int = asdl_seq_GET(lstcomp, 3);
for (y=i; y < nc; y++) {
var_st = (stmt_ty)asdl_seq_GET(body, y);
if ((var_st == var_of_int) &&
!ast_3x_warn(c, n, "This listcomp does not leak a variable in 3.x",
"assign the variable before use"))
return 0;
}
}
}
}


return FunctionDef(name, args, body, decorator_seq, LINENO(n),
n->n_col_offset, c->c_arena);
Expand Down Expand Up @@ -1436,8 +1466,9 @@ ast_for_atom(struct compiling *c, const node *n)
case LSQB: /* list (or list comprehension) */
ch = CHILD(n, 1);

if (TYPE(ch) == RSQB)
if (TYPE(ch) == RSQB) {
return List(NULL, Load, LINENO(n), n->n_col_offset, c->c_arena);
}

REQ(ch, listmaker);
if (NCH(ch) == 1 || TYPE(CHILD(ch, 1)) == COMMA) {
Expand All @@ -1448,6 +1479,10 @@ ast_for_atom(struct compiling *c, const node *n)
return List(elts, Load, LINENO(n), n->n_col_offset, c->c_arena);
}
else
if (Py_Py3kWarningFlag &&
!ast_3x_warn(c, n, "list comp without parenthesis is invalid in 3.x",
"use parenthesis for list items more than one"))
return NULL;
return ast_for_listcomp(c, ch);
case LBRACE: {
/* dictorsetmaker:
Expand Down

0 comments on commit 4b248a8

Please sign in to comment.