142 @type bool |
187 @type bool |
143 @param args dictionary of arguments for the miscellaneous checks |
188 @param args dictionary of arguments for the miscellaneous checks |
144 @type dict |
189 @type dict |
145 """ |
190 """ |
146 self.__select = tuple(select) |
191 self.__select = tuple(select) |
147 self.__ignore = ('',) if select else tuple(ignore) |
192 self.__ignore = ("",) if select else tuple(ignore) |
148 self.__expected = expected[:] |
193 self.__expected = expected[:] |
149 self.__repeat = repeat |
194 self.__repeat = repeat |
150 self.__filename = filename |
195 self.__filename = filename |
151 self.__source = source[:] |
196 self.__source = source[:] |
152 self.__tree = copy.deepcopy(tree) |
197 self.__tree = copy.deepcopy(tree) |
153 self.__args = args |
198 self.__args = args |
154 |
199 |
155 self.__pep3101FormatRegex = re.compile( |
200 self.__pep3101FormatRegex = re.compile( |
156 r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%') |
201 r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%' |
157 |
202 ) |
|
203 |
158 import builtins |
204 import builtins |
159 self.__builtins = [b for b in dir(builtins) |
205 |
160 if b not in self.BuiltinsWhiteList] |
206 self.__builtins = [b for b in dir(builtins) if b not in self.BuiltinsWhiteList] |
161 |
207 |
162 self.__eradicator = Eradicator() |
208 self.__eradicator = Eradicator() |
163 |
209 |
164 # statistics counters |
210 # statistics counters |
165 self.counters = {} |
211 self.counters = {} |
166 |
212 |
167 # collection of detected errors |
213 # collection of detected errors |
168 self.errors = [] |
214 self.errors = [] |
169 |
215 |
170 checkersWithCodes = [ |
216 checkersWithCodes = [ |
171 (self.__checkCoding, ("M101", "M102")), |
217 (self.__checkCoding, ("M101", "M102")), |
172 (self.__checkCopyright, ("M111", "M112")), |
218 (self.__checkCopyright, ("M111", "M112")), |
173 (self.__checkBuiltins, ("M131", "M132")), |
219 (self.__checkBuiltins, ("M131", "M132")), |
174 (self.__checkComprehensions, ("M181", "M182", "M183", "M184", |
220 ( |
175 "M185", "M186", "M187", "M188", |
221 self.__checkComprehensions, |
176 "M189", |
222 ( |
177 "M191", "M192", "M193", |
223 "M181", |
178 "M195", "M196", "M197", "M198")), |
224 "M182", |
|
225 "M183", |
|
226 "M184", |
|
227 "M185", |
|
228 "M186", |
|
229 "M187", |
|
230 "M188", |
|
231 "M189", |
|
232 "M191", |
|
233 "M192", |
|
234 "M193", |
|
235 "M195", |
|
236 "M196", |
|
237 "M197", |
|
238 "M198", |
|
239 ), |
|
240 ), |
179 (self.__checkDictWithSortedKeys, ("M201",)), |
241 (self.__checkDictWithSortedKeys, ("M201",)), |
180 (self.__checkDateTime, ("M301", "M302", "M303", "M304", "M305", |
242 ( |
181 "M306", "M307", "M308", "M311", "M312", |
243 self.__checkDateTime, |
182 "M313", "M314", "M315", "M321")), |
244 ( |
183 (self.__checkSysVersion, ("M401", "M402", "M403", |
245 "M301", |
184 "M411", "M412", "M413", "M414", |
246 "M302", |
185 "M421", "M422", "M423")), |
247 "M303", |
186 (self.__checkBugBear, ("M501", "M502", "M503", "M504", "M505", |
248 "M304", |
187 "M506", "M507", "M508", "M509", |
249 "M305", |
188 "M511", "M512", "M513", |
250 "M306", |
189 "M521", "M522", "M523", "M524")), |
251 "M307", |
|
252 "M308", |
|
253 "M311", |
|
254 "M312", |
|
255 "M313", |
|
256 "M314", |
|
257 "M315", |
|
258 "M321", |
|
259 ), |
|
260 ), |
|
261 ( |
|
262 self.__checkSysVersion, |
|
263 ( |
|
264 "M401", |
|
265 "M402", |
|
266 "M403", |
|
267 "M411", |
|
268 "M412", |
|
269 "M413", |
|
270 "M414", |
|
271 "M421", |
|
272 "M422", |
|
273 "M423", |
|
274 ), |
|
275 ), |
|
276 ( |
|
277 self.__checkBugBear, |
|
278 ( |
|
279 "M501", |
|
280 "M502", |
|
281 "M503", |
|
282 "M504", |
|
283 "M505", |
|
284 "M506", |
|
285 "M507", |
|
286 "M508", |
|
287 "M509", |
|
288 "M511", |
|
289 "M512", |
|
290 "M513", |
|
291 "M521", |
|
292 "M522", |
|
293 "M523", |
|
294 "M524", |
|
295 ), |
|
296 ), |
190 (self.__checkPep3101, ("M601",)), |
297 (self.__checkPep3101, ("M601",)), |
191 (self.__checkFormatString, ("M611", "M612", "M613", |
298 ( |
192 "M621", "M622", "M623", "M624", "M625", |
299 self.__checkFormatString, |
193 "M631", "M632")), |
300 ( |
|
301 "M611", |
|
302 "M612", |
|
303 "M613", |
|
304 "M621", |
|
305 "M622", |
|
306 "M623", |
|
307 "M624", |
|
308 "M625", |
|
309 "M631", |
|
310 "M632", |
|
311 ), |
|
312 ), |
194 (self.__checkLogging, ("M651", "M652", "M653", "M654", "M655")), |
313 (self.__checkLogging, ("M651", "M652", "M653", "M654", "M655")), |
195 (self.__checkFuture, ("M701", "M702")), |
314 (self.__checkFuture, ("M701", "M702")), |
196 (self.__checkGettext, ("M711",)), |
315 (self.__checkGettext, ("M711",)), |
197 (self.__checkPrintStatements, ("M801",)), |
316 (self.__checkPrintStatements, ("M801",)), |
198 (self.__checkTuple, ("M811",)), |
317 (self.__checkTuple, ("M811",)), |
199 (self.__checkMutableDefault, ("M821", "M822")), |
318 (self.__checkMutableDefault, ("M821", "M822")), |
200 (self.__checkReturn, ("M831", "M832", "M833", "M834")), |
319 (self.__checkReturn, ("M831", "M832", "M833", "M834")), |
201 (self.__checkLineContinuation, ("M841",)), |
320 (self.__checkLineContinuation, ("M841",)), |
202 (self.__checkCommentedCode, ("M891",)), |
321 (self.__checkCommentedCode, ("M891",)), |
203 ] |
322 ] |
204 |
323 |
205 # the eradicate whitelist |
324 # the eradicate whitelist |
206 commentedCodeCheckerArgs = self.__args.get( |
325 commentedCodeCheckerArgs = self.__args.get( |
207 "CommentedCodeChecker", |
326 "CommentedCodeChecker", |
208 MiscellaneousCheckerDefaultArgs["CommentedCodeChecker"]) |
327 MiscellaneousCheckerDefaultArgs["CommentedCodeChecker"], |
|
328 ) |
209 commentedCodeCheckerWhitelist = commentedCodeCheckerArgs.get( |
329 commentedCodeCheckerWhitelist = commentedCodeCheckerArgs.get( |
210 "WhiteList", |
330 "WhiteList", |
211 MiscellaneousCheckerDefaultArgs[ |
331 MiscellaneousCheckerDefaultArgs["CommentedCodeChecker"]["WhiteList"], |
212 "CommentedCodeChecker"]["WhiteList"]) |
332 ) |
213 self.__eradicator.update_whitelist(commentedCodeCheckerWhitelist, |
333 self.__eradicator.update_whitelist( |
214 extend_default=False) |
334 commentedCodeCheckerWhitelist, extend_default=False |
215 |
335 ) |
|
336 |
216 self.__checkers = [] |
337 self.__checkers = [] |
217 for checker, codes in checkersWithCodes: |
338 for checker, codes in checkersWithCodes: |
218 if any(not (code and self.__ignoreCode(code)) |
339 if any(not (code and self.__ignoreCode(code)) for code in codes): |
219 for code in codes): |
|
220 self.__checkers.append(checker) |
340 self.__checkers.append(checker) |
221 |
341 |
222 def __ignoreCode(self, code): |
342 def __ignoreCode(self, code): |
223 """ |
343 """ |
224 Private method to check if the message code should be ignored. |
344 Private method to check if the message code should be ignored. |
225 |
345 |
226 @param code message code to check for |
346 @param code message code to check for |
227 @type str |
347 @type str |
228 @return flag indicating to ignore the given code |
348 @return flag indicating to ignore the given code |
229 @rtype bool |
349 @rtype bool |
230 """ |
350 """ |
231 return (code.startswith(self.__ignore) and |
351 return code.startswith(self.__ignore) and not code.startswith(self.__select) |
232 not code.startswith(self.__select)) |
352 |
233 |
|
234 def __error(self, lineNumber, offset, code, *args): |
353 def __error(self, lineNumber, offset, code, *args): |
235 """ |
354 """ |
236 Private method to record an issue. |
355 Private method to record an issue. |
237 |
356 |
238 @param lineNumber line number of the issue |
357 @param lineNumber line number of the issue |
239 @type int |
358 @type int |
240 @param offset position within line of the issue |
359 @param offset position within line of the issue |
241 @type int |
360 @type int |
242 @param code message code |
361 @param code message code |
590 cnt = itertools.count() |
709 cnt = itertools.count() |
591 implicit = False |
710 implicit = False |
592 explicit = False |
711 explicit = False |
593 try: |
712 try: |
594 for _literal, field, spec, conv in self.Formatter.parse(string): |
713 for _literal, field, spec, conv in self.Formatter.parse(string): |
595 if field is not None and (conv is None or conv in 'rsa'): |
714 if field is not None and (conv is None or conv in "rsa"): |
596 if not field: |
715 if not field: |
597 field = str(next(cnt)) |
716 field = str(next(cnt)) |
598 implicit = True |
717 implicit = True |
599 else: |
718 else: |
600 explicit = True |
719 explicit = True |
601 fields.add(field) |
720 fields.add(field) |
602 fields.update(parsedSpec[1] |
721 fields.update( |
603 for parsedSpec in self.Formatter.parse(spec) |
722 parsedSpec[1] |
604 if parsedSpec[1] is not None) |
723 for parsedSpec in self.Formatter.parse(spec) |
|
724 if parsedSpec[1] is not None |
|
725 ) |
605 except ValueError: |
726 except ValueError: |
606 return set(), False, False |
727 return set(), False, False |
607 else: |
728 else: |
608 return fields, implicit, explicit |
729 return fields, implicit, explicit |
609 |
730 |
610 def __checkBuiltins(self): |
731 def __checkBuiltins(self): |
611 """ |
732 """ |
612 Private method to check, if built-ins are shadowed. |
733 Private method to check, if built-ins are shadowed. |
613 """ |
734 """ |
614 functionDefs = [ast.FunctionDef] |
735 functionDefs = [ast.FunctionDef] |
615 with contextlib.suppress(AttributeError): |
736 with contextlib.suppress(AttributeError): |
616 functionDefs.append(ast.AsyncFunctionDef) |
737 functionDefs.append(ast.AsyncFunctionDef) |
617 |
738 |
618 ignoreBuiltinAssignments = self.__args.get( |
739 ignoreBuiltinAssignments = self.__args.get( |
619 "BuiltinsChecker", |
740 "BuiltinsChecker", MiscellaneousCheckerDefaultArgs["BuiltinsChecker"] |
620 MiscellaneousCheckerDefaultArgs["BuiltinsChecker"]) |
741 ) |
621 |
742 |
622 for node in ast.walk(self.__tree): |
743 for node in ast.walk(self.__tree): |
623 if isinstance(node, ast.Assign): |
744 if isinstance(node, ast.Assign): |
624 # assign statement |
745 # assign statement |
625 for element in node.targets: |
746 for element in node.targets: |
626 if ( |
747 if isinstance(element, ast.Name) and element.id in self.__builtins: |
627 isinstance(element, ast.Name) and |
|
628 element.id in self.__builtins |
|
629 ): |
|
630 value = node.value |
748 value = node.value |
631 if ( |
749 if ( |
632 isinstance(value, ast.Name) and |
750 isinstance(value, ast.Name) |
633 element.id in ignoreBuiltinAssignments and |
751 and element.id in ignoreBuiltinAssignments |
634 value.id in ignoreBuiltinAssignments[element.id] |
752 and value.id in ignoreBuiltinAssignments[element.id] |
635 ): |
753 ): |
636 # ignore compatibility assignments |
754 # ignore compatibility assignments |
637 continue |
755 continue |
638 self.__error(element.lineno - 1, element.col_offset, |
756 self.__error( |
639 "M131", element.id) |
757 element.lineno - 1, element.col_offset, "M131", element.id |
|
758 ) |
640 elif isinstance(element, (ast.Tuple, ast.List)): |
759 elif isinstance(element, (ast.Tuple, ast.List)): |
641 for tupleElement in element.elts: |
760 for tupleElement in element.elts: |
642 if ( |
761 if ( |
643 isinstance(tupleElement, ast.Name) and |
762 isinstance(tupleElement, ast.Name) |
644 tupleElement.id in self.__builtins |
763 and tupleElement.id in self.__builtins |
645 ): |
764 ): |
646 self.__error(tupleElement.lineno - 1, |
765 self.__error( |
647 tupleElement.col_offset, |
766 tupleElement.lineno - 1, |
648 "M131", tupleElement.id) |
767 tupleElement.col_offset, |
|
768 "M131", |
|
769 tupleElement.id, |
|
770 ) |
649 elif isinstance(node, ast.For): |
771 elif isinstance(node, ast.For): |
650 # for loop |
772 # for loop |
651 target = node.target |
773 target = node.target |
652 if ( |
774 if isinstance(target, ast.Name) and target.id in self.__builtins: |
653 isinstance(target, ast.Name) and |
775 self.__error( |
654 target.id in self.__builtins |
776 target.lineno - 1, target.col_offset, "M131", target.id |
655 ): |
777 ) |
656 self.__error(target.lineno - 1, target.col_offset, |
|
657 "M131", target.id) |
|
658 elif isinstance(target, (ast.Tuple, ast.List)): |
778 elif isinstance(target, (ast.Tuple, ast.List)): |
659 for element in target.elts: |
779 for element in target.elts: |
660 if ( |
780 if ( |
661 isinstance(element, ast.Name) and |
781 isinstance(element, ast.Name) |
662 element.id in self.__builtins |
782 and element.id in self.__builtins |
663 ): |
783 ): |
664 self.__error(element.lineno - 1, |
784 self.__error( |
665 element.col_offset, |
785 element.lineno - 1, |
666 "M131", element.id) |
786 element.col_offset, |
667 elif any(isinstance(node, functionDef) |
787 "M131", |
668 for functionDef in functionDefs): |
788 element.id, |
|
789 ) |
|
790 elif any(isinstance(node, functionDef) for functionDef in functionDefs): |
669 # (asynchronous) function definition |
791 # (asynchronous) function definition |
670 for arg in node.args.args: |
792 for arg in node.args.args: |
671 if ( |
793 if isinstance(arg, ast.arg) and arg.arg in self.__builtins: |
672 isinstance(arg, ast.arg) and |
794 self.__error(arg.lineno - 1, arg.col_offset, "M132", arg.arg) |
673 arg.arg in self.__builtins |
795 |
674 ): |
|
675 self.__error(arg.lineno - 1, arg.col_offset, |
|
676 "M132", arg.arg) |
|
677 |
|
678 def __checkComprehensions(self): |
796 def __checkComprehensions(self): |
679 """ |
797 """ |
680 Private method to check some comprehension related things. |
798 Private method to check some comprehension related things. |
681 """ |
799 """ |
682 for node in ast.walk(self.__tree): |
800 for node in ast.walk(self.__tree): |
683 if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): |
801 if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): |
684 nArgs = len(node.args) |
802 nArgs = len(node.args) |
685 nKwArgs = len(node.keywords) |
803 nKwArgs = len(node.keywords) |
686 |
804 |
687 if ( |
805 if ( |
688 nArgs == 1 and |
806 nArgs == 1 |
689 isinstance(node.args[0], ast.GeneratorExp) and |
807 and isinstance(node.args[0], ast.GeneratorExp) |
690 node.func.id in ('list', 'set') |
808 and node.func.id in ("list", "set") |
691 ): |
809 ): |
692 errorCode = { |
810 errorCode = { |
693 "list": "M181", |
811 "list": "M181", |
694 "set": "M182", |
812 "set": "M182", |
695 }[node.func.id] |
813 }[node.func.id] |
696 self.__error(node.lineno - 1, node.col_offset, errorCode) |
814 self.__error(node.lineno - 1, node.col_offset, errorCode) |
697 |
815 |
698 elif ( |
816 elif ( |
699 nArgs == 1 and |
817 nArgs == 1 |
700 isinstance(node.args[0], |
818 and isinstance(node.args[0], (ast.GeneratorExp, ast.ListComp)) |
701 (ast.GeneratorExp, ast.ListComp)) and |
819 and isinstance(node.args[0].elt, ast.Tuple) |
702 isinstance(node.args[0].elt, ast.Tuple) and |
820 and len(node.args[0].elt.elts) == 2 |
703 len(node.args[0].elt.elts) == 2 and |
821 and node.func.id == "dict" |
704 node.func.id == "dict" |
|
705 ): |
822 ): |
706 if isinstance(node.args[0], ast.GeneratorExp): |
823 if isinstance(node.args[0], ast.GeneratorExp): |
707 errorCode = "M183" |
824 errorCode = "M183" |
708 else: |
825 else: |
709 errorCode = "M185" |
826 errorCode = "M185" |
710 self.__error(node.lineno - 1, node.col_offset, errorCode) |
827 self.__error(node.lineno - 1, node.col_offset, errorCode) |
711 |
828 |
712 elif ( |
829 elif ( |
713 nArgs == 1 and |
830 nArgs == 1 |
714 isinstance(node.args[0], ast.ListComp) and |
831 and isinstance(node.args[0], ast.ListComp) |
715 node.func.id in ('list', 'set') |
832 and node.func.id in ("list", "set") |
716 ): |
833 ): |
717 errorCode = { |
834 errorCode = { |
718 'list': 'M195', |
835 "list": "M195", |
719 'set': 'M184', |
836 "set": "M184", |
720 }[node.func.id] |
837 }[node.func.id] |
721 self.__error(node.lineno - 1, node.col_offset, errorCode) |
838 self.__error(node.lineno - 1, node.col_offset, errorCode) |
722 |
839 |
723 elif nArgs == 1 and ( |
840 elif nArgs == 1 and ( |
724 isinstance(node.args[0], ast.Tuple) and |
841 isinstance(node.args[0], ast.Tuple) |
725 node.func.id == "tuple" or |
842 and node.func.id == "tuple" |
726 isinstance(node.args[0], ast.List) and |
843 or isinstance(node.args[0], ast.List) |
727 node.func.id == "list" |
844 and node.func.id == "list" |
728 ): |
845 ): |
729 errorCode = { |
846 errorCode = { |
730 'tuple': 'M197', |
847 "tuple": "M197", |
731 'list': 'M198', |
848 "list": "M198", |
732 }[node.func.id] |
849 }[node.func.id] |
733 self.__error(node.lineno - 1, node.col_offset, errorCode, |
850 self.__error( |
734 type(node.args[0]).__name__.lower(), |
851 node.lineno - 1, |
735 node.func.id) |
852 node.col_offset, |
736 |
853 errorCode, |
|
854 type(node.args[0]).__name__.lower(), |
|
855 node.func.id, |
|
856 ) |
|
857 |
737 elif ( |
858 elif ( |
738 nArgs == 1 and |
859 nArgs == 1 |
739 isinstance(node.args[0], (ast.Tuple, ast.List)) and |
860 and isinstance(node.args[0], (ast.Tuple, ast.List)) |
740 node.func.id in ("tuple", "list", "set", "dict") |
861 and node.func.id in ("tuple", "list", "set", "dict") |
741 ): |
862 ): |
742 errorCode = { |
863 errorCode = { |
743 "tuple": "M192", |
864 "tuple": "M192", |
744 "list": "M193", |
865 "list": "M193", |
745 "set": "M191", |
866 "set": "M191", |
746 "dict": "M191", |
867 "dict": "M191", |
747 }[node.func.id] |
868 }[node.func.id] |
748 self.__error(node.lineno - 1, node.col_offset, errorCode, |
869 self.__error( |
749 type(node.args[0]).__name__.lower(), |
870 node.lineno - 1, |
750 node.func.id) |
871 node.col_offset, |
751 |
872 errorCode, |
|
873 type(node.args[0]).__name__.lower(), |
|
874 node.func.id, |
|
875 ) |
|
876 |
752 elif ( |
877 elif ( |
753 nArgs == 0 and |
878 nArgs == 0 |
754 not any(isinstance(a, ast.Starred) for a in node.args) and |
879 and not any(isinstance(a, ast.Starred) for a in node.args) |
755 not any(k.arg is None for k in node.keywords) and |
880 and not any(k.arg is None for k in node.keywords) |
756 node.func.id == "dict" |
881 and node.func.id == "dict" |
757 ) or ( |
882 ) or ( |
758 nArgs == 0 and |
883 nArgs == 0 and nKwArgs == 0 and node.func.id in ("tuple", "list") |
759 nKwArgs == 0 and |
|
760 node.func.id in ("tuple", "list") |
|
761 ): |
884 ): |
762 self.__error(node.lineno - 1, node.col_offset, "M186", |
885 self.__error(node.lineno - 1, node.col_offset, "M186", node.func.id) |
763 node.func.id) |
886 |
764 |
|
765 elif ( |
887 elif ( |
766 node.func.id in {"list", "reversed"} and |
888 node.func.id in {"list", "reversed"} |
767 nArgs > 0 and |
889 and nArgs > 0 |
768 isinstance(node.args[0], ast.Call) and |
890 and isinstance(node.args[0], ast.Call) |
769 isinstance(node.args[0].func, ast.Name) and |
891 and isinstance(node.args[0].func, ast.Name) |
770 node.args[0].func.id == "sorted" |
892 and node.args[0].func.id == "sorted" |
771 ): |
893 ): |
772 if node.func.id == "reversed": |
894 if node.func.id == "reversed": |
773 reverseFlagValue = False |
895 reverseFlagValue = False |
774 for kw in node.args[0].keywords: |
896 for kw in node.args[0].keywords: |
775 if kw.arg != "reverse": |
897 if kw.arg != "reverse": |
1258 """ |
1399 """ |
1259 try: |
1400 try: |
1260 return node.func.attr == "format" |
1401 return node.func.attr == "format" |
1261 except AttributeError: |
1402 except AttributeError: |
1262 return False |
1403 return False |
1263 |
1404 |
1264 def visit_Call(self, node): |
1405 def visit_Call(self, node): |
1265 """ |
1406 """ |
1266 Public method to handle a function call. |
1407 Public method to handle a function call. |
1267 |
1408 |
1268 Every logging statement and string format is expected to be a function |
1409 Every logging statement and string format is expected to be a function |
1269 call. |
1410 call. |
1270 |
1411 |
1271 @param node reference to the node to be processed |
1412 @param node reference to the node to be processed |
1272 @type ast.Call |
1413 @type ast.Call |
1273 """ |
1414 """ |
1274 # we are in a logging statement |
1415 # we are in a logging statement |
1275 if ( |
1416 if ( |
1276 self.__withinLoggingStatement() and |
1417 self.__withinLoggingStatement() |
1277 self.__withinLoggingArgument() and |
1418 and self.__withinLoggingArgument() |
1278 self.__isFormatCall(node) |
1419 and self.__isFormatCall(node) |
1279 ): |
1420 ): |
1280 self.violations.append((node, "M651")) |
1421 self.violations.append((node, "M651")) |
1281 super().generic_visit(node) |
1422 super().generic_visit(node) |
1282 return |
1423 return |
1283 |
1424 |
1284 loggingLevel = self.__detectLoggingLevel(node) |
1425 loggingLevel = self.__detectLoggingLevel(node) |
1285 |
1426 |
1286 if loggingLevel and self.__currentLoggingLevel is None: |
1427 if loggingLevel and self.__currentLoggingLevel is None: |
1287 self.__currentLoggingLevel = loggingLevel |
1428 self.__currentLoggingLevel = loggingLevel |
1288 |
1429 |
1289 # we are in some other statement |
1430 # we are in some other statement |
1290 if loggingLevel is None: |
1431 if loggingLevel is None: |
1291 super().generic_visit(node) |
1432 super().generic_visit(node) |
1292 return |
1433 return |
1293 |
1434 |
1294 # we are entering a new logging statement |
1435 # we are entering a new logging statement |
1295 self.__currentLoggingCall = node |
1436 self.__currentLoggingCall = node |
1296 |
1437 |
1297 if loggingLevel == "warn": |
1438 if loggingLevel == "warn": |
1298 self.violations.append((node, "M655")) |
1439 self.violations.append((node, "M655")) |
1299 |
1440 |
1300 for index, child in enumerate(ast.iter_child_nodes(node)): |
1441 for index, child in enumerate(ast.iter_child_nodes(node)): |
1301 if index == 1: |
1442 if index == 1: |
1302 self.__currentLoggingArgument = child |
1443 self.__currentLoggingArgument = child |
1303 if ( |
1444 if index > 1 and isinstance(child, ast.keyword) and child.arg == "extra": |
1304 index > 1 and |
|
1305 isinstance(child, ast.keyword) and |
|
1306 child.arg == "extra" |
|
1307 ): |
|
1308 self.__currentExtraKeyword = child |
1445 self.__currentExtraKeyword = child |
1309 |
1446 |
1310 super().visit(child) |
1447 super().visit(child) |
1311 |
1448 |
1312 self.__currentLoggingArgument = None |
1449 self.__currentLoggingArgument = None |
1313 self.__currentExtraKeyword = None |
1450 self.__currentExtraKeyword = None |
1314 |
1451 |
1315 self.__currentLoggingCall = None |
1452 self.__currentLoggingCall = None |
1316 self.__currentLoggingLevel = None |
1453 self.__currentLoggingLevel = None |
1317 |
1454 |
1318 def visit_BinOp(self, node): |
1455 def visit_BinOp(self, node): |
1319 """ |
1456 """ |
1320 Public method to handle binary operations while processing the first |
1457 Public method to handle binary operations while processing the first |
1321 logging argument. |
1458 logging argument. |
1322 |
1459 |
1323 @param node reference to the node to be processed |
1460 @param node reference to the node to be processed |
1324 @type ast.BinOp |
1461 @type ast.BinOp |
1325 """ |
1462 """ |
1326 if self.__withinLoggingStatement() and self.__withinLoggingArgument(): |
1463 if self.__withinLoggingStatement() and self.__withinLoggingArgument(): |
1327 # handle percent format |
1464 # handle percent format |
1328 if isinstance(node.op, ast.Mod): |
1465 if isinstance(node.op, ast.Mod): |
1329 self.violations.append((node, "M652")) |
1466 self.violations.append((node, "M652")) |
1330 |
1467 |
1331 # handle string concat |
1468 # handle string concat |
1332 if isinstance(node.op, ast.Add): |
1469 if isinstance(node.op, ast.Add): |
1333 self.violations.append((node, "M653")) |
1470 self.violations.append((node, "M653")) |
1334 |
1471 |
1335 super().generic_visit(node) |
1472 super().generic_visit(node) |
1336 |
1473 |
1337 def visit_JoinedStr(self, node): |
1474 def visit_JoinedStr(self, node): |
1338 """ |
1475 """ |
1339 Public method to handle f-string arguments. |
1476 Public method to handle f-string arguments. |
1340 |
1477 |
1341 @param node reference to the node to be processed |
1478 @param node reference to the node to be processed |
1342 @type ast.JoinedStr |
1479 @type ast.JoinedStr |
1343 """ |
1480 """ |
1344 if ( |
1481 if ( |
1345 self.__withinLoggingStatement() and |
1482 self.__withinLoggingStatement() |
1346 any(isinstance(i, ast.FormattedValue) for i in node.values) and |
1483 and any(isinstance(i, ast.FormattedValue) for i in node.values) |
1347 self.__withinLoggingArgument() |
1484 and self.__withinLoggingArgument() |
1348 ): |
1485 ): |
1349 self.violations.append((node, "M654")) |
1486 self.violations.append((node, "M654")) |
1350 |
1487 |
1351 super().generic_visit(node) |
1488 super().generic_visit(node) |
1352 |
1489 |
1353 |
1490 |
1354 class BugBearVisitor(ast.NodeVisitor): |
1491 class BugBearVisitor(ast.NodeVisitor): |
1355 """ |
1492 """ |
1356 Class implementing a node visitor to check for various topics. |
1493 Class implementing a node visitor to check for various topics. |
1357 """ |
1494 """ |
|
1495 |
1358 # |
1496 # |
1359 # This class was implemented along the BugBear flake8 extension (v 19.3.0). |
1497 # This class was implemented along the BugBear flake8 extension (v 19.3.0). |
1360 # Original: Copyright (c) 2016 Łukasz Langa |
1498 # Original: Copyright (c) 2016 Łukasz Langa |
1361 # |
1499 # |
1362 |
1500 |
1363 NodeWindowSize = 4 |
1501 NodeWindowSize = 4 |
1364 |
1502 |
1365 def __init__(self): |
1503 def __init__(self): |
1366 """ |
1504 """ |
1367 Constructor |
1505 Constructor |
1368 """ |
1506 """ |
1369 super().__init__() |
1507 super().__init__() |
1370 |
1508 |
1371 self.__nodeStack = [] |
1509 self.__nodeStack = [] |
1372 self.__nodeWindow = [] |
1510 self.__nodeWindow = [] |
1373 self.violations = [] |
1511 self.violations = [] |
1374 |
1512 |
1375 def visit(self, node): |
1513 def visit(self, node): |
1376 """ |
1514 """ |
1377 Public method to traverse a given AST node. |
1515 Public method to traverse a given AST node. |
1378 |
1516 |
1379 @param node AST node to be traversed |
1517 @param node AST node to be traversed |
1380 @type ast.Node |
1518 @type ast.Node |
1381 """ |
1519 """ |
1382 self.__nodeStack.append(node) |
1520 self.__nodeStack.append(node) |
1383 self.__nodeWindow.append(node) |
1521 self.__nodeWindow.append(node) |
1384 self.__nodeWindow = self.__nodeWindow[-BugBearVisitor.NodeWindowSize:] |
1522 self.__nodeWindow = self.__nodeWindow[-BugBearVisitor.NodeWindowSize :] |
1385 |
1523 |
1386 super().visit(node) |
1524 super().visit(node) |
1387 |
1525 |
1388 self.__nodeStack.pop() |
1526 self.__nodeStack.pop() |
1389 |
1527 |
1390 def visit_UAdd(self, node): |
1528 def visit_UAdd(self, node): |
1391 """ |
1529 """ |
1392 Public method to handle unary additions. |
1530 Public method to handle unary additions. |
1393 |
1531 |
1394 @param node reference to the node to be processed |
1532 @param node reference to the node to be processed |
1395 @type ast.UAdd |
1533 @type ast.UAdd |
1396 """ |
1534 """ |
1397 trailingNodes = list(map(type, self.__nodeWindow[-4:])) |
1535 trailingNodes = list(map(type, self.__nodeWindow[-4:])) |
1398 if trailingNodes == [ast.UnaryOp, ast.UAdd, ast.UnaryOp, ast.UAdd]: |
1536 if trailingNodes == [ast.UnaryOp, ast.UAdd, ast.UnaryOp, ast.UAdd]: |
1399 originator = self.__nodeWindow[-4] |
1537 originator = self.__nodeWindow[-4] |
1400 self.violations.append((originator, "M501")) |
1538 self.violations.append((originator, "M501")) |
1401 |
1539 |
1402 self.generic_visit(node) |
1540 self.generic_visit(node) |
1403 |
1541 |
1404 def visit_Call(self, node): |
1542 def visit_Call(self, node): |
1405 """ |
1543 """ |
1406 Public method to handle a function call. |
1544 Public method to handle a function call. |
1407 |
1545 |
1408 @param node reference to the node to be processed |
1546 @param node reference to the node to be processed |
1409 @type ast.Call |
1547 @type ast.Call |
1410 """ |
1548 """ |
1411 validPaths = ("six", "future.utils", "builtins") |
1549 validPaths = ("six", "future.utils", "builtins") |
1412 methodsDict = { |
1550 methodsDict = { |
1413 "M521": ("iterkeys", "itervalues", "iteritems", "iterlists"), |
1551 "M521": ("iterkeys", "itervalues", "iteritems", "iterlists"), |
1414 "M522": ("viewkeys", "viewvalues", "viewitems", "viewlists"), |
1552 "M522": ("viewkeys", "viewvalues", "viewitems", "viewlists"), |
1415 "M523": ("next",), |
1553 "M523": ("next",), |
1416 } |
1554 } |
1417 |
1555 |
1418 if isinstance(node.func, ast.Attribute): |
1556 if isinstance(node.func, ast.Attribute): |
1419 for code, methods in methodsDict.items(): |
1557 for code, methods in methodsDict.items(): |
1420 if node.func.attr in methods: |
1558 if node.func.attr in methods: |
1421 callPath = ".".join(composeCallPath(node.func.value)) |
1559 callPath = ".".join(composeCallPath(node.func.value)) |
1422 if callPath not in validPaths: |
1560 if callPath not in validPaths: |
1428 with contextlib.suppress(AttributeError, IndexError): |
1566 with contextlib.suppress(AttributeError, IndexError): |
1429 # bad super() call |
1567 # bad super() call |
1430 if isinstance(node.func, ast.Name) and node.func.id == "super": |
1568 if isinstance(node.func, ast.Name) and node.func.id == "super": |
1431 args = node.args |
1569 args = node.args |
1432 if ( |
1570 if ( |
1433 len(args) == 2 and |
1571 len(args) == 2 |
1434 isinstance(args[0], ast.Attribute) and |
1572 and isinstance(args[0], ast.Attribute) |
1435 isinstance(args[0].value, ast.Name) and |
1573 and isinstance(args[0].value, ast.Name) |
1436 args[0].value.id == 'self' and |
1574 and args[0].value.id == "self" |
1437 args[0].attr == '__class__' |
1575 and args[0].attr == "__class__" |
1438 ): |
1576 ): |
1439 self.violations.append((node, "M509")) |
1577 self.violations.append((node, "M509")) |
1440 |
1578 |
1441 # bad getattr and setattr |
1579 # bad getattr and setattr |
1442 if ( |
1580 if ( |
1443 node.func.id in ("getattr", "hasattr") and |
1581 node.func.id in ("getattr", "hasattr") |
1444 node.args[1].s == "__call__" |
1582 and node.args[1].s == "__call__" |
1445 ): |
1583 ): |
1446 self.violations.append((node, "M511")) |
1584 self.violations.append((node, "M511")) |
1447 if ( |
1585 if ( |
1448 node.func.id == "getattr" and |
1586 node.func.id == "getattr" |
1449 len(node.args) == 2 and |
1587 and len(node.args) == 2 |
1450 AstUtilities.isString(node.args[1]) |
1588 and AstUtilities.isString(node.args[1]) |
1451 ): |
1589 ): |
1452 self.violations.append((node, "M512")) |
1590 self.violations.append((node, "M512")) |
1453 elif ( |
1591 elif ( |
1454 node.func.id == "setattr" and |
1592 node.func.id == "setattr" |
1455 len(node.args) == 3 and |
1593 and len(node.args) == 3 |
1456 AstUtilities.isString(node.args[1]) |
1594 and AstUtilities.isString(node.args[1]) |
1457 ): |
1595 ): |
1458 self.violations.append((node, "M513")) |
1596 self.violations.append((node, "M513")) |
1459 |
1597 |
1460 self.generic_visit(node) |
1598 self.generic_visit(node) |
1461 |
1599 |
1462 def visit_Attribute(self, node): |
1600 def visit_Attribute(self, node): |
1463 """ |
1601 """ |
1464 Public method to handle attributes. |
1602 Public method to handle attributes. |
1465 |
1603 |
1466 @param node reference to the node to be processed |
1604 @param node reference to the node to be processed |
1467 @type ast.Attribute |
1605 @type ast.Attribute |
1468 """ |
1606 """ |
1469 callPath = list(composeCallPath(node)) |
1607 callPath = list(composeCallPath(node)) |
1470 |
1608 |
1471 if '.'.join(callPath) == 'sys.maxint': |
1609 if ".".join(callPath) == "sys.maxint": |
1472 self.violations.append((node, "M504")) |
1610 self.violations.append((node, "M504")) |
1473 |
1611 |
1474 elif ( |
1612 elif len(callPath) == 2 and callPath[1] == "message": |
1475 len(callPath) == 2 and |
|
1476 callPath[1] == 'message' |
|
1477 ): |
|
1478 name = callPath[0] |
1613 name = callPath[0] |
1479 for elem in reversed(self.__nodeStack[:-1]): |
1614 for elem in reversed(self.__nodeStack[:-1]): |
1480 if isinstance(elem, ast.ExceptHandler) and elem.name == name: |
1615 if isinstance(elem, ast.ExceptHandler) and elem.name == name: |
1481 self.violations.append((node, "M505")) |
1616 self.violations.append((node, "M505")) |
1482 break |
1617 break |
1483 |
1618 |
1484 def visit_Assign(self, node): |
1619 def visit_Assign(self, node): |
1485 """ |
1620 """ |
1486 Public method to handle assignments. |
1621 Public method to handle assignments. |
1487 |
1622 |
1488 @param node reference to the node to be processed |
1623 @param node reference to the node to be processed |
1489 @type ast.Assign |
1624 @type ast.Assign |
1490 """ |
1625 """ |
1491 if isinstance(self.__nodeStack[-2], ast.ClassDef): |
1626 if isinstance(self.__nodeStack[-2], ast.ClassDef): |
1492 # By using 'hasattr' below we're ignoring starred arguments, slices |
1627 # By using 'hasattr' below we're ignoring starred arguments, slices |
1493 # and tuples for simplicity. |
1628 # and tuples for simplicity. |
1494 assignTargets = {t.id for t in node.targets if hasattr(t, 'id')} |
1629 assignTargets = {t.id for t in node.targets if hasattr(t, "id")} |
1495 if '__metaclass__' in assignTargets: |
1630 if "__metaclass__" in assignTargets: |
1496 self.violations.append((node, "M524")) |
1631 self.violations.append((node, "M524")) |
1497 |
1632 |
1498 elif len(node.targets) == 1: |
1633 elif len(node.targets) == 1: |
1499 target = node.targets[0] |
1634 target = node.targets[0] |
1500 if ( |
1635 if ( |
1501 isinstance(target, ast.Attribute) and |
1636 isinstance(target, ast.Attribute) |
1502 isinstance(target.value, ast.Name) and |
1637 and isinstance(target.value, ast.Name) |
1503 (target.value.id, target.attr) == ('os', 'environ') |
1638 and (target.value.id, target.attr) == ("os", "environ") |
1504 ): |
1639 ): |
1505 self.violations.append((node, "M506")) |
1640 self.violations.append((node, "M506")) |
1506 |
1641 |
1507 self.generic_visit(node) |
1642 self.generic_visit(node) |
1508 |
1643 |
1509 def visit_For(self, node): |
1644 def visit_For(self, node): |
1510 """ |
1645 """ |
1511 Public method to handle 'for' statements. |
1646 Public method to handle 'for' statements. |
1512 |
1647 |
1513 @param node reference to the node to be processed |
1648 @param node reference to the node to be processed |
1514 @type ast.For |
1649 @type ast.For |
1515 """ |
1650 """ |
1516 self.__checkForM507(node) |
1651 self.__checkForM507(node) |
1517 |
1652 |
1518 self.generic_visit(node) |
1653 self.generic_visit(node) |
1519 |
1654 |
1520 def visit_AsyncFor(self, node): |
1655 def visit_AsyncFor(self, node): |
1521 """ |
1656 """ |
1522 Public method to handle 'for' statements. |
1657 Public method to handle 'for' statements. |
1523 |
1658 |
1524 @param node reference to the node to be processed |
1659 @param node reference to the node to be processed |
1525 @type ast.AsyncFor |
1660 @type ast.AsyncFor |
1526 """ |
1661 """ |
1527 self.__checkForM507(node) |
1662 self.__checkForM507(node) |
1528 |
1663 |
1529 self.generic_visit(node) |
1664 self.generic_visit(node) |
1530 |
1665 |
1531 def visit_Assert(self, node): |
1666 def visit_Assert(self, node): |
1532 """ |
1667 """ |
1533 Public method to handle 'assert' statements. |
1668 Public method to handle 'assert' statements. |
1534 |
1669 |
1535 @param node reference to the node to be processed |
1670 @param node reference to the node to be processed |
1536 @type ast.Assert |
1671 @type ast.Assert |
1537 """ |
1672 """ |
1538 if ( |
1673 if ( |
1539 AstUtilities.isNameConstant(node.test) and |
1674 AstUtilities.isNameConstant(node.test) |
1540 AstUtilities.getValue(node.test) is False |
1675 and AstUtilities.getValue(node.test) is False |
1541 ): |
1676 ): |
1542 self.violations.append((node, "M503")) |
1677 self.violations.append((node, "M503")) |
1543 |
1678 |
1544 self.generic_visit(node) |
1679 self.generic_visit(node) |
1545 |
1680 |
1546 def visit_JoinedStr(self, node): |
1681 def visit_JoinedStr(self, node): |
1547 """ |
1682 """ |
1548 Public method to handle f-string arguments. |
1683 Public method to handle f-string arguments. |
1549 |
1684 |
1550 @param node reference to the node to be processed |
1685 @param node reference to the node to be processed |
1551 @type ast.JoinedStr |
1686 @type ast.JoinedStr |
1552 """ |
1687 """ |
1553 for value in node.values: |
1688 for value in node.values: |
1554 if isinstance(value, ast.FormattedValue): |
1689 if isinstance(value, ast.FormattedValue): |
1555 return |
1690 return |
1556 |
1691 |
1557 self.violations.append((node, "M508")) |
1692 self.violations.append((node, "M508")) |
1558 |
1693 |
1559 def __checkForM502(self, node): |
1694 def __checkForM502(self, node): |
1560 """ |
1695 """ |
1561 Private method to check the use of *strip(). |
1696 Private method to check the use of *strip(). |
1562 |
1697 |
1563 @param node reference to the node to be processed |
1698 @param node reference to the node to be processed |
1564 @type ast.Call |
1699 @type ast.Call |
1565 """ |
1700 """ |
1566 if node.func.attr not in ("lstrip", "rstrip", "strip"): |
1701 if node.func.attr not in ("lstrip", "rstrip", "strip"): |
1567 return # method name doesn't match |
1702 return # method name doesn't match |
1568 |
1703 |
1569 if len(node.args) != 1 or not AstUtilities.isString(node.args[0]): |
1704 if len(node.args) != 1 or not AstUtilities.isString(node.args[0]): |
1570 return # used arguments don't match the builtin strip |
1705 return # used arguments don't match the builtin strip |
1571 |
1706 |
1572 s = AstUtilities.getValue(node.args[0]) |
1707 s = AstUtilities.getValue(node.args[0]) |
1573 if len(s) == 1: |
1708 if len(s) == 1: |
1574 return # stripping just one character |
1709 return # stripping just one character |
1575 |
1710 |
1576 if len(s) == len(set(s)): |
1711 if len(s) == len(set(s)): |
1577 return # no characters appear more than once |
1712 return # no characters appear more than once |
1578 |
1713 |
1579 self.violations.append((node, "M502")) |
1714 self.violations.append((node, "M502")) |
1580 |
1715 |
1581 def __checkForM507(self, node): |
1716 def __checkForM507(self, node): |
1582 """ |
1717 """ |
1583 Private method to check for unused loop variables. |
1718 Private method to check for unused loop variables. |
1584 |
1719 |
1585 @param node reference to the node to be processed |
1720 @param node reference to the node to be processed |
1586 @type ast.For |
1721 @type ast.For |
1587 """ |
1722 """ |
1588 targets = NameFinder() |
1723 targets = NameFinder() |
1589 targets.visit(node.target) |
1724 targets.visit(node.target) |
1590 ctrlNames = set(filter(lambda s: not s.startswith('_'), |
1725 ctrlNames = set(filter(lambda s: not s.startswith("_"), targets.getNames())) |
1591 targets.getNames())) |
|
1592 body = NameFinder() |
1726 body = NameFinder() |
1593 for expr in node.body: |
1727 for expr in node.body: |
1594 body.visit(expr) |
1728 body.visit(expr) |
1595 usedNames = set(body.getNames()) |
1729 usedNames = set(body.getNames()) |
1596 for name in sorted(ctrlNames - usedNames): |
1730 for name in sorted(ctrlNames - usedNames): |
1600 |
1734 |
1601 class NameFinder(ast.NodeVisitor): |
1735 class NameFinder(ast.NodeVisitor): |
1602 """ |
1736 """ |
1603 Class to extract a name out of a tree of nodes. |
1737 Class to extract a name out of a tree of nodes. |
1604 """ |
1738 """ |
|
1739 |
1605 def __init__(self): |
1740 def __init__(self): |
1606 """ |
1741 """ |
1607 Constructor |
1742 Constructor |
1608 """ |
1743 """ |
1609 super().__init__() |
1744 super().__init__() |
1610 |
1745 |
1611 self.__names = {} |
1746 self.__names = {} |
1612 |
1747 |
1613 def visit_Name(self, node): |
1748 def visit_Name(self, node): |
1614 """ |
1749 """ |
1615 Public method to handle 'Name' nodes. |
1750 Public method to handle 'Name' nodes. |
1616 |
1751 |
1617 @param node reference to the node to be processed |
1752 @param node reference to the node to be processed |
1618 @type ast.Name |
1753 @type ast.Name |
1619 """ |
1754 """ |
1620 self.__names.setdefault(node.id, []).append(node) |
1755 self.__names.setdefault(node.id, []).append(node) |
1621 |
1756 |
1622 def visit(self, node): |
1757 def visit(self, node): |
1623 """ |
1758 """ |
1624 Public method to traverse a given AST node. |
1759 Public method to traverse a given AST node. |
1625 |
1760 |
1626 @param node AST node to be traversed |
1761 @param node AST node to be traversed |
1627 @type ast.Node |
1762 @type ast.Node |
1628 """ |
1763 """ |
1629 if isinstance(node, list): |
1764 if isinstance(node, list): |
1630 for elem in node: |
1765 for elem in node: |
1631 super().visit(elem) |
1766 super().visit(elem) |
1632 else: |
1767 else: |
1633 super().visit(node) |
1768 super().visit(node) |
1634 |
1769 |
1635 def getNames(self): |
1770 def getNames(self): |
1636 """ |
1771 """ |
1637 Public method to return the extracted names and Name nodes. |
1772 Public method to return the extracted names and Name nodes. |
1638 |
1773 |
1639 @return dictionary containing the names as keys and the list of nodes |
1774 @return dictionary containing the names as keys and the list of nodes |
1640 @rtype dict |
1775 @rtype dict |
1641 """ |
1776 """ |
1642 return self.__names |
1777 return self.__names |
1643 |
1778 |
1644 |
1779 |
1645 class ReturnVisitor(ast.NodeVisitor): |
1780 class ReturnVisitor(ast.NodeVisitor): |
1646 """ |
1781 """ |
1647 Class implementing a node visitor to check return statements. |
1782 Class implementing a node visitor to check return statements. |
1648 """ |
1783 """ |
1649 Assigns = 'assigns' |
1784 |
1650 Refs = 'refs' |
1785 Assigns = "assigns" |
1651 Returns = 'returns' |
1786 Refs = "refs" |
1652 |
1787 Returns = "returns" |
|
1788 |
1653 def __init__(self): |
1789 def __init__(self): |
1654 """ |
1790 """ |
1655 Constructor |
1791 Constructor |
1656 """ |
1792 """ |
1657 super().__init__() |
1793 super().__init__() |
1658 |
1794 |
1659 self.__stack = [] |
1795 self.__stack = [] |
1660 self.violations = [] |
1796 self.violations = [] |
1661 self.__loopCount = 0 |
1797 self.__loopCount = 0 |
1662 |
1798 |
1663 @property |
1799 @property |
1664 def assigns(self): |
1800 def assigns(self): |
1665 """ |
1801 """ |
1666 Public method to get the Assign nodes. |
1802 Public method to get the Assign nodes. |
1667 |
1803 |
1668 @return dictionary containing the node name as key and line number |
1804 @return dictionary containing the node name as key and line number |
1669 as value |
1805 as value |
1670 @rtype dict |
1806 @rtype dict |
1671 """ |
1807 """ |
1672 return self.__stack[-1][ReturnVisitor.Assigns] |
1808 return self.__stack[-1][ReturnVisitor.Assigns] |
1673 |
1809 |
1674 @property |
1810 @property |
1675 def refs(self): |
1811 def refs(self): |
1676 """ |
1812 """ |
1677 Public method to get the References nodes. |
1813 Public method to get the References nodes. |
1678 |
1814 |
1679 @return dictionary containing the node name as key and line number |
1815 @return dictionary containing the node name as key and line number |
1680 as value |
1816 as value |
1681 @rtype dict |
1817 @rtype dict |
1682 """ |
1818 """ |
1683 return self.__stack[-1][ReturnVisitor.Refs] |
1819 return self.__stack[-1][ReturnVisitor.Refs] |
1684 |
1820 |
1685 @property |
1821 @property |
1686 def returns(self): |
1822 def returns(self): |
1687 """ |
1823 """ |
1688 Public method to get the Return nodes. |
1824 Public method to get the Return nodes. |
1689 |
1825 |
1690 @return dictionary containing the node name as key and line number |
1826 @return dictionary containing the node name as key and line number |
1691 as value |
1827 as value |
1692 @rtype dict |
1828 @rtype dict |
1693 """ |
1829 """ |
1694 return self.__stack[-1][ReturnVisitor.Returns] |
1830 return self.__stack[-1][ReturnVisitor.Returns] |
1695 |
1831 |
1696 def visit_For(self, node): |
1832 def visit_For(self, node): |
1697 """ |
1833 """ |
1698 Public method to handle a for loop. |
1834 Public method to handle a for loop. |
1699 |
1835 |
1700 @param node reference to the for node to handle |
1836 @param node reference to the for node to handle |
1701 @type ast.For |
1837 @type ast.For |
1702 """ |
1838 """ |
1703 self.__visitLoop(node) |
1839 self.__visitLoop(node) |
1704 |
1840 |
1705 def visit_AsyncFor(self, node): |
1841 def visit_AsyncFor(self, node): |
1706 """ |
1842 """ |
1707 Public method to handle an async for loop. |
1843 Public method to handle an async for loop. |
1708 |
1844 |
1709 @param node reference to the async for node to handle |
1845 @param node reference to the async for node to handle |
1710 @type ast.AsyncFor |
1846 @type ast.AsyncFor |
1711 """ |
1847 """ |
1712 self.__visitLoop(node) |
1848 self.__visitLoop(node) |
1713 |
1849 |
1714 def visit_While(self, node): |
1850 def visit_While(self, node): |
1715 """ |
1851 """ |
1716 Public method to handle a while loop. |
1852 Public method to handle a while loop. |
1717 |
1853 |
1718 @param node reference to the while node to handle |
1854 @param node reference to the while node to handle |
1719 @type ast.While |
1855 @type ast.While |
1720 """ |
1856 """ |
1721 self.__visitLoop(node) |
1857 self.__visitLoop(node) |
1722 |
1858 |
1723 def __visitLoop(self, node): |
1859 def __visitLoop(self, node): |
1724 """ |
1860 """ |
1725 Private method to handle loop nodes. |
1861 Private method to handle loop nodes. |
1726 |
1862 |
1727 @param node reference to the loop node to handle |
1863 @param node reference to the loop node to handle |
1728 @type ast.For, ast.AsyncFor or ast.While |
1864 @type ast.For, ast.AsyncFor or ast.While |
1729 """ |
1865 """ |
1730 self.__loopCount += 1 |
1866 self.__loopCount += 1 |
1731 self.generic_visit(node) |
1867 self.generic_visit(node) |
1732 self.__loopCount -= 1 |
1868 self.__loopCount -= 1 |
1733 |
1869 |
1734 def __visitWithStack(self, node): |
1870 def __visitWithStack(self, node): |
1735 """ |
1871 """ |
1736 Private method to traverse a given function node using a stack. |
1872 Private method to traverse a given function node using a stack. |
1737 |
1873 |
1738 @param node AST node to be traversed |
1874 @param node AST node to be traversed |
1739 @type ast.FunctionDef or ast.AsyncFunctionDef |
1875 @type ast.FunctionDef or ast.AsyncFunctionDef |
1740 """ |
1876 """ |
1741 self.__stack.append({ |
1877 self.__stack.append( |
1742 ReturnVisitor.Assigns: defaultdict(list), |
1878 { |
1743 ReturnVisitor.Refs: defaultdict(list), |
1879 ReturnVisitor.Assigns: defaultdict(list), |
1744 ReturnVisitor.Returns: [] |
1880 ReturnVisitor.Refs: defaultdict(list), |
1745 }) |
1881 ReturnVisitor.Returns: [], |
1746 |
1882 } |
|
1883 ) |
|
1884 |
1747 self.generic_visit(node) |
1885 self.generic_visit(node) |
1748 self.__checkFunction(node) |
1886 self.__checkFunction(node) |
1749 self.__stack.pop() |
1887 self.__stack.pop() |
1750 |
1888 |
1751 def visit_FunctionDef(self, node): |
1889 def visit_FunctionDef(self, node): |
1752 """ |
1890 """ |
1753 Public method to handle a function definition. |
1891 Public method to handle a function definition. |
1754 |
1892 |
1755 @param node reference to the node to handle |
1893 @param node reference to the node to handle |
1756 @type ast.FunctionDef |
1894 @type ast.FunctionDef |
1757 """ |
1895 """ |
1758 self.__visitWithStack(node) |
1896 self.__visitWithStack(node) |
1759 |
1897 |
1760 def visit_AsyncFunctionDef(self, node): |
1898 def visit_AsyncFunctionDef(self, node): |
1761 """ |
1899 """ |
1762 Public method to handle a function definition. |
1900 Public method to handle a function definition. |
1763 |
1901 |
1764 @param node reference to the node to handle |
1902 @param node reference to the node to handle |
1765 @type ast.AsyncFunctionDef |
1903 @type ast.AsyncFunctionDef |
1766 """ |
1904 """ |
1767 self.__visitWithStack(node) |
1905 self.__visitWithStack(node) |
1768 |
1906 |
1769 def visit_Return(self, node): |
1907 def visit_Return(self, node): |
1770 """ |
1908 """ |
1771 Public method to handle a return node. |
1909 Public method to handle a return node. |
1772 |
1910 |
1773 @param node reference to the node to handle |
1911 @param node reference to the node to handle |
1774 @type ast.Return |
1912 @type ast.Return |
1775 """ |
1913 """ |
1776 self.returns.append(node) |
1914 self.returns.append(node) |
1777 self.generic_visit(node) |
1915 self.generic_visit(node) |
1778 |
1916 |
1779 def visit_Assign(self, node): |
1917 def visit_Assign(self, node): |
1780 """ |
1918 """ |
1781 Public method to handle an assign node. |
1919 Public method to handle an assign node. |
1782 |
1920 |
1783 @param node reference to the node to handle |
1921 @param node reference to the node to handle |
1784 @type ast.Assign |
1922 @type ast.Assign |
1785 """ |
1923 """ |
1786 if not self.__stack: |
1924 if not self.__stack: |
1787 return |
1925 return |
1788 |
1926 |
1789 self.generic_visit(node.value) |
1927 self.generic_visit(node.value) |
1790 |
1928 |
1791 target = node.targets[0] |
1929 target = node.targets[0] |
1792 if ( |
1930 if isinstance(target, ast.Tuple) and not isinstance(node.value, ast.Tuple): |
1793 isinstance(target, ast.Tuple) and |
|
1794 not isinstance(node.value, ast.Tuple) |
|
1795 ): |
|
1796 # skip unpacking assign |
1931 # skip unpacking assign |
1797 return |
1932 return |
1798 |
1933 |
1799 self.__visitAssignTarget(target) |
1934 self.__visitAssignTarget(target) |
1800 |
1935 |
1801 def visit_Name(self, node): |
1936 def visit_Name(self, node): |
1802 """ |
1937 """ |
1803 Public method to handle a name node. |
1938 Public method to handle a name node. |
1804 |
1939 |
1805 @param node reference to the node to handle |
1940 @param node reference to the node to handle |
1806 @type ast.Name |
1941 @type ast.Name |
1807 """ |
1942 """ |
1808 if self.__stack: |
1943 if self.__stack: |
1809 self.refs[node.id].append(node.lineno) |
1944 self.refs[node.id].append(node.lineno) |
1810 |
1945 |
1811 def __visitAssignTarget(self, node): |
1946 def __visitAssignTarget(self, node): |
1812 """ |
1947 """ |
1813 Private method to handle an assign target node. |
1948 Private method to handle an assign target node. |
1814 |
1949 |
1815 @param node reference to the node to handle |
1950 @param node reference to the node to handle |
1816 @type ast.AST |
1951 @type ast.AST |
1817 """ |
1952 """ |
1818 if isinstance(node, ast.Tuple): |
1953 if isinstance(node, ast.Tuple): |
1819 for elt in node.elts: |
1954 for elt in node.elts: |
1820 self.__visitAssignTarget(elt) |
1955 self.__visitAssignTarget(elt) |
1821 return |
1956 return |
1822 |
1957 |
1823 if not self.__loopCount and isinstance(node, ast.Name): |
1958 if not self.__loopCount and isinstance(node, ast.Name): |
1824 self.assigns[node.id].append(node.lineno) |
1959 self.assigns[node.id].append(node.lineno) |
1825 return |
1960 return |
1826 |
1961 |
1827 self.generic_visit(node) |
1962 self.generic_visit(node) |
1828 |
1963 |
1829 def __checkFunction(self, node): |
1964 def __checkFunction(self, node): |
1830 """ |
1965 """ |
1831 Private method to check a function definition node. |
1966 Private method to check a function definition node. |
1832 |
1967 |
1833 @param node reference to the node to check |
1968 @param node reference to the node to check |
1834 @type ast.AsyncFunctionDef or ast.FunctionDef |
1969 @type ast.AsyncFunctionDef or ast.FunctionDef |
1835 """ |
1970 """ |
1836 if not self.returns or not node.body: |
1971 if not self.returns or not node.body: |
1837 return |
1972 return |
1838 |
1973 |
1839 if len(node.body) == 1 and isinstance(node.body[-1], ast.Return): |
1974 if len(node.body) == 1 and isinstance(node.body[-1], ast.Return): |
1840 # skip functions that consist of `return None` only |
1975 # skip functions that consist of `return None` only |
1841 return |
1976 return |
1842 |
1977 |
1843 if not self.__resultExists(): |
1978 if not self.__resultExists(): |
1844 self.__checkUnnecessaryReturnNone() |
1979 self.__checkUnnecessaryReturnNone() |
1845 return |
1980 return |
1846 |
1981 |
1847 self.__checkImplicitReturnValue() |
1982 self.__checkImplicitReturnValue() |
1848 self.__checkImplicitReturn(node.body[-1]) |
1983 self.__checkImplicitReturn(node.body[-1]) |
1849 |
1984 |
1850 for n in self.returns: |
1985 for n in self.returns: |
1851 if n.value: |
1986 if n.value: |
1852 self.__checkUnnecessaryAssign(n.value) |
1987 self.__checkUnnecessaryAssign(n.value) |
1853 |
1988 |
1854 def __isNone(self, node): |
1989 def __isNone(self, node): |
1855 """ |
1990 """ |
1856 Private method to check, if a node value is None. |
1991 Private method to check, if a node value is None. |
1857 |
1992 |
1858 @param node reference to the node to check |
1993 @param node reference to the node to check |
1859 @type ast.AST |
1994 @type ast.AST |
1860 @return flag indicating the node contains a None value |
1995 @return flag indicating the node contains a None value |
1861 @rtype bool |
1996 @rtype bool |
1862 """ |
1997 """ |
1863 return ( |
1998 return AstUtilities.isNameConstant(node) and AstUtilities.getValue(node) is None |
1864 AstUtilities.isNameConstant(node) and |
1999 |
1865 AstUtilities.getValue(node) is None |
|
1866 ) |
|
1867 |
|
1868 def __isFalse(self, node): |
2000 def __isFalse(self, node): |
1869 """ |
2001 """ |
1870 Private method to check, if a node value is False. |
2002 Private method to check, if a node value is False. |
1871 |
2003 |
1872 @param node reference to the node to check |
2004 @param node reference to the node to check |
1873 @type ast.AST |
2005 @type ast.AST |
1874 @return flag indicating the node contains a False value |
2006 @return flag indicating the node contains a False value |
1875 @rtype bool |
2007 @rtype bool |
1876 """ |
2008 """ |
1877 return ( |
2009 return ( |
1878 AstUtilities.isNameConstant(node) and |
2010 AstUtilities.isNameConstant(node) and AstUtilities.getValue(node) is False |
1879 AstUtilities.getValue(node) is False |
|
1880 ) |
2011 ) |
1881 |
2012 |
1882 def __resultExists(self): |
2013 def __resultExists(self): |
1883 """ |
2014 """ |
1884 Private method to check the existance of a return result. |
2015 Private method to check the existance of a return result. |
1885 |
2016 |
1886 @return flag indicating the existence of a return result |
2017 @return flag indicating the existence of a return result |
1887 @rtype bool |
2018 @rtype bool |
1888 """ |
2019 """ |
1889 for node in self.returns: |
2020 for node in self.returns: |
1890 value = node.value |
2021 value = node.value |
1891 if value and not self.__isNone(value): |
2022 if value and not self.__isNone(value): |
1892 return True |
2023 return True |
1893 |
2024 |
1894 return False |
2025 return False |
1895 |
2026 |
1896 def __checkImplicitReturnValue(self): |
2027 def __checkImplicitReturnValue(self): |
1897 """ |
2028 """ |
1898 Private method to check for implicit return values. |
2029 Private method to check for implicit return values. |
1899 """ |
2030 """ |
1900 for node in self.returns: |
2031 for node in self.returns: |
1901 if not node.value: |
2032 if not node.value: |
1902 self.violations.append((node, "M832")) |
2033 self.violations.append((node, "M832")) |
1903 |
2034 |
1904 def __checkUnnecessaryReturnNone(self): |
2035 def __checkUnnecessaryReturnNone(self): |
1905 """ |
2036 """ |
1906 Private method to check for an unnecessary 'return None' statement. |
2037 Private method to check for an unnecessary 'return None' statement. |
1907 """ |
2038 """ |
1908 for node in self.returns: |
2039 for node in self.returns: |
1909 if self.__isNone(node.value): |
2040 if self.__isNone(node.value): |
1910 self.violations.append((node, "M831")) |
2041 self.violations.append((node, "M831")) |
1911 |
2042 |
1912 def __checkImplicitReturn(self, node): |
2043 def __checkImplicitReturn(self, node): |
1913 """ |
2044 """ |
1914 Private method to check for an implicit return statement. |
2045 Private method to check for an implicit return statement. |
1915 |
2046 |
1916 @param node reference to the node to check |
2047 @param node reference to the node to check |
1917 @type ast.AST |
2048 @type ast.AST |
1918 """ |
2049 """ |
1919 if isinstance(node, ast.If): |
2050 if isinstance(node, ast.If): |
1920 if not node.body or not node.orelse: |
2051 if not node.body or not node.orelse: |
1921 self.violations.append((node, "M833")) |
2052 self.violations.append((node, "M833")) |
1922 return |
2053 return |
1923 |
2054 |
1924 self.__checkImplicitReturn(node.body[-1]) |
2055 self.__checkImplicitReturn(node.body[-1]) |
1925 self.__checkImplicitReturn(node.orelse[-1]) |
2056 self.__checkImplicitReturn(node.orelse[-1]) |
1926 return |
2057 return |
1927 |
2058 |
1928 if isinstance(node, (ast.For, ast.AsyncFor)) and node.orelse: |
2059 if isinstance(node, (ast.For, ast.AsyncFor)) and node.orelse: |
1929 self.__checkImplicitReturn(node.orelse[-1]) |
2060 self.__checkImplicitReturn(node.orelse[-1]) |
1930 return |
2061 return |
1931 |
2062 |
1932 if isinstance(node, (ast.With, ast.AsyncWith)): |
2063 if isinstance(node, (ast.With, ast.AsyncWith)): |
1933 self.__checkImplicitReturn(node.body[-1]) |
2064 self.__checkImplicitReturn(node.body[-1]) |
1934 return |
2065 return |
1935 |
2066 |
1936 if isinstance(node, ast.Assert) and self.__isFalse(node.test): |
2067 if isinstance(node, ast.Assert) and self.__isFalse(node.test): |
1937 return |
2068 return |
1938 |
2069 |
1939 try: |
2070 try: |
1940 okNodes = (ast.Return, ast.Raise, ast.While, ast.Try) |
2071 okNodes = (ast.Return, ast.Raise, ast.While, ast.Try) |
1941 except AttributeError: |
2072 except AttributeError: |
1942 okNodes = (ast.Return, ast.Raise, ast.While) |
2073 okNodes = (ast.Return, ast.Raise, ast.While) |
1943 if not isinstance(node, okNodes): |
2074 if not isinstance(node, okNodes): |
1944 self.violations.append((node, "M833")) |
2075 self.violations.append((node, "M833")) |
1945 |
2076 |
1946 def __checkUnnecessaryAssign(self, node): |
2077 def __checkUnnecessaryAssign(self, node): |
1947 """ |
2078 """ |
1948 Private method to check for an unnecessary assign statement. |
2079 Private method to check for an unnecessary assign statement. |
1949 |
2080 |
1950 @param node reference to the node to check |
2081 @param node reference to the node to check |
1951 @type ast.AST |
2082 @type ast.AST |
1952 """ |
2083 """ |
1953 if not isinstance(node, ast.Name): |
2084 if not isinstance(node, ast.Name): |
1954 return |
2085 return |
1955 |
2086 |
1956 varname = node.id |
2087 varname = node.id |
1957 returnLineno = node.lineno |
2088 returnLineno = node.lineno |
1958 |
2089 |
1959 if varname not in self.assigns: |
2090 if varname not in self.assigns: |
1960 return |
2091 return |
1961 |
2092 |
1962 if varname not in self.refs: |
2093 if varname not in self.refs: |
1963 self.violations.append((node, "M834")) |
2094 self.violations.append((node, "M834")) |
1964 return |
2095 return |
1965 |
2096 |
1966 if self.__hasRefsBeforeNextAssign(varname, returnLineno): |
2097 if self.__hasRefsBeforeNextAssign(varname, returnLineno): |
1967 return |
2098 return |
1968 |
2099 |
1969 self.violations.append((node, "M834")) |
2100 self.violations.append((node, "M834")) |
1970 |
2101 |
1971 def __hasRefsBeforeNextAssign(self, varname, returnLineno): |
2102 def __hasRefsBeforeNextAssign(self, varname, returnLineno): |
1972 """ |
2103 """ |
1973 Private method to check for references before a following assign |
2104 Private method to check for references before a following assign |
1974 statement. |
2105 statement. |
1975 |
2106 |
1976 @param varname variable name to check for |
2107 @param varname variable name to check for |
1977 @type str |
2108 @type str |
1978 @param returnLineno line number of the return statement |
2109 @param returnLineno line number of the return statement |
1979 @type int |
2110 @type int |
1980 @return flag indicating the existence of references |
2111 @return flag indicating the existence of references |
1981 @rtype bool |
2112 @rtype bool |
1982 """ |
2113 """ |
1983 beforeAssign = 0 |
2114 beforeAssign = 0 |
1984 afterAssign = None |
2115 afterAssign = None |
1985 |
2116 |
1986 for lineno in sorted(self.assigns[varname]): |
2117 for lineno in sorted(self.assigns[varname]): |
1987 if lineno > returnLineno: |
2118 if lineno > returnLineno: |
1988 afterAssign = lineno |
2119 afterAssign = lineno |
1989 break |
2120 break |
1990 |
2121 |
1991 if lineno <= returnLineno: |
2122 if lineno <= returnLineno: |
1992 beforeAssign = lineno |
2123 beforeAssign = lineno |
1993 |
2124 |
1994 for lineno in self.refs[varname]: |
2125 for lineno in self.refs[varname]: |
1995 if lineno == returnLineno: |
2126 if lineno == returnLineno: |
1996 continue |
2127 continue |
1997 |
2128 |
1998 if afterAssign: |
2129 if afterAssign: |
1999 if beforeAssign < lineno <= afterAssign: |
2130 if beforeAssign < lineno <= afterAssign: |
2000 return True |
2131 return True |
2001 |
2132 |
2002 elif beforeAssign < lineno: |
2133 elif beforeAssign < lineno: |
2003 return True |
2134 return True |
2004 |
2135 |
2005 return False |
2136 return False |
2006 |
2137 |
2007 |
2138 |
2008 class DateTimeVisitor(ast.NodeVisitor): |
2139 class DateTimeVisitor(ast.NodeVisitor): |
2009 """ |
2140 """ |
2010 Class implementing a node visitor to check datetime function calls. |
2141 Class implementing a node visitor to check datetime function calls. |
2011 |
2142 |
2012 Note: This class is modelled after flake8_datetimez checker. |
2143 Note: This class is modelled after flake8_datetimez checker. |
2013 """ |
2144 """ |
|
2145 |
2014 def __init__(self): |
2146 def __init__(self): |
2015 """ |
2147 """ |
2016 Constructor |
2148 Constructor |
2017 """ |
2149 """ |
2018 super().__init__() |
2150 super().__init__() |
2019 |
2151 |
2020 self.violations = [] |
2152 self.violations = [] |
2021 |
2153 |
2022 def __getFromKeywords(self, keywords, name): |
2154 def __getFromKeywords(self, keywords, name): |
2023 """ |
2155 """ |
2024 Private method to get a keyword node given its name. |
2156 Private method to get a keyword node given its name. |
2025 |
2157 |
2026 @param keywords list of keyword argument nodes |
2158 @param keywords list of keyword argument nodes |
2027 @type list of ast.AST |
2159 @type list of ast.AST |
2028 @param name name of the keyword node |
2160 @param name name of the keyword node |
2029 @type str |
2161 @type str |
2030 @return keyword node |
2162 @return keyword node |
2031 @rtype ast.AST |
2163 @rtype ast.AST |
2032 """ |
2164 """ |
2033 for keyword in keywords: |
2165 for keyword in keywords: |
2034 if keyword.arg == name: |
2166 if keyword.arg == name: |
2035 return keyword |
2167 return keyword |
2036 |
2168 |
2037 return None |
2169 return None |
2038 |
2170 |
2039 def visit_Call(self, node): |
2171 def visit_Call(self, node): |
2040 """ |
2172 """ |
2041 Public method to handle a function call. |
2173 Public method to handle a function call. |
2042 |
2174 |
2043 Every datetime related function call is check for use of the naive |
2175 Every datetime related function call is check for use of the naive |
2044 variant (i.e. use without TZ info). |
2176 variant (i.e. use without TZ info). |
2045 |
2177 |
2046 @param node reference to the node to be processed |
2178 @param node reference to the node to be processed |
2047 @type ast.Call |
2179 @type ast.Call |
2048 """ |
2180 """ |
2049 # datetime.something() |
2181 # datetime.something() |
2050 isDateTimeClass = ( |
2182 isDateTimeClass = ( |
2051 isinstance(node.func, ast.Attribute) and |
2183 isinstance(node.func, ast.Attribute) |
2052 isinstance(node.func.value, ast.Name) and |
2184 and isinstance(node.func.value, ast.Name) |
2053 node.func.value.id == 'datetime') |
2185 and node.func.value.id == "datetime" |
2054 |
2186 ) |
|
2187 |
2055 # datetime.datetime.something() |
2188 # datetime.datetime.something() |
2056 isDateTimeModuleAndClass = ( |
2189 isDateTimeModuleAndClass = ( |
2057 isinstance(node.func, ast.Attribute) and |
2190 isinstance(node.func, ast.Attribute) |
2058 isinstance(node.func.value, ast.Attribute) and |
2191 and isinstance(node.func.value, ast.Attribute) |
2059 node.func.value.attr == 'datetime' and |
2192 and node.func.value.attr == "datetime" |
2060 isinstance(node.func.value.value, ast.Name) and |
2193 and isinstance(node.func.value.value, ast.Name) |
2061 node.func.value.value.id == 'datetime') |
2194 and node.func.value.value.id == "datetime" |
2062 |
2195 ) |
|
2196 |
2063 if isDateTimeClass: |
2197 if isDateTimeClass: |
2064 if node.func.attr == 'datetime': |
2198 if node.func.attr == "datetime": |
2065 # datetime.datetime(2000, 1, 1, 0, 0, 0, 0, |
2199 # datetime.datetime(2000, 1, 1, 0, 0, 0, 0, |
2066 # datetime.timezone.utc) |
2200 # datetime.timezone.utc) |
|
2201 isCase1 = len(node.args) >= 8 and not ( |
|
2202 AstUtilities.isNameConstant(node.args[7]) |
|
2203 and AstUtilities.getValue(node.args[7]) is None |
|
2204 ) |
|
2205 |
|
2206 # datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc) |
|
2207 tzinfoKeyword = self.__getFromKeywords(node.keywords, "tzinfo") |
|
2208 isCase2 = tzinfoKeyword is not None and not ( |
|
2209 AstUtilities.isNameConstant(tzinfoKeyword.value) |
|
2210 and AstUtilities.getValue(tzinfoKeyword.value) is None |
|
2211 ) |
|
2212 |
|
2213 if not (isCase1 or isCase2): |
|
2214 self.violations.append((node, "M301")) |
|
2215 |
|
2216 elif node.func.attr == "time": |
|
2217 # time(12, 10, 45, 0, datetime.timezone.utc) |
|
2218 isCase1 = len(node.args) >= 5 and not ( |
|
2219 AstUtilities.isNameConstant(node.args[4]) |
|
2220 and AstUtilities.getValue(node.args[4]) is None |
|
2221 ) |
|
2222 |
|
2223 # datetime.time(12, 10, 45, tzinfo=datetime.timezone.utc) |
|
2224 tzinfoKeyword = self.__getFromKeywords(node.keywords, "tzinfo") |
|
2225 isCase2 = tzinfoKeyword is not None and not ( |
|
2226 AstUtilities.isNameConstant(tzinfoKeyword.value) |
|
2227 and AstUtilities.getValue(tzinfoKeyword.value) is None |
|
2228 ) |
|
2229 |
|
2230 if not (isCase1 or isCase2): |
|
2231 self.violations.append((node, "M321")) |
|
2232 |
|
2233 elif node.func.attr == "date": |
|
2234 self.violations.append((node, "M311")) |
|
2235 |
|
2236 if isDateTimeClass or isDateTimeModuleAndClass: |
|
2237 if node.func.attr == "today": |
|
2238 self.violations.append((node, "M302")) |
|
2239 |
|
2240 elif node.func.attr == "utcnow": |
|
2241 self.violations.append((node, "M303")) |
|
2242 |
|
2243 elif node.func.attr == "utcfromtimestamp": |
|
2244 self.violations.append((node, "M304")) |
|
2245 |
|
2246 elif node.func.attr in "now": |
|
2247 # datetime.now(UTC) |
2067 isCase1 = ( |
2248 isCase1 = ( |
2068 len(node.args) >= 8 and |
2249 len(node.args) == 1 |
2069 not ( |
2250 and len(node.keywords) == 0 |
2070 AstUtilities.isNameConstant(node.args[7]) and |
2251 and not ( |
2071 AstUtilities.getValue(node.args[7]) is None |
2252 AstUtilities.isNameConstant(node.args[0]) |
|
2253 and AstUtilities.getValue(node.args[0]) is None |
2072 ) |
2254 ) |
2073 ) |
2255 ) |
2074 |
2256 |
2075 # datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc) |
2257 # datetime.now(tz=UTC) |
2076 tzinfoKeyword = self.__getFromKeywords(node.keywords, 'tzinfo') |
2258 tzKeyword = self.__getFromKeywords(node.keywords, "tz") |
2077 isCase2 = ( |
2259 isCase2 = tzKeyword is not None and not ( |
2078 tzinfoKeyword is not None and |
2260 AstUtilities.isNameConstant(tzKeyword.value) |
2079 not ( |
2261 and AstUtilities.getValue(tzKeyword.value) is None |
2080 AstUtilities.isNameConstant(tzinfoKeyword.value) and |
2262 ) |
2081 AstUtilities.getValue(tzinfoKeyword.value) is None |
2263 |
|
2264 if not (isCase1 or isCase2): |
|
2265 self.violations.append((node, "M305")) |
|
2266 |
|
2267 elif node.func.attr == "fromtimestamp": |
|
2268 # datetime.fromtimestamp(1234, UTC) |
|
2269 isCase1 = ( |
|
2270 len(node.args) == 2 |
|
2271 and len(node.keywords) == 0 |
|
2272 and not ( |
|
2273 AstUtilities.isNameConstant(node.args[1]) |
|
2274 and AstUtilities.getValue(node.args[1]) is None |
2082 ) |
2275 ) |
2083 ) |
2276 ) |
2084 |
2277 |
2085 if not (isCase1 or isCase2): |
2278 # datetime.fromtimestamp(1234, tz=UTC) |
2086 self.violations.append((node, "M301")) |
2279 tzKeyword = self.__getFromKeywords(node.keywords, "tz") |
2087 |
2280 isCase2 = tzKeyword is not None and not ( |
2088 elif node.func.attr == 'time': |
2281 AstUtilities.isNameConstant(tzKeyword.value) |
2089 # time(12, 10, 45, 0, datetime.timezone.utc) |
2282 and AstUtilities.getValue(tzKeyword.value) is None |
2090 isCase1 = ( |
|
2091 len(node.args) >= 5 and |
|
2092 not ( |
|
2093 AstUtilities.isNameConstant(node.args[4]) and |
|
2094 AstUtilities.getValue(node.args[4]) is None |
|
2095 ) |
|
2096 ) |
2283 ) |
2097 |
2284 |
2098 # datetime.time(12, 10, 45, tzinfo=datetime.timezone.utc) |
|
2099 tzinfoKeyword = self.__getFromKeywords(node.keywords, 'tzinfo') |
|
2100 isCase2 = ( |
|
2101 tzinfoKeyword is not None and |
|
2102 not ( |
|
2103 AstUtilities.isNameConstant(tzinfoKeyword.value) and |
|
2104 AstUtilities.getValue(tzinfoKeyword.value) is None |
|
2105 ) |
|
2106 ) |
|
2107 |
|
2108 if not (isCase1 or isCase2): |
|
2109 self.violations.append((node, "M321")) |
|
2110 |
|
2111 elif node.func.attr == 'date': |
|
2112 self.violations.append((node, "M311")) |
|
2113 |
|
2114 if isDateTimeClass or isDateTimeModuleAndClass: |
|
2115 if node.func.attr == 'today': |
|
2116 self.violations.append((node, "M302")) |
|
2117 |
|
2118 elif node.func.attr == 'utcnow': |
|
2119 self.violations.append((node, "M303")) |
|
2120 |
|
2121 elif node.func.attr == 'utcfromtimestamp': |
|
2122 self.violations.append((node, "M304")) |
|
2123 |
|
2124 elif node.func.attr in 'now': |
|
2125 # datetime.now(UTC) |
|
2126 isCase1 = ( |
|
2127 len(node.args) == 1 and |
|
2128 len(node.keywords) == 0 and |
|
2129 not ( |
|
2130 AstUtilities.isNameConstant(node.args[0]) and |
|
2131 AstUtilities.getValue(node.args[0]) is None |
|
2132 ) |
|
2133 ) |
|
2134 |
|
2135 # datetime.now(tz=UTC) |
|
2136 tzKeyword = self.__getFromKeywords(node.keywords, 'tz') |
|
2137 isCase2 = ( |
|
2138 tzKeyword is not None and |
|
2139 not ( |
|
2140 AstUtilities.isNameConstant(tzKeyword.value) and |
|
2141 AstUtilities.getValue(tzKeyword.value) is None |
|
2142 ) |
|
2143 ) |
|
2144 |
|
2145 if not (isCase1 or isCase2): |
|
2146 self.violations.append((node, "M305")) |
|
2147 |
|
2148 elif node.func.attr == 'fromtimestamp': |
|
2149 # datetime.fromtimestamp(1234, UTC) |
|
2150 isCase1 = ( |
|
2151 len(node.args) == 2 and |
|
2152 len(node.keywords) == 0 and |
|
2153 not ( |
|
2154 AstUtilities.isNameConstant(node.args[1]) and |
|
2155 AstUtilities.getValue(node.args[1]) is None |
|
2156 ) |
|
2157 ) |
|
2158 |
|
2159 # datetime.fromtimestamp(1234, tz=UTC) |
|
2160 tzKeyword = self.__getFromKeywords(node.keywords, 'tz') |
|
2161 isCase2 = ( |
|
2162 tzKeyword is not None and |
|
2163 not ( |
|
2164 AstUtilities.isNameConstant(tzKeyword.value) and |
|
2165 AstUtilities.getValue(tzKeyword.value) is None |
|
2166 ) |
|
2167 ) |
|
2168 |
|
2169 if not (isCase1 or isCase2): |
2285 if not (isCase1 or isCase2): |
2170 self.violations.append((node, "M306")) |
2286 self.violations.append((node, "M306")) |
2171 |
2287 |
2172 elif node.func.attr == 'strptime': |
2288 elif node.func.attr == "strptime": |
2173 # datetime.strptime(...).replace(tzinfo=UTC) |
2289 # datetime.strptime(...).replace(tzinfo=UTC) |
2174 parent = getattr(node, '_dtCheckerParent', None) |
2290 parent = getattr(node, "_dtCheckerParent", None) |
2175 pparent = getattr(parent, '_dtCheckerParent', None) |
2291 pparent = getattr(parent, "_dtCheckerParent", None) |
2176 if ( |
2292 if not ( |
2177 not (isinstance(parent, ast.Attribute) and |
2293 isinstance(parent, ast.Attribute) and parent.attr == "replace" |
2178 parent.attr == 'replace') or |
2294 ) or not isinstance(pparent, ast.Call): |
2179 not isinstance(pparent, ast.Call) |
|
2180 ): |
|
2181 isCase1 = False |
2295 isCase1 = False |
2182 else: |
2296 else: |
2183 tzinfoKeyword = self.__getFromKeywords(pparent.keywords, |
2297 tzinfoKeyword = self.__getFromKeywords(pparent.keywords, "tzinfo") |
2184 'tzinfo') |
2298 isCase1 = tzinfoKeyword is not None and not ( |
2185 isCase1 = ( |
2299 AstUtilities.isNameConstant(tzinfoKeyword.value) |
2186 tzinfoKeyword is not None and |
2300 and AstUtilities.getValue(tzinfoKeyword.value) is None |
2187 not ( |
|
2188 AstUtilities.isNameConstant( |
|
2189 tzinfoKeyword.value) and |
|
2190 AstUtilities.getValue(tzinfoKeyword.value) is None |
|
2191 ) |
|
2192 ) |
2301 ) |
2193 |
2302 |
2194 if not isCase1: |
2303 if not isCase1: |
2195 self.violations.append((node, "M307")) |
2304 self.violations.append((node, "M307")) |
2196 |
2305 |
2197 elif node.func.attr == 'fromordinal': |
2306 elif node.func.attr == "fromordinal": |
2198 self.violations.append((node, "M308")) |
2307 self.violations.append((node, "M308")) |
2199 |
2308 |
2200 # date.something() |
2309 # date.something() |
2201 isDateClass = (isinstance(node.func, ast.Attribute) and |
2310 isDateClass = ( |
2202 isinstance(node.func.value, ast.Name) and |
2311 isinstance(node.func, ast.Attribute) |
2203 node.func.value.id == 'date') |
2312 and isinstance(node.func.value, ast.Name) |
2204 |
2313 and node.func.value.id == "date" |
|
2314 ) |
|
2315 |
2205 # datetime.date.something() |
2316 # datetime.date.something() |
2206 isDateModuleAndClass = (isinstance(node.func, ast.Attribute) and |
2317 isDateModuleAndClass = ( |
2207 isinstance(node.func.value, ast.Attribute) and |
2318 isinstance(node.func, ast.Attribute) |
2208 node.func.value.attr == 'date' and |
2319 and isinstance(node.func.value, ast.Attribute) |
2209 isinstance(node.func.value.value, ast.Name) and |
2320 and node.func.value.attr == "date" |
2210 node.func.value.value.id == 'datetime') |
2321 and isinstance(node.func.value.value, ast.Name) |
2211 |
2322 and node.func.value.value.id == "datetime" |
|
2323 ) |
|
2324 |
2212 if isDateClass or isDateModuleAndClass: |
2325 if isDateClass or isDateModuleAndClass: |
2213 if node.func.attr == 'today': |
2326 if node.func.attr == "today": |
2214 self.violations.append((node, "M312")) |
2327 self.violations.append((node, "M312")) |
2215 |
2328 |
2216 elif node.func.attr == 'fromtimestamp': |
2329 elif node.func.attr == "fromtimestamp": |
2217 self.violations.append((node, "M313")) |
2330 self.violations.append((node, "M313")) |
2218 |
2331 |
2219 elif node.func.attr == 'fromordinal': |
2332 elif node.func.attr == "fromordinal": |
2220 self.violations.append((node, "M314")) |
2333 self.violations.append((node, "M314")) |
2221 |
2334 |
2222 elif node.func.attr == 'fromisoformat': |
2335 elif node.func.attr == "fromisoformat": |
2223 self.violations.append((node, "M315")) |
2336 self.violations.append((node, "M315")) |
2224 |
2337 |
2225 self.generic_visit(node) |
2338 self.generic_visit(node) |
2226 |
2339 |
2227 |
2340 |
2228 class SysVersionVisitor(ast.NodeVisitor): |
2341 class SysVersionVisitor(ast.NodeVisitor): |
2229 """ |
2342 """ |
2230 Class implementing a node visitor to check the use of sys.version and |
2343 Class implementing a node visitor to check the use of sys.version and |
2231 sys.version_info. |
2344 sys.version_info. |
2232 |
2345 |
2233 Note: This class is modelled after flake8-2020 checker. |
2346 Note: This class is modelled after flake8-2020 checker. |
2234 """ |
2347 """ |
|
2348 |
2235 def __init__(self): |
2349 def __init__(self): |
2236 """ |
2350 """ |
2237 Constructor |
2351 Constructor |
2238 """ |
2352 """ |
2239 super().__init__() |
2353 super().__init__() |
2240 |
2354 |
2241 self.violations = [] |
2355 self.violations = [] |
2242 self.__fromImports = {} |
2356 self.__fromImports = {} |
2243 |
2357 |
2244 def visit_ImportFrom(self, node): |
2358 def visit_ImportFrom(self, node): |
2245 """ |
2359 """ |
2246 Public method to handle a from ... import ... statement. |
2360 Public method to handle a from ... import ... statement. |
2247 |
2361 |
2248 @param node reference to the node to be processed |
2362 @param node reference to the node to be processed |
2249 @type ast.ImportFrom |
2363 @type ast.ImportFrom |
2250 """ |
2364 """ |
2251 for alias in node.names: |
2365 for alias in node.names: |
2252 if node.module is not None and not alias.asname: |
2366 if node.module is not None and not alias.asname: |
2253 self.__fromImports[alias.name] = node.module |
2367 self.__fromImports[alias.name] = node.module |
2254 |
2368 |
2255 self.generic_visit(node) |
2369 self.generic_visit(node) |
2256 |
2370 |
2257 def __isSys(self, attr, node): |
2371 def __isSys(self, attr, node): |
2258 """ |
2372 """ |
2259 Private method to check for a reference to sys attribute. |
2373 Private method to check for a reference to sys attribute. |
2260 |
2374 |
2261 @param attr attribute name |
2375 @param attr attribute name |
2262 @type str |
2376 @type str |
2263 @param node reference to the node to be checked |
2377 @param node reference to the node to be checked |
2264 @type ast.Node |
2378 @type ast.Node |
2265 @return flag indicating a match |
2379 @return flag indicating a match |
2266 @rtype bool |
2380 @rtype bool |
2267 """ |
2381 """ |
2268 match = False |
2382 match = False |
2269 if ( |
2383 if ( |
2270 (isinstance(node, ast.Attribute) and |
2384 isinstance(node, ast.Attribute) |
2271 isinstance(node.value, ast.Name) and |
2385 and isinstance(node.value, ast.Name) |
2272 node.value.id == "sys" and |
2386 and node.value.id == "sys" |
2273 node.attr == attr) or |
2387 and node.attr == attr |
2274 (isinstance(node, ast.Name) and |
2388 ) or ( |
2275 node.id == attr and |
2389 isinstance(node, ast.Name) |
2276 self.__fromImports.get(node.id) == "sys") |
2390 and node.id == attr |
|
2391 and self.__fromImports.get(node.id) == "sys" |
2277 ): |
2392 ): |
2278 match = True |
2393 match = True |
2279 |
2394 |
2280 return match |
2395 return match |
2281 |
2396 |
2282 def __isSysVersionUpperSlice(self, node, n): |
2397 def __isSysVersionUpperSlice(self, node, n): |
2283 """ |
2398 """ |
2284 Private method to check the upper slice of sys.version. |
2399 Private method to check the upper slice of sys.version. |
2285 |
2400 |
2286 @param node reference to the node to be checked |
2401 @param node reference to the node to be checked |
2287 @type ast.Node |
2402 @type ast.Node |
2288 @param n slice value to check against |
2403 @param n slice value to check against |
2289 @type int |
2404 @type int |
2290 @return flag indicating a match |
2405 @return flag indicating a match |
2291 @rtype bool |
2406 @rtype bool |
2292 """ |
2407 """ |
2293 return ( |
2408 return ( |
2294 self.__isSys("version", node.value) and |
2409 self.__isSys("version", node.value) |
2295 isinstance(node.slice, ast.Slice) and |
2410 and isinstance(node.slice, ast.Slice) |
2296 node.slice.lower is None and |
2411 and node.slice.lower is None |
2297 AstUtilities.isNumber(node.slice.upper) and |
2412 and AstUtilities.isNumber(node.slice.upper) |
2298 AstUtilities.getValue(node.slice.upper) == n and |
2413 and AstUtilities.getValue(node.slice.upper) == n |
2299 node.slice.step is None |
2414 and node.slice.step is None |
2300 ) |
2415 ) |
2301 |
2416 |
2302 def visit_Subscript(self, node): |
2417 def visit_Subscript(self, node): |
2303 """ |
2418 """ |
2304 Public method to handle a subscript. |
2419 Public method to handle a subscript. |
2305 |
2420 |
2306 @param node reference to the node to be processed |
2421 @param node reference to the node to be processed |
2307 @type ast.Subscript |
2422 @type ast.Subscript |
2308 """ |
2423 """ |
2309 if self.__isSysVersionUpperSlice(node, 1): |
2424 if self.__isSysVersionUpperSlice(node, 1): |
2310 self.violations.append((node.value, "M423")) |
2425 self.violations.append((node.value, "M423")) |
2311 elif self.__isSysVersionUpperSlice(node, 3): |
2426 elif self.__isSysVersionUpperSlice(node, 3): |
2312 self.violations.append((node.value, "M401")) |
2427 self.violations.append((node.value, "M401")) |
2313 elif ( |
2428 elif ( |
2314 self.__isSys('version', node.value) and |
2429 self.__isSys("version", node.value) |
2315 isinstance(node.slice, ast.Index) and |
2430 and isinstance(node.slice, ast.Index) |
2316 AstUtilities.isNumber(node.slice.value) and |
2431 and AstUtilities.isNumber(node.slice.value) |
2317 AstUtilities.getValue(node.slice.value) == 2 |
2432 and AstUtilities.getValue(node.slice.value) == 2 |
2318 ): |
2433 ): |
2319 self.violations.append((node.value, "M402")) |
2434 self.violations.append((node.value, "M402")) |
2320 elif ( |
2435 elif ( |
2321 self.__isSys('version', node.value) and |
2436 self.__isSys("version", node.value) |
2322 isinstance(node.slice, ast.Index) and |
2437 and isinstance(node.slice, ast.Index) |
2323 AstUtilities.isNumber(node.slice.value) and |
2438 and AstUtilities.isNumber(node.slice.value) |
2324 AstUtilities.getValue(node.slice.value) == 0 |
2439 and AstUtilities.getValue(node.slice.value) == 0 |
2325 ): |
2440 ): |
2326 self.violations.append((node.value, "M421")) |
2441 self.violations.append((node.value, "M421")) |
2327 |
2442 |
2328 self.generic_visit(node) |
2443 self.generic_visit(node) |
2329 |
2444 |
2330 def visit_Compare(self, node): |
2445 def visit_Compare(self, node): |
2331 """ |
2446 """ |
2332 Public method to handle a comparison. |
2447 Public method to handle a comparison. |
2333 |
2448 |
2334 @param node reference to the node to be processed |
2449 @param node reference to the node to be processed |
2335 @type ast.Compare |
2450 @type ast.Compare |
2336 """ |
2451 """ |
2337 if ( |
2452 if ( |
2338 isinstance(node.left, ast.Subscript) and |
2453 isinstance(node.left, ast.Subscript) |
2339 self.__isSys('version_info', node.left.value) and |
2454 and self.__isSys("version_info", node.left.value) |
2340 isinstance(node.left.slice, ast.Index) and |
2455 and isinstance(node.left.slice, ast.Index) |
2341 AstUtilities.isNumber(node.left.slice.value) and |
2456 and AstUtilities.isNumber(node.left.slice.value) |
2342 AstUtilities.getValue(node.left.slice.value) == 0 and |
2457 and AstUtilities.getValue(node.left.slice.value) == 0 |
2343 len(node.ops) == 1 and |
2458 and len(node.ops) == 1 |
2344 isinstance(node.ops[0], ast.Eq) and |
2459 and isinstance(node.ops[0], ast.Eq) |
2345 AstUtilities.isNumber(node.comparators[0]) and |
2460 and AstUtilities.isNumber(node.comparators[0]) |
2346 AstUtilities.getValue(node.comparators[0]) == 3 |
2461 and AstUtilities.getValue(node.comparators[0]) == 3 |
2347 ): |
2462 ): |
2348 self.violations.append((node.left, "M411")) |
2463 self.violations.append((node.left, "M411")) |
2349 elif ( |
2464 elif ( |
2350 self.__isSys('version', node.left) and |
2465 self.__isSys("version", node.left) |
2351 len(node.ops) == 1 and |
2466 and len(node.ops) == 1 |
2352 isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) and |
2467 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) |
2353 AstUtilities.isString(node.comparators[0]) |
2468 and AstUtilities.isString(node.comparators[0]) |
2354 ): |
2469 ): |
2355 if len(AstUtilities.getValue(node.comparators[0])) == 1: |
2470 if len(AstUtilities.getValue(node.comparators[0])) == 1: |
2356 errorCode = "M422" |
2471 errorCode = "M422" |
2357 else: |
2472 else: |
2358 errorCode = "M403" |
2473 errorCode = "M403" |
2359 self.violations.append((node.left, errorCode)) |
2474 self.violations.append((node.left, errorCode)) |
2360 elif ( |
2475 elif ( |
2361 isinstance(node.left, ast.Subscript) and |
2476 isinstance(node.left, ast.Subscript) |
2362 self.__isSys('version_info', node.left.value) and |
2477 and self.__isSys("version_info", node.left.value) |
2363 isinstance(node.left.slice, ast.Index) and |
2478 and isinstance(node.left.slice, ast.Index) |
2364 AstUtilities.isNumber(node.left.slice.value) and |
2479 and AstUtilities.isNumber(node.left.slice.value) |
2365 AstUtilities.getValue(node.left.slice.value) == 1 and |
2480 and AstUtilities.getValue(node.left.slice.value) == 1 |
2366 len(node.ops) == 1 and |
2481 and len(node.ops) == 1 |
2367 isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) and |
2482 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) |
2368 AstUtilities.isNumber(node.comparators[0]) |
2483 and AstUtilities.isNumber(node.comparators[0]) |
2369 ): |
2484 ): |
2370 self.violations.append((node, "M413")) |
2485 self.violations.append((node, "M413")) |
2371 elif ( |
2486 elif ( |
2372 isinstance(node.left, ast.Attribute) and |
2487 isinstance(node.left, ast.Attribute) |
2373 self.__isSys('version_info', node.left.value) and |
2488 and self.__isSys("version_info", node.left.value) |
2374 node.left.attr == 'minor' and |
2489 and node.left.attr == "minor" |
2375 len(node.ops) == 1 and |
2490 and len(node.ops) == 1 |
2376 isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) and |
2491 and isinstance(node.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)) |
2377 AstUtilities.isNumber(node.comparators[0]) |
2492 and AstUtilities.isNumber(node.comparators[0]) |
2378 ): |
2493 ): |
2379 self.violations.append((node, "M414")) |
2494 self.violations.append((node, "M414")) |
2380 |
2495 |
2381 self.generic_visit(node) |
2496 self.generic_visit(node) |
2382 |
2497 |
2383 def visit_Attribute(self, node): |
2498 def visit_Attribute(self, node): |
2384 """ |
2499 """ |
2385 Public method to handle an attribute. |
2500 Public method to handle an attribute. |
2386 |
2501 |
2387 @param node reference to the node to be processed |
2502 @param node reference to the node to be processed |
2388 @type ast.Attribute |
2503 @type ast.Attribute |
2389 """ |
2504 """ |
2390 if ( |
2505 if ( |
2391 isinstance(node.value, ast.Name) and |
2506 isinstance(node.value, ast.Name) |
2392 node.value.id == 'six' and |
2507 and node.value.id == "six" |
2393 node.attr == 'PY3' |
2508 and node.attr == "PY3" |
2394 ): |
2509 ): |
2395 self.violations.append((node, "M412")) |
2510 self.violations.append((node, "M412")) |
2396 |
2511 |
2397 self.generic_visit(node) |
2512 self.generic_visit(node) |
2398 |
2513 |
2399 def visit_Name(self, node): |
2514 def visit_Name(self, node): |
2400 """ |
2515 """ |
2401 Public method to handle an name. |
2516 Public method to handle an name. |
2402 |
2517 |
2403 @param node reference to the node to be processed |
2518 @param node reference to the node to be processed |
2404 @type ast.Name |
2519 @type ast.Name |
2405 """ |
2520 """ |
2406 if node.id == 'PY3' and self.__fromImports.get(node.id) == 'six': |
2521 if node.id == "PY3" and self.__fromImports.get(node.id) == "six": |
2407 self.violations.append((node, "M412")) |
2522 self.violations.append((node, "M412")) |
2408 |
2523 |
2409 self.generic_visit(node) |
2524 self.generic_visit(node) |
|
2525 |
2410 |
2526 |
2411 # |
2527 # |
2412 # eflag: noqa = M891 |
2528 # eflag: noqa = M891 |