Plugins/CheckerPlugins/CodeStyleChecker/MiscellaneousChecker.py

changeset 6180
8d72871c16ba
parent 6178
905ea208884a
child 6182
f293e95b914d
equal deleted inserted replaced
6179:8d74886f2107 6180:8d72871c16ba
30 30
31 "M601", 31 "M601",
32 "M611", "M612", "M613", 32 "M611", "M612", "M613",
33 "M621", "M622", "M623", "M624", "M625", 33 "M621", "M622", "M623", "M624", "M625",
34 "M631", "M632", 34 "M631", "M632",
35 "M651", "M652", "M653", "M654", "M655",
35 36
36 "M701", "M702", 37 "M701", "M702",
37 38
38 "M801", 39 "M801",
39 "M811", 40 "M811",
106 (self.__checkDictWithSortedKeys, ("M201",)), 107 (self.__checkDictWithSortedKeys, ("M201",)),
107 (self.__checkPep3101, ("M601",)), 108 (self.__checkPep3101, ("M601",)),
108 (self.__checkFormatString, ("M611", "M612", "M613", 109 (self.__checkFormatString, ("M611", "M612", "M613",
109 "M621", "M622", "M623", "M624", "M625", 110 "M621", "M622", "M623", "M624", "M625",
110 "M631", "M632")), 111 "M631", "M632")),
112 (self.__checkLogging, ("M651", "M652", "M653", "M654", "M655")),
111 (self.__checkFuture, ("M701", "M702")), 113 (self.__checkFuture, ("M701", "M702")),
112 (self.__checkPrintStatements, ("M801",)), 114 (self.__checkPrintStatements, ("M801",)),
113 (self.__checkTuple, ("M811", )), 115 (self.__checkTuple, ("M811", )),
114 (self.__checkMutableDefault, ("M821", "M822")), 116 (self.__checkMutableDefault, ("M821", "M822")),
115 ] 117 ]
642 if isinstance(node, ast.Dict) and self.__dictShouldBeChecked(node): 644 if isinstance(node, ast.Dict) and self.__dictShouldBeChecked(node):
643 for key1, key2 in zip(node.keys, node.keys[1:]): 645 for key1, key2 in zip(node.keys, node.keys[1:]):
644 if key2.s < key1.s: 646 if key2.s < key1.s:
645 self.__error(key2.lineno - 1, key2.col_offset, 647 self.__error(key2.lineno - 1, key2.col_offset,
646 "M201", key2.s, key1.s) 648 "M201", key2.s, key1.s)
649
650 def __checkLogging(self):
651 """
652 Private method to check logging statements.
653 """
654 visitor = LoggingVisitor()
655 visitor.visit(self.__tree)
656 for node, reason in visitor.violations:
657 self.__error(node.lineno - 1, node.col_offset, reason)
647 658
648 659
649 class TextVisitor(ast.NodeVisitor): 660 class TextVisitor(ast.NodeVisitor):
650 """ 661 """
651 Class implementing a node visitor for bytes and str instances. 662 Class implementing a node visitor for bytes and str instances.
782 node.func.value.id == 'str' and node.args and 793 node.func.value.id == 'str' and node.args and
783 self.__isBaseString(node.args[0])): 794 self.__isBaseString(node.args[0])):
784 self.calls[node.args[0]] = (node, True) 795 self.calls[node.args[0]] = (node, True)
785 super(TextVisitor, self).generic_visit(node) 796 super(TextVisitor, self).generic_visit(node)
786 797
798
799 class LoggingVisitor(ast.NodeVisitor):
800 """
801 Class implementing a node visitor to check logging statements.
802 """
803 LoggingLevels = {
804 "debug",
805 "critical",
806 "error",
807 "info",
808 "warn",
809 "warning",
810 }
811
812 def __init__(self):
813 """
814 Constructor
815 """
816 super(LoggingVisitor, self).__init__()
817
818 self.__currentLoggingCall = None
819 self.__currentLoggingArgument = None
820 self.__currentLoggingLevel = None
821 self.__currentExtraKeyword = None
822 self.violations = []
823
824 def __withinLoggingStatement(self):
825 """
826 Private method to check, if we are inside a logging statement.
827
828 @return flag indicating we are inside a logging statement
829 @rtype bool
830 """
831 return self.__currentLoggingCall is not None
832
833 def __withinLoggingArgument(self):
834 """
835 Private method to check, if we are inside a logging argument.
836
837 @return flag indicating we are inside a logging argument
838 @rtype bool
839 """
840 return self.__currentLoggingArgument is not None
841
842 def __withinExtraKeyword(self, node):
843 """
844 Private method to check, if we are inside the extra keyword.
845
846 @param node reference to the node to be checked
847 @type ast.keyword
848 @return flag indicating we are inside the extra keyword
849 @rtype bool
850 """
851 return self.__currentExtraKeyword is not None and \
852 self.__currentExtraKeyword != node
853
854 def __detectLoggingLevel(self, node):
855 """
856 Private method to decide whether an AST Call is a logging call.
857
858 @param node reference to the node to be processed
859 @type ast.Call
860 @return logging level
861 @rtype str or None
862 """
863 try:
864 if node.func.value.id == "warnings":
865 return None
866
867 if node.func.attr in LoggingVisitor.LoggingLevels:
868 return node.func.attr
869 except AttributeError:
870 pass
871
872 return None
873
874 def __isFormatCall(self, node):
875 """
876 Private method to check if a function call uses format.
877
878 @param node reference to the node to be processed
879 @type ast.Call
880 @return flag indicating the function call uses format
881 @rtype bool
882 """
883 try:
884 return node.func.attr == "format"
885 except AttributeError:
886 return False
887
888 def visit_Call(self, node):
889 """
890 Public method to handle a function call.
891
892 Every logging statement and string format is expected to be a function
893 call.
894
895 @param node reference to the node to be processed
896 @type ast.Call
897 """
898 # we are in a logging statement
899 if self.__withinLoggingStatement():
900 if self.__withinLoggingArgument() and self.__isFormatCall(node):
901 self.violations.append((node, "M651"))
902 super(LoggingVisitor, self).generic_visit(node)
903 return
904
905 loggingLevel = self.__detectLoggingLevel(node)
906
907 if loggingLevel and self.__currentLoggingLevel is None:
908 self.__currentLoggingLevel = loggingLevel
909
910 # we are in some other statement
911 if loggingLevel is None:
912 super(LoggingVisitor, self).generic_visit(node)
913 return
914
915 # we are entering a new logging statement
916 self.__currentLoggingCall = node
917
918 if loggingLevel == "warn":
919 self.violations.append((node, "M655"))
920
921 for index, child in enumerate(ast.iter_child_nodes(node)):
922 if index == 1:
923 self.__currentLoggingArgument = child
924 if index > 1 and isinstance(child, ast.keyword) and \
925 child.arg == "extra":
926 self.__currentExtraKeyword = child
927
928 super(LoggingVisitor, self).visit(child)
929
930 self.__currentLoggingArgument = None
931 self.__currentExtraKeyword = None
932
933 self.__currentLoggingCall = None
934 self.__currentLoggingLevel = None
935
936 def visit_BinOp(self, node):
937 """
938 Public method to handle binary operations while processing the first
939 logging argument.
940
941 @param node reference to the node to be processed
942 @type ast.BinOp
943 """
944 if self.__withinLoggingStatement() and self.__withinLoggingArgument():
945 # handle percent format
946 if isinstance(node.op, ast.Mod):
947 self.violations.append((node, "M652"))
948
949 # handle string concat
950 if isinstance(node.op, ast.Add):
951 self.violations.append((node, "M653"))
952
953 super(LoggingVisitor, self).generic_visit(node)
954
955 def visit_JoinedStr(self, node):
956 """
957 Public method to handle f-string arguments.
958
959 @param node reference to the node to be processed
960 @type ast.JoinedStr
961 """
962 if sys.version_info >= (3, 6):
963 if self.__withinLoggingStatement():
964 if any(isinstance(i, ast.FormattedValue) for i in node.values):
965 if self.__withinLoggingArgument():
966 self.violations.append((node, "M654"))
967
968 super(LoggingVisitor, self).generic_visit(node)
969
787 # 970 #
788 # eflag: noqa = M702 971 # eflag: noqa = M702

eric ide

mercurial