src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py

branch
eric7
changeset 10360
9ffdb1490bd2
parent 10359
de0420dac60e
child 10361
e6ff9a4f6ee5
equal deleted inserted replaced
10359:de0420dac60e 10360:9ffdb1490bd2
55 "M112", 55 "M112",
56 ## Shadowed Builtins 56 ## Shadowed Builtins
57 "M131", 57 "M131",
58 "M132", 58 "M132",
59 ## Comprehensions 59 ## Comprehensions
60 "M180",
60 "M181", 61 "M181",
61 "M182", 62 "M182",
62 "M183", 63 "M183",
63 "M184", 64 "M184",
64 "M185", 65 "M185",
65 "M186", 66 "M186",
66 "M187", 67 "M187",
67 "M188", 68 "M188",
68 "M189", 69 "M189",
70 "M190",
69 "M191", 71 "M191",
70 "M192", 72 "M192",
71 "M193", 73 "M193",
74 "M194",
72 "M195", 75 "M195",
73 "M196",
74 "M197",
75 "M198",
76 ## Dictionaries with sorted keys 76 ## Dictionaries with sorted keys
77 "M201", 77 "M201",
78 ## Property 78 ## Property
79 "M210", 79 "M210",
80 "M211", 80 "M211",
246 (self.__checkCopyright, ("M111", "M112")), 246 (self.__checkCopyright, ("M111", "M112")),
247 (self.__checkBuiltins, ("M131", "M132")), 247 (self.__checkBuiltins, ("M131", "M132")),
248 ( 248 (
249 self.__checkComprehensions, 249 self.__checkComprehensions,
250 ( 250 (
251 "M180",
251 "M181", 252 "M181",
252 "M182", 253 "M182",
253 "M183", 254 "M183",
254 "M184", 255 "M184",
255 "M185", 256 "M185",
256 "M186", 257 "M186",
257 "M187", 258 "M187",
258 "M188", 259 "M188",
259 "M189", 260 "M189",
261 "M190",
260 "M191", 262 "M191",
261 "M192", 263 "M192",
262 "M193", 264 "M193",
265 "M194",
263 "M195", 266 "M195",
264 "M196",
265 "M197",
266 "M198",
267 ), 267 ),
268 ), 268 ),
269 (self.__checkDictWithSortedKeys, ("M201",)), 269 (self.__checkDictWithSortedKeys, ("M201",)),
270 ( 270 (
271 self.__checkProperties, 271 self.__checkProperties,
845 self.__error(arg.lineno - 1, arg.col_offset, "M132", arg.arg) 845 self.__error(arg.lineno - 1, arg.col_offset, "M132", arg.arg)
846 846
847 def __checkComprehensions(self): 847 def __checkComprehensions(self):
848 """ 848 """
849 Private method to check some comprehension related things. 849 Private method to check some comprehension related things.
850 """ 850
851 This method is adapted from: flake8-comprehensions v3.14.0
852 Original: Copyright (c) 2017 Adam Johnson
853 """
854 visitedMapCalls = set()
855
851 for node in ast.walk(self.__tree): 856 for node in ast.walk(self.__tree):
852 if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): 857 if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):
853 nArgs = len(node.args) 858 numPositionalArgs = len(node.args)
854 nKwArgs = len(node.keywords) 859 numKeywordArgs = len(node.keywords)
855 860
856 if ( 861 if (
857 nArgs == 1 862 numPositionalArgs == 1
858 and isinstance(node.args[0], ast.GeneratorExp) 863 and isinstance(node.args[0], ast.GeneratorExp)
859 and node.func.id in ("list", "set") 864 and node.func.id in ("list", "set")
860 ): 865 ):
861 errorCode = { 866 errorCode = {
862 "list": "M181", 867 "list": "M180",
863 "set": "M182", 868 "set": "M181",
864 }[node.func.id] 869 }[node.func.id]
865 self.__error(node.lineno - 1, node.col_offset, errorCode) 870 self.__error(node.lineno - 1, node.col_offset, errorCode)
866 871
867 elif ( 872 elif (
868 nArgs == 1 873 numPositionalArgs == 1
874 and node.func.id == "dict"
875 and len(node.keywords) == 0
869 and isinstance(node.args[0], (ast.GeneratorExp, ast.ListComp)) 876 and isinstance(node.args[0], (ast.GeneratorExp, ast.ListComp))
870 and isinstance(node.args[0].elt, ast.Tuple) 877 and isinstance(node.args[0].elt, ast.Tuple)
871 and len(node.args[0].elt.elts) == 2 878 and len(node.args[0].elt.elts) == 2
872 and node.func.id == "dict"
873 ): 879 ):
874 if isinstance(node.args[0], ast.GeneratorExp): 880 if isinstance(node.args[0], ast.GeneratorExp):
875 errorCode = "M183" 881 errorCode = "M182"
876 else: 882 else:
877 errorCode = "M185" 883 errorCode = "M184"
878 self.__error(node.lineno - 1, node.col_offset, errorCode) 884 self.__error(node.lineno - 1, node.col_offset, errorCode)
879 885
880 elif ( 886 elif (
881 nArgs == 1 887 numPositionalArgs == 1
882 and isinstance(node.args[0], ast.ListComp) 888 and isinstance(node.args[0], ast.ListComp)
883 and node.func.id in ("list", "set") 889 and node.func.id in ("list", "set", "any", "all")
884 ): 890 ):
885 errorCode = { 891 errorCode = {
886 "list": "M195", 892 "list": "M191",
887 "set": "M184", 893 "set": "M183",
894 "any": "M199",
895 "all": "M199",
888 }[node.func.id] 896 }[node.func.id]
889 self.__error(node.lineno - 1, node.col_offset, errorCode) 897 self.__error(
890 898 node.lineno - 1, node.col_offset, errorCode, node.func.id
891 elif nArgs == 1 and ( 899 )
900
901 elif numPositionalArgs == 1 and (
892 isinstance(node.args[0], ast.Tuple) 902 isinstance(node.args[0], ast.Tuple)
893 and node.func.id == "tuple" 903 and node.func.id == "tuple"
894 or isinstance(node.args[0], ast.List) 904 or isinstance(node.args[0], ast.List)
895 and node.func.id == "list" 905 and node.func.id == "list"
896 ): 906 ):
897 errorCode = { 907 errorCode = {
898 "tuple": "M197", 908 "tuple": "M189a",
899 "list": "M198", 909 "list": "M190a",
910 }[node.func.id]
911 ##suffix = "remove the outer call to {func}()."
912 self.__error(
913 node.lineno - 1,
914 node.col_offset,
915 errorCode,
916 type(node.args[0]).__name__.lower(),
917 node.func.id,
918 )
919
920 elif (
921 numPositionalArgs == 1
922 and numKeywordArgs == 0
923 and isinstance(node.args[0], (ast.Dict, ast.DictComp))
924 and node.func.id == "dict"
925 ):
926 if isinstance(node.args[0], ast.Dict):
927 type_ = "dict"
928 else:
929 type_ = "dict comprehension"
930 self.__error(
931 node.lineno - 1,
932 node.col_offset,
933 "M198",
934 type_,
935 )
936
937 elif (
938 numPositionalArgs == 1
939 and isinstance(node.args[0], (ast.Tuple, ast.List))
940 and (
941 node.func.id in ("tuple", "list", "set")
942 or (
943 node.func.id == "dict"
944 and all(
945 isinstance(elt, ast.Tuple) and len(elt.elts) == 2
946 for elt in node.args[0].elts
947 )
948 )
949 )
950 ):
951 ##suffix = "rewrite as a {func} literal."
952 errorCode = {
953 "tuple": "M189b",
954 "list": "M190b",
955 "set": "M185",
956 "dict": "M186",
900 }[node.func.id] 957 }[node.func.id]
901 self.__error( 958 self.__error(
902 node.lineno - 1, 959 node.lineno - 1,
903 node.col_offset, 960 node.col_offset,
904 errorCode, 961 errorCode,
905 type(node.args[0]).__name__.lower(), 962 type(node.args[0]).__name__.lower(),
906 node.func.id, 963 node.func.id,
907 ) 964 )
908 965
909 elif ( 966 elif (
910 nArgs == 1 967 numPositionalArgs == 0
911 and isinstance(node.args[0], (ast.Tuple, ast.List))
912 and node.func.id in ("tuple", "list", "set", "dict")
913 ):
914 errorCode = {
915 "tuple": "M192",
916 "list": "M193",
917 "set": "M191",
918 "dict": "M191",
919 }[node.func.id]
920 self.__error(
921 node.lineno - 1,
922 node.col_offset,
923 errorCode,
924 type(node.args[0]).__name__.lower(),
925 node.func.id,
926 )
927
928 elif (
929 nArgs == 0
930 and not any(isinstance(a, ast.Starred) for a in node.args) 968 and not any(isinstance(a, ast.Starred) for a in node.args)
931 and not any(k.arg is None for k in node.keywords) 969 and not any(k.arg is None for k in node.keywords)
932 and node.func.id == "dict" 970 and node.func.id == "dict"
933 ) or ( 971 ) or (
934 nArgs == 0 and nKwArgs == 0 and node.func.id in ("tuple", "list") 972 numKeywordArgs == 0 and numKeywordArgs == 0 and node.func.id in (
973 "tuple", "list"
974 )
935 ): 975 ):
936 self.__error(node.lineno - 1, node.col_offset, "M186", node.func.id) 976 self.__error(node.lineno - 1, node.col_offset, "M188", node.func.id)
937 977
938 elif ( 978 elif (
939 node.func.id in {"list", "reversed"} 979 node.func.id in {"list", "reversed"}
940 and nArgs > 0 980 and numPositionalArgs > 0
941 and isinstance(node.args[0], ast.Call) 981 and isinstance(node.args[0], ast.Call)
942 and isinstance(node.args[0].func, ast.Name) 982 and isinstance(node.args[0].func, ast.Name)
943 and node.args[0].func.id == "sorted" 983 and node.args[0].func.id == "sorted"
944 ): 984 ):
945 if node.func.id == "reversed": 985 if node.func.id == "reversed":
955 995
956 if reverseFlagValue is None: 996 if reverseFlagValue is None:
957 self.__error( 997 self.__error(
958 node.lineno - 1, 998 node.lineno - 1,
959 node.col_offset, 999 node.col_offset,
960 "M187a", 1000 "M193a",
961 node.func.id, 1001 node.func.id,
962 node.args[0].func.id, 1002 node.args[0].func.id,
963 ) 1003 )
964 else: 1004 else:
965 self.__error( 1005 self.__error(
966 node.lineno - 1, 1006 node.lineno - 1,
967 node.col_offset, 1007 node.col_offset,
968 "M187b", 1008 "M193b",
969 node.func.id, 1009 node.func.id,
970 node.args[0].func.id, 1010 node.args[0].func.id,
971 not reverseFlagValue, 1011 not reverseFlagValue,
972 ) 1012 )
1013 ##if reverse_flag_value is None:
1014 ##remediation = " - toggle reverse argument to sorted()"
1015 ##else:
1016 ##remediation = " - use sorted(..., reverse={!r})".format(
1017 ##not reverse_flag_value
1018 ##)
1019
973 else: 1020 else:
974 self.__error( 1021 self.__error(
975 node.lineno - 1, 1022 node.lineno - 1,
976 node.col_offset, 1023 node.col_offset,
977 "M187c", 1024 "M193c",
978 node.func.id, 1025 node.func.id,
979 node.args[0].func.id, 1026 node.args[0].func.id,
980 ) 1027 )
981 1028
982 elif ( 1029 elif (
983 nArgs > 0 1030 numPositionalArgs > 0
984 and isinstance(node.args[0], ast.Call) 1031 and isinstance(node.args[0], ast.Call)
985 and isinstance(node.args[0].func, ast.Name) 1032 and isinstance(node.args[0].func, ast.Name)
986 and ( 1033 and (
987 ( 1034 (
988 node.func.id in {"set", "sorted"} 1035 node.func.id in {"set", "sorted"}
997 ) 1044 )
998 ): 1045 ):
999 self.__error( 1046 self.__error(
1000 node.lineno - 1, 1047 node.lineno - 1,
1001 node.col_offset, 1048 node.col_offset,
1002 "M188", 1049 "M194",
1003 node.args[0].func.id, 1050 node.args[0].func.id,
1004 node.func.id, 1051 node.func.id,
1005 ) 1052 )
1006 1053
1007 elif ( 1054 elif (
1008 node.func.id in {"reversed", "set", "sorted"} 1055 node.func.id in {"reversed", "set", "sorted"}
1009 and nArgs > 0 1056 and numPositionalArgs > 0
1010 and isinstance(node.args[0], ast.Subscript) 1057 and isinstance(node.args[0], ast.Subscript)
1011 and isinstance(node.args[0].slice, ast.Slice) 1058 and isinstance(node.args[0].slice, ast.Slice)
1012 and node.args[0].slice.lower is None 1059 and node.args[0].slice.lower is None
1013 and node.args[0].slice.upper is None 1060 and node.args[0].slice.upper is None
1014 and isinstance(node.args[0].slice.step, ast.UnaryOp) 1061 and isinstance(node.args[0].slice.step, ast.UnaryOp)
1015 and isinstance(node.args[0].slice.step.op, ast.USub) 1062 and isinstance(node.args[0].slice.step.op, ast.USub)
1016 and isinstance(node.args[0].slice.step.operand, ast.Constant) 1063 and isinstance(node.args[0].slice.step.operand, ast.Constant)
1017 and node.args[0].slice.step.operand.n == 1 1064 and node.args[0].slice.step.operand.n == 1
1018 ): 1065 ):
1019 self.__error(node.lineno - 1, node.col_offset, "M189", node.func.id) 1066 self.__error(node.lineno - 1, node.col_offset, "M195", node.func.id)
1020 1067
1021 elif isinstance(node, (ast.ListComp, ast.SetComp)) and ( 1068 elif (
1069 node.func.id == "map"
1070 and node not in visitedMapCalls
1071 and len(node.args) == 2
1072 and isinstance(node.args[0], ast.Lambda)
1073 ):
1074 self.__error(node.lineno - 1, node.col_offset, "M197", "generator expression")
1075
1076 elif (
1077 node.func.id in ("list", "set", "dict")
1078 and len(node.args) == 1
1079 and isinstance(node.args[0], ast.Call)
1080 and isinstance(node.args[0].func, ast.Name)
1081 and node.args[0].func.id == "map"
1082 and len(node.args[0].args) == 2
1083 and isinstance(node.args[0].args[0], ast.Lambda)
1084 ):
1085 # To avoid raising M197 on the map() call inside the list/set/dict.
1086 mapCall = node.args[0]
1087 visitedMapCalls.add(mapCall)
1088
1089 rewriteable = True
1090 if node.func.id == "dict":
1091 # For the generator expression to be rewriteable as a
1092 # dict comprehension, its lambda must return a 2-tuple.
1093 lambdaNode = node.args[0].args[0]
1094 if (
1095 not isinstance(lambdaNode.body, (ast.List, ast.Tuple))
1096 or len(lambdaNode.body.elts) != 2
1097 ):
1098 rewriteable = False
1099
1100 if rewriteable:
1101 comprehensionType = f"{node.func.id} comprehension"
1102 self.__error(node.lineno - 1, node.col_offset, "M197", comprehensionType)
1103
1104 elif isinstance(node, (ast.DictComp, ast.ListComp, ast.SetComp)) and (
1022 len(node.generators) == 1 1105 len(node.generators) == 1
1023 and not node.generators[0].ifs 1106 and not node.generators[0].ifs
1024 and not node.generators[0].is_async 1107 and not node.generators[0].is_async
1025 and ( 1108 and (
1026 isinstance(node.elt, ast.Name) 1109 (
1027 and isinstance(node.generators[0].target, ast.Name) 1110 isinstance(node, (ast.ListComp, ast.SetComp))
1028 and node.elt.id == node.generators[0].target.id 1111 and isinstance(node.elt, ast.Name)
1112 and isinstance(node.generators[0].target, ast.Name)
1113 and node.elt.id == node.generators[0].target.id
1114 )
1115 or (
1116 isinstance(node, ast.DictComp)
1117 and isinstance(node.key, ast.Name)
1118 and isinstance(node.value, ast.Name)
1119 and isinstance(node.generators[0].target, ast.Tuple)
1120 and len(node.generators[0].target.elts) == 2
1121 and isinstance(node.generators[0].target.elts[0], ast.Name)
1122 and node.generators[0].target.elts[0].id == node.key.id
1123 and isinstance(node.generators[0].target.elts[1], ast.Name)
1124 and node.generators[0].target.elts[1].id == node.value.id
1125 )
1029 ) 1126 )
1030 ): 1127 ):
1031 compType = { 1128 compType = {
1032 ast.DictComp: "dict", 1129 ast.DictComp: "dict",
1033 ast.ListComp: "list", 1130 ast.ListComp: "list",

eric ide

mercurial