136 |
137 |
137 @param node reference to the Call node |
138 @param node reference to the Call node |
138 @type ast.Call |
139 @type ast.Call |
139 """ |
140 """ |
140 self.__check115(node) |
141 self.__check115(node) |
|
142 |
|
143 self.generic_visit(node) |
|
144 |
|
145 def visit_With(self, node): |
|
146 """ |
|
147 Public method to process a With node. |
|
148 |
|
149 @param node reference to the With node |
|
150 @type ast.With |
|
151 """ |
|
152 self.__check117(node) |
|
153 |
|
154 self.generic_visit(node) |
|
155 |
|
156 def visit_Compare(self, node): |
|
157 """ |
|
158 Public method to process a Compare node. |
|
159 |
|
160 @param node reference to the Compare node |
|
161 @type ast.Compare |
|
162 """ |
|
163 self.__check118a(node) |
|
164 |
|
165 self.generic_visit(node) |
|
166 |
|
167 def visit_ClassDef(self, node): |
|
168 """ |
|
169 Public method to process a ClassDef node. |
|
170 |
|
171 @param node reference to the ClassDef node |
|
172 @type ast.ClassDef |
|
173 """ |
|
174 self.__check119(node) |
141 |
175 |
142 self.generic_visit(node) |
176 self.generic_visit(node) |
143 |
177 |
144 ############################################################# |
178 ############################################################# |
145 ## Helper methods for the various checkers below |
179 ## Helper methods for the various checkers below |
466 ): |
500 ): |
467 assign = unparse(node.body[0].targets[0]) |
501 assign = unparse(node.body[0].targets[0]) |
468 body = unparse(node.body[0].value) |
502 body = unparse(node.body[0].value) |
469 cond = unparse(node.test) |
503 cond = unparse(node.test) |
470 orelse = unparse(node.orelse[0].value) |
504 orelse = unparse(node.orelse[0].value) |
471 self.__error(node.lineno - 1, node.col_offset, "Y108", |
505 if len( |
472 assign, body, cond, orelse) |
506 "{0} = {1} if {2} else {3}".format(assign, body, cond, orelse) |
|
507 ) > 79: |
|
508 self.__error(node.lineno - 1, node.col_offset, "Y108a") |
|
509 else: |
|
510 self.__error(node.lineno - 1, node.col_offset, "Y108b", |
|
511 assign, body, cond, orelse) |
473 |
512 |
474 def __check109(self, node): |
513 def __check109(self, node): |
475 """ |
514 """ |
476 Private method to check for multiple equalities with the same value |
515 Private method to check for multiple equalities with the same value |
477 are combined via "or". |
516 are combined via "or". |
728 isinstance(node.orelse[0], ast.If) |
767 isinstance(node.orelse[0], ast.If) |
729 ): |
768 ): |
730 variable = node.test.left |
769 variable = node.test.left |
731 child = node.orelse[0] |
770 child = node.orelse[0] |
732 elseValue = None |
771 elseValue = None |
|
772 if node.body[0].value is not None: |
|
773 bodyValueStr = unparse(node.body[0].value).strip("'") |
|
774 else: |
|
775 bodyValueStr = "None" |
733 if isinstance(node.test.comparators[0], ast.Str): |
776 if isinstance(node.test.comparators[0], ast.Str): |
734 keyValuePairs = { |
777 keyValuePairs = { |
735 node.test.comparators[0].s: |
778 node.test.comparators[0].s: bodyValueStr |
736 unparse(node.body[0].value).strip("'") |
|
737 } |
779 } |
738 elif isinstance(node.test.comparators[0], ast.Num): |
780 elif isinstance(node.test.comparators[0], ast.Num): |
739 keyValuePairs = { |
781 keyValuePairs = { |
740 node.test.comparators[0].n: |
782 node.test.comparators[0].n: bodyValueStr, |
741 unparse(node.body[0].value).strip("'") |
|
742 } |
783 } |
743 else: |
784 else: |
744 keyValuePairs = { |
785 keyValuePairs = { |
745 node.test.comparators[0].value: |
786 node.test.comparators[0].value: bodyValueStr |
746 unparse(node.body[0].value).strip("'") |
|
747 } |
787 } |
748 while child: |
788 while child: |
749 if not ( |
789 if not ( |
750 isinstance(child.test, ast.Compare) and |
790 isinstance(child.test, ast.Compare) and |
751 isinstance(child.test.left, ast.Name) and |
791 isinstance(child.test.left, ast.Name) and |
785 ret = f"{keyValuePairs}.get({variable.id}, {elseValue})" |
825 ret = f"{keyValuePairs}.get({variable.id}, {elseValue})" |
786 else: |
826 else: |
787 ret = f"{keyValuePairs}.get({variable.id})" |
827 ret = f"{keyValuePairs}.get({variable.id})" |
788 |
828 |
789 self.__error(node.lineno - 1, node.col_offset, "Y116", ret) |
829 self.__error(node.lineno - 1, node.col_offset, "Y116", ret) |
|
830 |
|
831 def __check117(self, node): |
|
832 """ |
|
833 Private method to check for multiple with-statements with same scope. |
|
834 |
|
835 @param node reference to the AST node to be checked |
|
836 @type ast.With |
|
837 """ |
|
838 # with A() as a: |
|
839 # with B() as b: |
|
840 # print("hello") |
|
841 if ( |
|
842 len(node.body) == 1 and |
|
843 isinstance(node.body[0], ast.With) |
|
844 ): |
|
845 withItems = [] |
|
846 for withitem in node.items + node.body[0].items: |
|
847 withItems.append(f"{unparse(withitem)}") |
|
848 mergedWith = f"with {', '.join(withItems)}:" |
|
849 self.__error(node.lineno - 1, node.col_offset, "Y117", mergedWith) |
|
850 |
|
851 def __check118a(self, node): |
|
852 """ |
|
853 Private method to check for usages of "key in dict.keys()". |
|
854 |
|
855 @param node reference to the AST node to be checked |
|
856 @type ast.Compare |
|
857 """ |
|
858 # key in dict.keys() |
|
859 if ( |
|
860 len(node.ops) == 1 and |
|
861 isinstance(node.ops[0], ast.In) and |
|
862 len(node.comparators) == 1 |
|
863 ): |
|
864 callNode = node.comparators[0] |
|
865 if not isinstance(callNode, ast.Call): |
|
866 return |
|
867 |
|
868 attrNode = callNode.func |
|
869 if ( |
|
870 isinstance(callNode.func, ast.Attribute) and |
|
871 callNode.func.attr == "keys" and |
|
872 isinstance(callNode.func.ctx, ast.Load) |
|
873 ): |
|
874 keyStr = unparse(node.left) |
|
875 dictStr = unparse(attrNode.value) |
|
876 self.__error(node.lineno - 1, node.col_offset, "Y118", |
|
877 keyStr, dictStr) |
|
878 |
|
879 def __check118b(self, node): |
|
880 """ |
|
881 Private method to check for usages of "key in dict.keys()". |
|
882 |
|
883 @param node reference to the AST node to be checked |
|
884 @type ast.For |
|
885 """ |
|
886 # for key in dict.keys(): |
|
887 # # do something |
|
888 callNode = node.iter |
|
889 if not isinstance(callNode, ast.Call): |
|
890 return |
|
891 |
|
892 attrNode = callNode.func |
|
893 if ( |
|
894 isinstance(callNode.func, ast.Attribute) and |
|
895 callNode.func.attr == "keys" and |
|
896 isinstance(callNode.func.ctx, ast.Load) |
|
897 ): |
|
898 keyStr = unparse(node.target) |
|
899 dictStr = unparse(attrNode.value) |
|
900 self.__error(node.lineno - 1, node.col_offset, "Y118", |
|
901 keyStr, dictStr) |
|
902 |
|
903 def __check119(self, node): |
|
904 """ |
|
905 Public method to check for classes that should be "dataclasses". |
|
906 |
|
907 @param node reference to the AST node to be checked |
|
908 @type ast.ClassDef |
|
909 """ |
|
910 if ( |
|
911 len(node.decorator_list) == 0 and |
|
912 len(node.bases) == 0 |
|
913 ): |
|
914 dataclassFunctions = [ |
|
915 "__init__", |
|
916 "__eq__", |
|
917 "__hash__", |
|
918 "__repr__", |
|
919 "__str__", |
|
920 ] |
|
921 hasOnlyConstructorMethod = True |
|
922 for bodyElement in node.body: |
|
923 if ( |
|
924 isinstance(bodyElement, ast.FunctionDef) and |
|
925 bodyElement.name not in dataclassFunctions |
|
926 ): |
|
927 hasOnlyConstructorMethod = False |
|
928 break |
|
929 |
|
930 if ( |
|
931 hasOnlyConstructorMethod and |
|
932 sum(1 for el in node.body |
|
933 if isinstance(el, ast.FunctionDef)) > 0 |
|
934 ): |
|
935 self.__error(node.lineno - 1, node.col_offset, "Y119", |
|
936 node.name) |
790 |
937 |
791 # |
938 # |
792 # eflag: noqa = M891 |
939 # eflag: noqa = M891 |