Skip to content

Commit 4b248a8

Browse files
Merge python#10
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 <[email protected]>
2 parents 8deb7c5 + e22a3b7 commit 4b248a8

File tree

3 files changed

+60
-7
lines changed

3 files changed

+60
-7
lines changed

Lib/test/test_gdb.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def get_gdb_repr(self, source,
262262

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

268268
def assertMultilineMatches(self, actual, pattern):

Lib/test/test_grammar.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ def test_plain_integers(self):
7575
self.assertEqual(str(w.message), "using just a '0' prefix for octal literals is not supported in 3.x: " \
7676
"use the '0o' prefix for octal integers")
7777
self.assertEqual(len(w), 2)
78-
self.assertIn("using just a '0' prefix for octal literals is not supported in 3.x:: \n"
78+
self.assertIn("using just a '0' prefix for octal literals is not supported in 3.x:: \n"
7979
"use the '0o' prefix for octal integers",
8080
str(oct.exception))
81-
81+
8282
def test_long_integers(self):
8383
x = 0L
8484
x = 0l
@@ -472,7 +472,7 @@ def test_print_py3k_warnings(self):
472472
print >> sys.stdout
473473
for warning in w:
474474
self.assertTrue(Py3xWarning is w.category)
475-
self.assertEqual(str(w.message), "print must be called as a function, not a statement in 3.x",
475+
self.assertEqual(str(w.message), "print must be called as a function, not a statement in 3.x",
476476
"You can fix this now by using parentheses for arguments to 'print'")
477477

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

1060+
def test_listcomp_py3k_paren(self):
1061+
[x for x in [1, 2, 2]]
1062+
expected = "list comp without parenthesis is invalid in 3.x: use parenthesis for list items more than one"
1063+
with check_py3k_warnings((expected, SyntaxWarning)):
1064+
[x for x in 1, 2, 3]
1065+
1066+
def test_listcomps_py3k_scope(self):
1067+
def foo(): print([x for x in [1, 2, 2]])
1068+
expected = "This listcomp does not leak a variable in 3.x: assign the variable before use"
1069+
with check_py3k_warnings((expected, SyntaxWarning)):
1070+
def foo(): x = 0; [x for x in [1, 2, 2]]; print(x)
1071+
def foo(): x = 0; print(x); [x for x in [1, 2, 2]]
1072+
def foo():
1073+
x = 0
1074+
if x > 0:
1075+
[x for x in [1, 2, 2]]
1076+
print(x)
1077+
10601078
def test_listcomps(self):
10611079
# list comprehension tests
10621080
nums = [1, 2, 3, 4, 5]
@@ -1247,7 +1265,7 @@ def test_py3x_unicode_warnings_ur(self):
12471265
self.assertEqual(ur'foo', u'foo')
12481266
for warning in w:
12491267
self.assertTrue(Py3xWarning is w.category)
1250-
self.assertEqual(str(w.message), "the 'ur' prefix in string literals is not supported in 3.x: ",
1268+
self.assertEqual(str(w.message), "the 'ur' prefix in string literals is not supported in 3.x: ",
12511269
"use a 'u' and two backslashes for a literal backslash")
12521270

12531271

Python/ast.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -903,13 +903,23 @@ ast_for_decorators(struct compiling *c, const node *n)
903903
return decorator_seq;
904904
}
905905

906+
static int
907+
compiler_islistcomp(stmt_ty s)
908+
{
909+
if (s->kind != Expr_kind)
910+
return 0;
911+
return s->v.Expr.value->kind == ListComp_kind;
912+
}
913+
906914
static stmt_ty
907915
ast_for_funcdef(struct compiling *c, const node *n, asdl_seq *decorator_seq)
908916
{
909917
/* funcdef: 'def' NAME parameters ':' suite */
910918
identifier name;
911919
arguments_ty args;
912-
asdl_seq *body;
920+
asdl_seq *body, *lstcomp, *var_of_int;
921+
int nc, i, y, islistcomp;
922+
stmt_ty st, var_st;
913923
int name_i = 1;
914924

915925
REQ(n, funcdef);
@@ -925,6 +935,26 @@ ast_for_funcdef(struct compiling *c, const node *n, asdl_seq *decorator_seq)
925935
body = ast_for_suite(c, CHILD(n, name_i + 3));
926936
if (!body)
927937
return NULL;
938+
939+
if (Py_Py3kWarningFlag) {
940+
nc = asdl_seq_LEN(body);
941+
for (i = 0; i < nc; i++) {
942+
st = (stmt_ty)asdl_seq_GET(body, i);
943+
islistcomp = compiler_islistcomp(st);
944+
if (islistcomp) {
945+
lstcomp = asdl_seq_GET(body, i);
946+
var_of_int = asdl_seq_GET(lstcomp, 3);
947+
for (y=i; y < nc; y++) {
948+
var_st = (stmt_ty)asdl_seq_GET(body, y);
949+
if ((var_st == var_of_int) &&
950+
!ast_3x_warn(c, n, "This listcomp does not leak a variable in 3.x",
951+
"assign the variable before use"))
952+
return 0;
953+
}
954+
}
955+
}
956+
}
957+
928958

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

1439-
if (TYPE(ch) == RSQB)
1469+
if (TYPE(ch) == RSQB) {
14401470
return List(NULL, Load, LINENO(n), n->n_col_offset, c->c_arena);
1471+
}
14411472

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

0 commit comments

Comments
 (0)