eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py

branch
eric7
changeset 8802
129a973fc33e
parent 8801
8fbb21be8579
child 8808
033fa34447d0
equal deleted inserted replaced
8801:8fbb21be8579 8802:129a973fc33e
5 5
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 copy 11 import copy
11 import sys 12 import sys
12 13
13 14
14 class ImportsChecker: 15 class ImportsChecker:
16 Class implementing a checker for import statements. 17 Class implementing a checker for import statements.
17 """ 18 """
18 Codes = [ 19 Codes = [
19 ## Local imports 20 ## Local imports
20 "I101", "I102", "I103", 21 "I101", "I102", "I103",
22
23 ## Imports order
24 "I201", "I202", "I203", "I204",
21 ] 25 ]
22 26
23 def __init__(self, source, filename, tree, select, ignore, expected, 27 def __init__(self, source, filename, tree, select, ignore, expected,
24 repeat, args): 28 repeat, args):
25 """ 29 """
57 # collection of detected errors 61 # collection of detected errors
58 self.errors = [] 62 self.errors = []
59 63
60 checkersWithCodes = [ 64 checkersWithCodes = [
61 (self.__checkLocalImports, ("I101", "I102", "I103")), 65 (self.__checkLocalImports, ("I101", "I102", "I103")),
66 (self.__checkImportOrder, ("I201", "I202", "I203", "I204"))
62 ] 67 ]
63 68
64 self.__checkers = [] 69 self.__checkers = []
65 for checker, codes in checkersWithCodes: 70 for checker, codes in checkersWithCodes:
66 if any(not (code and self.__ignoreCode(code)) 71 if any(not (code and self.__ignoreCode(code))
196 from .LocalImportVisitor import LocalImportVisitor 201 from .LocalImportVisitor import LocalImportVisitor
197 202
198 visitor = LocalImportVisitor(self.__args, self) 203 visitor = LocalImportVisitor(self.__args, self)
199 visitor.visit(copy.deepcopy(self.__tree)) 204 visitor.visit(copy.deepcopy(self.__tree))
200 for violation in visitor.violations: 205 for violation in visitor.violations:
201 node = violation[0] 206 if not self.__ignoreCode(violation[1]):
202 reason = violation[1] 207 node = violation[0]
203 self.__error(node.lineno - 1, node.col_offset, reason) 208 reason = violation[1]
209 self.__error(node.lineno - 1, node.col_offset, reason)
210
211 #######################################################################
212 ## Import order
213 ##
214 ## adapted from: flake8-alphabetize v0.0.17
215 #######################################################################
216
217 def __checkImportOrder(self):
218 """
219 Private method to check the order of import statements.
220 """
221 from .ImportNode import ImportNode
222
223 errors = []
224 imports = []
225 importNodes, listNode = self.__findNodes(self.__tree)
226
227 # check for an error in '__all__'
228 allError = self.__findErrorInAll(listNode)
229 if allError is not None:
230 errors.append(allError)
231
232 for importNode in importNodes:
233 if (
234 isinstance(importNode, ast.Import) and
235 len(importNode.names) > 1
236 ):
237 # skip suck imports because its already handled by pycodestyle
238 continue
239
240 imports.append(ImportNode(
241 self.__args.get("ApplicationPackageNames", []),
242 importNode, self))
243
244 lenImports = len(imports)
245 if lenImports > 0:
246 p = imports[0]
247 if p.error is not None:
248 errors.append(p.error)
249
250 if lenImports > 1:
251 for n in imports[1:]:
252 if n.error is not None:
253 errors.append(n.error)
254
255 if n == p:
256 errors.append((n.node, "I203", str(p), str(n)))
257 elif n < p:
258 errors.append((n.node, "I201", str(n), str(p)))
259
260 p = n
261
262 for error in errors:
263 if not self.__ignoreCode(error[1]):
264 node = error[0]
265 reason = error[1]
266 args = error[2:]
267 self.__error(node.lineno - 1, node.col_offset, reason, *args)
268
269 def __findNodes(self, tree):
270 """
271 Private method to find all import and import from nodes of the given
272 tree.
273
274 @param tree reference to the ast node tree to be parsed
275 @type ast.AST
276 @return tuple containing a list of import nodes and the '__all__' node
277 @rtype tuple of (ast.Import | ast.ImportFrom, ast.List | ast.Tuple)
278 """
279 importNodes = []
280 listNode = None
281
282 if isinstance(tree, ast.Module):
283 body = tree.body
284
285 for n in body:
286 if isinstance(n, (ast.Import, ast.ImportFrom)):
287 importNodes.append(n)
288
289 elif isinstance(n, ast.Assign):
290 for t in n.targets:
291 if isinstance(t, ast.Name) and t.id == "__all__":
292 value = n.value
293
294 if isinstance(value, (ast.List, ast.Tuple)):
295 listNode = value
296
297 return importNodes, listNode
298
299 def __findErrorInAll(self, node):
300 """
301 Private method to check the '__all__' node for errors.
302
303 @param node reference to the '__all__' node
304 @type ast.List or ast.Tuple
305 @return tuple containing a reference to the node and an error code
306 @rtype rtype tuple of (ast.List | ast.Tuple, str)
307 """
308 if node is not None:
309 actualList = []
310 for el in node.elts:
311 if isinstance(el, ast.Constant):
312 actualList.append(el.value)
313 elif isinstance(el, ast.Str):
314 actualList.append(el.s)
315 else:
316 # Can't handle anything that isn't a string literal
317 return None
318
319 expectedList = sorted(actualList)
320 if expectedList != actualList:
321 return (node, "I204", ", ".join(expectedList))
322
323 return None

eric ide

mercurial