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

branch
eric7
changeset 9275
1a7d545d3ef2
parent 9274
86fab0c74430
child 9453
e5065dde905d
equal deleted inserted replaced
9274:86fab0c74430 9275:1a7d545d3ef2
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 11 import copy
12 import re
12 import sys 13 import sys
13 14
14 15
15 class ImportsChecker: 16 class ImportsChecker:
16 """ 17 """
503 return None 504 return None
504 505
505 ####################################################################### 506 #######################################################################
506 ## Tidy imports 507 ## Tidy imports
507 ## 508 ##
508 ## adapted from: flake8-tidy-imports v4.5.0 509 ## adapted from: flake8-tidy-imports v4.8.0
509 ####################################################################### 510 #######################################################################
510 # TODO: update to v4.8.0
511 511
512 def __tidyImports(self): 512 def __tidyImports(self):
513 """ 513 """
514 Private method to check various other import related topics. 514 Private method to check various other import related topics.
515 """ 515 """
516 self.__bannedModules = self.__args.get("BannedModules", [])
517 self.__banRelativeImports = self.__args.get("BanRelativeImports", "") 516 self.__banRelativeImports = self.__args.get("BanRelativeImports", "")
517 self.__bannedModules = []
518 self.__bannedStructuredPatterns = []
519 self.__bannedUnstructuredPatterns = []
520 for module in self.__args.get("BannedModules", []):
521 module = module.strip()
522 if "*" in module[:-1] or module == "*":
523 # unstructured
524 self.__bannedUnstructuredPatterns.append(
525 self.__compileUnstructuredGlob(module)
526 )
527 elif module.endswith(".*"):
528 # structured
529 self.__bannedStructuredPatterns.append(module)
530 # Also check for exact matches without the wildcard
531 # e.g. "foo.*" matches "foo"
532 prefix = module[:-2]
533 if prefix not in self.__bannedModules:
534 self.__bannedModules.append(prefix)
535 else:
536 self.__bannedModules.append(module)
537
538 # Sort the structured patterns so we match the specifc ones first.
539 self.__bannedStructuredPatterns.sort(key=lambda x: len(x[0]), reverse=True)
518 540
519 ruleMethods = [] 541 ruleMethods = []
520 if not self.__ignoreCode("I901"): 542 if not self.__ignoreCode("I901"):
521 ruleMethods.append(self.__checkUnnecessaryAlias) 543 ruleMethods.append(self.__checkUnnecessaryAlias)
522 if not self.__ignoreCode("I902") and bool(self.__bannedModules): 544 if not self.__ignoreCode("I902") and bool(self.__bannedModules):
528 550
529 for node in ast.walk(self.__tree): 551 for node in ast.walk(self.__tree):
530 for method in ruleMethods: 552 for method in ruleMethods:
531 method(node) 553 method(node)
532 554
555 def __compileUnstructuredGlob(self, module):
556 """
557 Private method to convert a pattern to a regex such that ".*" matches zero or
558 more modules.
559
560 @param module module pattern to be converted
561 @type str
562 @return compiled regex
563 @rtype re.regex object
564 """
565 parts = module.split(".")
566 transformedParts = [
567 "(\\..*)?" if p == "*" else "\\." + re.escape(p) for p in parts
568 ]
569 if parts[0] == "*":
570 transformedParts[0] = ".*"
571 else:
572 transformedParts[0] = re.escape(parts[0])
573 return re.compile("".join(transformedParts) + "\\Z")
574
533 def __checkUnnecessaryAlias(self, node): 575 def __checkUnnecessaryAlias(self, node):
534 """ 576 """
535 Private method to check unnecessary import aliases. 577 Private method to check unnecessary import aliases.
536 578
537 @param node reference to the node to be checked 579 @param node reference to the node to be checked
557 for alias in node.names: 599 for alias in node.names:
558 if alias.name == alias.asname: 600 if alias.name == alias.asname:
559 rewritten = "from {0} import {1}".format(node.module, alias.name) 601 rewritten = "from {0} import {1}".format(node.module, alias.name)
560 602
561 self.__error(node.lineno - 1, node.col_offset, "I901", rewritten) 603 self.__error(node.lineno - 1, node.col_offset, "I901", rewritten)
604
605 def __isModuleBanned(self, moduleName):
606 """
607 Private method to check, if the given module name banned.
608
609 @param moduleName module name to be checked
610 @type str
611 @return flag indicating a banned module
612 @rtype bool
613 """
614 if moduleName in self.__bannedModules:
615 return True
616
617 # Check unustructed wildcards
618 if any(
619 bannedPattern.match(moduleName)
620 for bannedPattern in self.__bannedUnstructuredPatterns
621 ):
622 return True
623
624 # Check structured wildcards
625 if any(
626 moduleName.startswith(bannedPrefix[:-1])
627 for bannedPrefix in self.__bannedStructuredPatterns
628 ):
629 return True
630
631 return False
562 632
563 def __checkBannedImport(self, node): 633 def __checkBannedImport(self, node):
564 """ 634 """
565 Private method to check import of banned modules. 635 Private method to check import of banned modules.
566 636
584 moduleNames.sort(key=len, reverse=True) 654 moduleNames.sort(key=len, reverse=True)
585 655
586 warned = set() 656 warned = set()
587 657
588 for moduleName in moduleNames: 658 for moduleName in moduleNames:
589 if moduleName in self.__bannedModules: 659 if self.__isModuleBanned(moduleName):
590 if any(mod.startswith(moduleName) for mod in warned): 660 if any(mod.startswith(moduleName) for mod in warned):
591 # Do not show an error for this line if we already showed 661 # Do not show an error for this line if we already showed
592 # a more specific error. 662 # a more specific error.
593 continue 663 continue
594 else: 664 else:

eric ide

mercurial