|
99 | 99 | EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]') |
100 | 100 | WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)') |
101 | 101 | COMPARE_SINGLETON_REGEX = re.compile(r'([=!]=)\s*(None|False|True)') |
| 102 | +COMPARE_NEGATIVE_REGEX = re.compile(r'\b(not)\s+[^[({ ]+\s+(in|is)\s') |
102 | 103 | COMPARE_TYPE_REGEX = re.compile(r'(?:[=!]=|is(?:\s+not)?)\s*type(?:s.\w+Type' |
103 | 104 | r'|\s*\(\s*([^)]*[^ )])\s*\))') |
104 | 105 | KEYWORD_REGEX = re.compile(r'(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS)) |
@@ -937,6 +938,31 @@ def comparison_to_singleton(logical_line, noqa): |
937 | 938 | (code, singleton, msg)) |
938 | 939 |
|
939 | 940 |
|
| 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 | + |
940 | 966 | def comparison_type(logical_line): |
941 | 967 | """ |
942 | 968 | Object type comparisons should always use isinstance() instead of |
@@ -1018,40 +1044,6 @@ def python_3000_backticks(logical_line): |
1018 | 1044 | yield pos, "W604 backticks are deprecated, use 'repr()'" |
1019 | 1045 |
|
1020 | 1046 |
|
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 | | - |
1055 | 1047 | ############################################################################## |
1056 | 1048 | # Helper functions |
1057 | 1049 | ############################################################################## |
|
0 commit comments