src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/NameOrder/NameOrderChecker.py

branch
eric7
changeset 11150
73d80859079c
parent 11145
d328a7b74fd8
equal deleted inserted replaced
11149:fc45672fae42 11150:73d80859079c
6 """ 6 """
7 Module implementing a checker for import statements. 7 Module implementing a checker for import statements.
8 """ 8 """
9 9
10 import ast 10 import ast
11 import copy
12 import re 11 import re
13 12
14 13 from CodeStyleTopicChecker import CodeStyleTopicChecker
15 class NameOrderChecker: 14
15
16 class NameOrderChecker(CodeStyleTopicChecker):
16 """ 17 """
17 Class implementing a checker for name ordering. 18 Class implementing a checker for name ordering.
18 19
19 Note: Name ordering is checked for import statements, the '__all__' statement 20 Note: Name ordering is checked for import statements, the '__all__' statement
20 and exception names of exception handlers. 21 and exception names of exception handlers.
21 """ 22 """
22 23
23 Codes = [ 24 Codes = [
24 ## Imports order 25 ## Imports order
25 "NO101", 26 "NO-101",
26 "NO102", 27 "NO-102",
27 "NO103", 28 "NO-103",
28 "NO104", 29 "NO-104",
29 "NO105", 30 "NO-105",
30 ] 31 ]
31 Prefix = "NO" 32 Category = "NO"
32 33
33 def __init__(self, source, filename, tree, select, ignore, expected, repeat, args): 34 def __init__(self, source, filename, tree, select, ignore, expected, repeat, args):
34 """ 35 """
35 Constructor 36 Constructor
36 37
49 @param repeat flag indicating to report each occurrence of a code 50 @param repeat flag indicating to report each occurrence of a code
50 @type bool 51 @type bool
51 @param args dictionary of arguments for the various checks 52 @param args dictionary of arguments for the various checks
52 @type dict 53 @type dict
53 """ 54 """
54 self.__select = tuple( 55 super().__init__(
55 x for x in select if x.startswith(NameOrderChecker.Prefix) 56 NameOrderChecker.Category,
57 source,
58 filename,
59 tree,
60 select,
61 ignore,
62 expected,
63 repeat,
64 args,
56 ) 65 )
57 self.__ignore = tuple(
58 x for x in ignore if x.startswith(NameOrderChecker.Prefix)
59 )
60 self.__expected = expected[:]
61 self.__repeat = repeat
62 self.__filename = filename
63 self.__source = source[:]
64 self.__tree = copy.deepcopy(tree)
65 self.__args = args
66 66
67 # parameters for import sorting 67 # parameters for import sorting
68 if args["SortOrder"] == "native": 68 if args["SortOrder"] == "native":
69 self.__sortingFunction = sorted 69 self.__sortingFunction = sorted
70 else: 70 else:
71 # naturally is the default sort order 71 # naturally is the default sort order
72 self.__sortingFunction = self.__naturally 72 self.__sortingFunction = self.__naturally
73 self.__sortCaseSensitive = args["SortCaseSensitive"] 73 self.__sortCaseSensitive = args["SortCaseSensitive"]
74 74
75 # statistics counters
76 self.counters = {}
77
78 # collection of detected errors
79 self.errors = []
80
81 checkersWithCodes = [ 75 checkersWithCodes = [
82 (self.__checkNameOrder, ("NO101", "NO102", "NO103", "NO104", "NO105")), 76 (self.__checkNameOrder, ("NO-101", "NO-102", "NO-103", "NO-104", "NO-105")),
83 ] 77 ]
84 78 self._initializeCheckers(checkersWithCodes)
85 self.__checkers = []
86 for checker, codes in checkersWithCodes:
87 if any(not (code and self.__ignoreCode(code)) for code in codes):
88 self.__checkers.append(checker)
89
90 def __ignoreCode(self, code):
91 """
92 Private method to check if the message code should be ignored.
93
94 @param code message code to check for
95 @type str
96 @return flag indicating to ignore the given code
97 @rtype bool
98 """
99 return code in self.__ignore or (
100 code.startswith(self.__ignore) and not code.startswith(self.__select)
101 )
102
103 def __error(self, lineNumber, offset, code, *args):
104 """
105 Private method to record an issue.
106
107 @param lineNumber line number of the issue
108 @type int
109 @param offset position within line of the issue
110 @type int
111 @param code message code
112 @type str
113 @param args arguments for the message
114 @type list
115 """
116 if self.__ignoreCode(code):
117 return
118
119 if code in self.counters:
120 self.counters[code] += 1
121 else:
122 self.counters[code] = 1
123
124 # Don't care about expected codes
125 if code in self.__expected:
126 return
127
128 if code and (self.counters[code] == 1 or self.__repeat):
129 # record the issue with one based line number
130 self.errors.append(
131 {
132 "file": self.__filename,
133 "line": lineNumber + 1,
134 "offset": offset,
135 "code": code,
136 "args": args,
137 }
138 )
139
140 def run(self):
141 """
142 Public method to check the given source against miscellaneous
143 conditions.
144 """
145 if not self.__filename:
146 # don't do anything, if essential data is missing
147 return
148
149 if not self.__checkers:
150 # don't do anything, if no codes were selected
151 return
152
153 for check in self.__checkers:
154 check()
155 79
156 ####################################################################### 80 #######################################################################
157 ## Name Order 81 ## Name Order
158 ## 82 ##
159 ## adapted from: flake8-alphabetize v0.0.21 83 ## adapted from: flake8-alphabetize v0.0.21
165 """ 89 """
166 from .ImportNode import ImportNode 90 from .ImportNode import ImportNode
167 91
168 errors = [] 92 errors = []
169 imports = [] 93 imports = []
170 importNodes, aListNode, eListNodes = self.__findNodes(self.__tree) 94 importNodes, aListNode, eListNodes = self.__findNodes(self.tree)
171 95
172 # check for an error in '__all__' 96 # check for an error in '__all__'
173 allError = self.__findErrorInAll(aListNode) 97 allError = self.__findErrorInAll(aListNode)
174 if allError is not None: 98 if allError is not None:
175 errors.append(allError) 99 errors.append(allError)
181 # skip suck imports because its already handled by pycodestyle 105 # skip suck imports because its already handled by pycodestyle
182 continue 106 continue
183 107
184 imports.append( 108 imports.append(
185 ImportNode( 109 ImportNode(
186 self.__args.get("ApplicationPackageNames", []), 110 self.args.get("ApplicationPackageNames", []),
187 importNode, 111 importNode,
188 self, 112 self,
189 self.__args.get("SortIgnoringStyle", False), 113 self.args.get("SortIgnoringStyle", False),
190 self.__args.get("SortFromFirst", False), 114 self.args.get("SortFromFirst", False),
191 ) 115 )
192 ) 116 )
193 117
194 lenImports = len(imports) 118 lenImports = len(imports)
195 if lenImports > 0: 119 if lenImports > 0:
201 for n in imports[1:]: 125 for n in imports[1:]:
202 if n.error is not None: 126 if n.error is not None:
203 errors.append(n.error) 127 errors.append(n.error)
204 128
205 if n == p: 129 if n == p:
206 if self.__args.get("CombinedAsImports", False) or ( 130 if self.args.get("CombinedAsImports", False) or (
207 not n.asImport and not p.asImport 131 not n.asImport and not p.asImport
208 ): 132 ):
209 errors.append((n.node, "NO103", str(p), str(n))) 133 errors.append((n.node, "NO-103", str(p), str(n)))
210 elif n < p: 134 elif n < p:
211 errors.append((n.node, "NO101", str(n), str(p))) 135 errors.append((n.node, "NO-101", str(n), str(p)))
212 136
213 p = n 137 p = n
214 138
215 for error in errors: 139 for error in errors:
216 if not self.__ignoreCode(error[1]): 140 self.addErrorFromNode(error[0], error[1], *error[2:])
217 node = error[0]
218 reason = error[1]
219 args = error[2:]
220 self.__error(node.lineno - 1, node.col_offset, reason, *args)
221 141
222 def __findExceptionListNodes(self, tree): 142 def __findExceptionListNodes(self, tree):
223 """ 143 """
224 Private method to find all exception types handled by given tree. 144 Private method to find all exception types handled by given tree.
225 145
275 """ 195 """
276 Private method to check the '__all__' node for errors. 196 Private method to check the '__all__' node for errors.
277 197
278 @param node reference to the '__all__' node 198 @param node reference to the '__all__' node
279 @type ast.List or ast.Tuple 199 @type ast.List or ast.Tuple
280 @return tuple containing a reference to the node an error code and the error 200 @return tuple containing a reference to the node, an error code and the error
281 arguments 201 arguments
282 @rtype tuple of (ast.List | ast.Tuple, str, str) 202 @rtype tuple of (ast.List | ast.Tuple, str, str)
283 """ 203 """
284 if node is not None: 204 if node is not None:
285 actualList = [] 205 actualList = []
293 expectedList = self.sorted( 213 expectedList = self.sorted(
294 actualList, 214 actualList,
295 key=lambda k: self.moduleKey(k, subImports=True), 215 key=lambda k: self.moduleKey(k, subImports=True),
296 ) 216 )
297 if expectedList != actualList: 217 if expectedList != actualList:
298 return (node, "NO104", ", ".join(expectedList)) 218 return (node, "NO-104", ", ".join(expectedList))
299 219
300 return None 220 return None
301 221
302 def __findExceptionListStr(self, node): 222 def __findExceptionListStr(self, node):
303 """ 223 """
329 for node in nodes: 249 for node in nodes:
330 actualList = [self.__findExceptionListStr(elt) for elt in node.elts] 250 actualList = [self.__findExceptionListStr(elt) for elt in node.elts]
331 251
332 expectedList = self.sorted(actualList) 252 expectedList = self.sorted(actualList)
333 if expectedList != actualList: 253 if expectedList != actualList:
334 errors.append((node, "NO105", ", ".join(expectedList))) 254 errors.append((node, "NO-105", ", ".join(expectedList)))
335 255
336 return errors 256 return errors
337 257
338 def sorted(self, toSort, key=None, reverse=False): 258 def sorted(self, toSort, key=None, reverse=False):
339 """ 259 """

eric ide

mercurial