Skip to content

Commit 63a0a02

Browse files
committed
Catch more E713 and E714 with a regular expression; issue #236 continued
1 parent 922e7fd commit 63a0a02

3 files changed

Lines changed: 63 additions & 36 deletions

File tree

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ Changelog
55
1.x (unreleased)
66
----------------
77

8+
* Report E713 and E714 when operators ``not in`` and ``is not`` are
9+
recommended. (Issue #236)
10+
811
* Allow the checkers to report errors on empty files. (Issue #240)
912

1013
* Fix ignoring too many checks when ``--select`` is used with codes

pep8.py

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
100100
WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)')
101101
COMPARE_SINGLETON_REGEX = re.compile(r'([=!]=)\s*(None|False|True)')
102+
COMPARE_NEGATIVE_REGEX = re.compile(r'\b(not)\s+[^[({ ]+\s+(in|is)\s')
102103
COMPARE_TYPE_REGEX = re.compile(r'(?:[=!]=|is(?:\s+not)?)\s*type(?:s.\w+Type'
103104
r'|\s*\(\s*([^)]*[^ )])\s*\))')
104105
KEYWORD_REGEX = re.compile(r'(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS))
@@ -937,6 +938,31 @@ def comparison_to_singleton(logical_line, noqa):
937938
(code, singleton, msg))
938939

939940

941+
def comparison_negative(logical_line):
942+
r"""
943+
Negative comparison, either identity or membership, should be
944+
done using "not in" and "is not".
945+
946+
Okay: if x not in y:\n pass
947+
Okay: assert (X in Y or X is Z)
948+
Okay: if not (X in Y):\n pass
949+
Okay: zz = x is not y
950+
E713: Z = not X in Y
951+
E713: if not X.B in Y:\n pass
952+
E714: if not X is Y:\n pass
953+
E714: Z = not X.B is Y
954+
"""
955+
match = COMPARE_NEGATIVE_REGEX.search(logical_line)
956+
if match:
957+
if match.group(2) == 'in':
958+
msg = ("E713: Use the 'not in' "
959+
"operator for collection membership evaluation")
960+
else:
961+
msg = ("E714: Use the 'is not' "
962+
"operator when testing for unequal identities")
963+
yield match.start(1), msg
964+
965+
940966
def comparison_type(logical_line):
941967
"""
942968
Object type comparisons should always use isinstance() instead of
@@ -1018,40 +1044,6 @@ def python_3000_backticks(logical_line):
10181044
yield pos, "W604 backticks are deprecated, use 'repr()'"
10191045

10201046

1021-
def not_in(logical_line):
1022-
"""
1023-
Check for use of "not in" for evaluating membership.
1024-
1025-
Okay: if x not in y:\n pass
1026-
Okay: if not (X in Y or X is Z):\n pass
1027-
Okay: if not (X in Y):\n pass
1028-
E713: if not X in Y
1029-
E713: if not X.B in Y
1030-
"""
1031-
1032-
split_line = logical_line.split()
1033-
if (len(split_line) == 5 and split_line[0] == 'if' and
1034-
split_line[1] == 'not' and split_line[3] == 'in' and not
1035-
split_line[2].startswith('(')):
1036-
yield (logical_line.find('not'), "E713: Use the 'not in' "
1037-
"operator for collection membership evaluation")
1038-
1039-
1040-
def is_not(logical_line):
1041-
"""
1042-
Check for use of 'is not' for testing unequal identities.
1043-
1044-
Okay: if x is not y:\n pass
1045-
E714: if not X is Y
1046-
E714: if not X.B is Y
1047-
"""
1048-
1049-
split_line = logical_line.split()
1050-
if (len(split_line) == 5 and split_line[0] == 'if' and
1051-
split_line[1] == 'not' and split_line[3] == 'is'):
1052-
yield (logical_line.find('not'), "E714: Use the 'is not' "
1053-
"operator when testing for unequal identities")
1054-
10551047
##############################################################################
10561048
# Helper functions
10571049
##############################################################################

testsuite/E71.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,41 @@
1+
#: E711
2+
if res == None:
3+
pass
14
#: E712
25
if res == True:
36
pass
47
#: E712
58
if res != False:
69
pass
7-
#: E711
8-
if res == None:
10+
11+
#
12+
#: E713
13+
if not X in Y:
14+
pass
15+
#: E713
16+
if not X.B in Y:
17+
pass
18+
#: E713
19+
if not X in Y and Z == "zero":
20+
pass
21+
#: E713
22+
if X == "zero" or not Y in Z:
23+
pass
24+
25+
#
26+
#: E714
27+
if not X is Y:
28+
pass
29+
#: E714
30+
if not X.B is Y:
31+
pass
32+
#: Okay
33+
if x not in y:
34+
pass
35+
if not (X in Y or X is Z):
36+
pass
37+
if not (X in Y):
38+
pass
39+
if x is not y:
940
pass
41+
#:

0 commit comments

Comments
 (0)