149 self.__args = args |
149 self.__args = args |
150 |
150 |
151 self.__pep3101FormatRegex = re.compile( |
151 self.__pep3101FormatRegex = re.compile( |
152 r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%') |
152 r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%') |
153 |
153 |
154 if sys.version_info >= (3, 0): |
154 import builtins |
155 import builtins |
155 self.__builtins = [b for b in dir(builtins) |
156 self.__builtins = [b for b in dir(builtins) |
156 if b not in self.BuiltinsWhiteList] |
157 if b not in self.BuiltinsWhiteList] |
|
158 else: |
|
159 import __builtin__ |
|
160 self.__builtins = [b for b in dir(__builtin__) |
|
161 if b not in self.BuiltinsWhiteList] |
|
162 |
157 |
163 # statistics counters |
158 # statistics counters |
164 self.counters = {} |
159 self.counters = {} |
165 |
160 |
166 # collection of detected errors |
161 # collection of detected errors |
290 |
285 |
291 @return generated AST |
286 @return generated AST |
292 @rtype ast.AST |
287 @rtype ast.AST |
293 """ |
288 """ |
294 source = "".join(self.__source) |
289 source = "".join(self.__source) |
295 # Check type for py2: if not str it's unicode |
|
296 if sys.version_info[0] == 2: |
|
297 try: |
|
298 source = source.encode('utf-8') |
|
299 except UnicodeError: |
|
300 pass |
|
301 |
|
302 return compile(source, self.__filename, 'exec', ast.PyCF_ONLY_AST) |
290 return compile(source, self.__filename, 'exec', ast.PyCF_ONLY_AST) |
303 |
291 |
304 def run(self): |
292 def run(self): |
305 """ |
293 """ |
306 Public method to check the given source against miscellaneous |
294 Public method to check the given source against miscellaneous |
531 |
519 |
532 visitor = TextVisitor() |
520 visitor = TextVisitor() |
533 visitor.visit(self.__tree) |
521 visitor.visit(self.__tree) |
534 for node in visitor.nodes: |
522 for node in visitor.nodes: |
535 text = node.s |
523 text = node.s |
536 if sys.version_info[0] > 2 and isinstance(text, bytes): |
524 if isinstance(text, bytes): |
537 try: |
525 try: |
538 text = text.decode(coding) |
526 text = text.decode(coding) |
539 except UnicodeDecodeError: |
527 except UnicodeDecodeError: |
540 continue |
528 continue |
541 fields, implicit, explicit = self.__getFields(text) |
529 fields, implicit, explicit = self.__getFields(text) |
709 element.col_offset, |
697 element.col_offset, |
710 "M131", element.id) |
698 "M131", element.id) |
711 elif any(isinstance(node, functionDef) |
699 elif any(isinstance(node, functionDef) |
712 for functionDef in functionDefs): |
700 for functionDef in functionDefs): |
713 # (asynchronous) function definition |
701 # (asynchronous) function definition |
714 if sys.version_info >= (3, 0): |
702 for arg in node.args.args: |
715 for arg in node.args.args: |
703 if ( |
716 if ( |
704 isinstance(arg, ast.arg) and |
717 isinstance(arg, ast.arg) and |
705 arg.arg in self.__builtins |
718 arg.arg in self.__builtins |
706 ): |
719 ): |
707 self.__error(arg.lineno - 1, arg.col_offset, |
720 self.__error(arg.lineno - 1, arg.col_offset, |
708 "M132", arg.arg) |
721 "M132", arg.arg) |
|
722 else: |
|
723 for arg in node.args.args: |
|
724 if ( |
|
725 isinstance(arg, ast.Name) and |
|
726 arg.id in self.__builtins |
|
727 ): |
|
728 self.__error(arg.lineno - 1, arg.col_offset, |
|
729 "M132", arg.id) |
|
730 |
709 |
731 def __checkComprehensions(self): |
710 def __checkComprehensions(self): |
732 """ |
711 """ |
733 Private method to check some comprehension related things. |
712 Private method to check some comprehension related things. |
734 """ |
713 """ |
960 |
939 |
961 def __checkDateTime(self): |
940 def __checkDateTime(self): |
962 """ |
941 """ |
963 Private method to check use of naive datetime functions. |
942 Private method to check use of naive datetime functions. |
964 """ |
943 """ |
965 if sys.version_info[0] >= 3: |
944 # step 1: generate an augmented node tree containing parent info |
966 # this check is only performed for Python 3 |
945 # for each child node |
967 |
946 tree = self.__generateTree() |
968 # step 1: generate an augmented node tree containing parent info |
947 for node in ast.walk(tree): |
969 # for each child node |
948 for childNode in ast.iter_child_nodes(node): |
970 tree = self.__generateTree() |
949 childNode._dtCheckerParent = node |
971 for node in ast.walk(tree): |
950 |
972 for childNode in ast.iter_child_nodes(node): |
951 # step 2: perform checks and report issues |
973 childNode._dtCheckerParent = node |
952 visitor = DateTimeVisitor() |
974 |
953 visitor.visit(tree) |
975 # step 2: perform checks and report issues |
954 for violation in visitor.violations: |
976 visitor = DateTimeVisitor() |
955 node = violation[0] |
977 visitor.visit(tree) |
956 reason = violation[1] |
978 for violation in visitor.violations: |
957 self.__error(node.lineno - 1, node.col_offset, reason) |
979 node = violation[0] |
|
980 reason = violation[1] |
|
981 self.__error(node.lineno - 1, node.col_offset, reason) |
|
982 |
958 |
983 def __checkSysVersion(self): |
959 def __checkSysVersion(self): |
984 """ |
960 """ |
985 Private method to check the use of sys.version and sys.version_info. |
961 Private method to check the use of sys.version and sys.version_info. |
986 """ |
962 """ |
1382 Public method to handle a function call. |
1358 Public method to handle a function call. |
1383 |
1359 |
1384 @param node reference to the node to be processed |
1360 @param node reference to the node to be processed |
1385 @type ast.Call |
1361 @type ast.Call |
1386 """ |
1362 """ |
1387 if sys.version_info >= (3, 0): |
1363 validPaths = ("six", "future.utils", "builtins") |
1388 validPaths = ("six", "future.utils", "builtins") |
1364 methodsDict = { |
1389 methodsDict = { |
1365 "M521": ("iterkeys", "itervalues", "iteritems", "iterlists"), |
1390 "M521": ("iterkeys", "itervalues", "iteritems", "iterlists"), |
1366 "M522": ("viewkeys", "viewvalues", "viewitems", "viewlists"), |
1391 "M522": ("viewkeys", "viewvalues", "viewitems", "viewlists"), |
1367 "M523": ("next",), |
1392 "M523": ("next",), |
1368 } |
1393 } |
|
1394 else: |
|
1395 validPaths = () |
|
1396 methodsDict = {} |
|
1397 |
1369 |
1398 if isinstance(node.func, ast.Attribute): |
1370 if isinstance(node.func, ast.Attribute): |
1399 for code, methods in methodsDict.items(): |
1371 for code, methods in methodsDict.items(): |
1400 if node.func.attr in methods: |
1372 if node.func.attr in methods: |
1401 callPath = ".".join(composeCallPath(node.func.value)) |
1373 callPath = ".".join(composeCallPath(node.func.value)) |
1448 @param node reference to the node to be processed |
1420 @param node reference to the node to be processed |
1449 @type ast.Attribute |
1421 @type ast.Attribute |
1450 """ |
1422 """ |
1451 callPath = list(composeCallPath(node)) |
1423 callPath = list(composeCallPath(node)) |
1452 |
1424 |
1453 if '.'.join(callPath) == 'sys.maxint' and sys.version_info >= (3, 0): |
1425 if '.'.join(callPath) == 'sys.maxint': |
1454 self.violations.append((node, "M504")) |
1426 self.violations.append((node, "M504")) |
1455 |
1427 |
1456 elif ( |
1428 elif ( |
1457 len(callPath) == 2 and |
1429 len(callPath) == 2 and |
1458 callPath[1] == 'message' and |
1430 callPath[1] == 'message' |
1459 sys.version_info >= (2, 6) |
|
1460 ): |
1431 ): |
1461 name = callPath[0] |
1432 name = callPath[0] |
1462 for elem in reversed(self.__nodeStack[:-1]): |
1433 for elem in reversed(self.__nodeStack[:-1]): |
1463 if isinstance(elem, ast.ExceptHandler) and elem.name == name: |
1434 if isinstance(elem, ast.ExceptHandler) and elem.name == name: |
1464 self.violations.append((node, "M505")) |
1435 self.violations.append((node, "M505")) |
1473 """ |
1444 """ |
1474 if isinstance(self.__nodeStack[-2], ast.ClassDef): |
1445 if isinstance(self.__nodeStack[-2], ast.ClassDef): |
1475 # By using 'hasattr' below we're ignoring starred arguments, slices |
1446 # By using 'hasattr' below we're ignoring starred arguments, slices |
1476 # and tuples for simplicity. |
1447 # and tuples for simplicity. |
1477 assignTargets = {t.id for t in node.targets if hasattr(t, 'id')} |
1448 assignTargets = {t.id for t in node.targets if hasattr(t, 'id')} |
1478 if '__metaclass__' in assignTargets and sys.version_info >= (3, 0): |
1449 if '__metaclass__' in assignTargets: |
1479 self.violations.append((node, "M524")) |
1450 self.violations.append((node, "M524")) |
1480 |
1451 |
1481 elif len(node.targets) == 1: |
1452 elif len(node.targets) == 1: |
1482 target = node.targets[0] |
1453 target = node.targets[0] |
1483 if ( |
1454 if ( |
1783 |
1754 |
1784 @param node reference to the node to check |
1755 @param node reference to the node to check |
1785 @type ast.AST |
1756 @type ast.AST |
1786 @return flag indicating the node contains a None value |
1757 @return flag indicating the node contains a None value |
1787 """ |
1758 """ |
1788 if sys.version_info[0] > 2: |
1759 return ( |
1789 return ( |
1760 AstUtilities.isNameConstant(node) and |
1790 AstUtilities.isNameConstant(node) and |
1761 AstUtilities.getValue(node) is None |
1791 AstUtilities.getValue(node) is None |
1762 ) |
1792 ) |
|
1793 else: |
|
1794 return isinstance(node, ast.Name) and node.id == "None" |
|
1795 |
1763 |
1796 def __resultExists(self): |
1764 def __resultExists(self): |
1797 """ |
1765 """ |
1798 Private method to check the existance of a return result. |
1766 Private method to check the existance of a return result. |
1799 |
1767 |
1848 return |
1816 return |
1849 |
1817 |
1850 try: |
1818 try: |
1851 okNodes = (ast.Return, ast.Raise, ast.While, ast.Try) |
1819 okNodes = (ast.Return, ast.Raise, ast.While, ast.Try) |
1852 except AttributeError: |
1820 except AttributeError: |
1853 # Py2 |
|
1854 okNodes = (ast.Return, ast.Raise, ast.While) |
1821 okNodes = (ast.Return, ast.Raise, ast.While) |
1855 if not isinstance(node, okNodes): |
1822 if not isinstance(node, okNodes): |
1856 self.violations.append((node, "M833")) |
1823 self.violations.append((node, "M833")) |
1857 |
1824 |
1858 def __checkUnnecessaryAssign(self, node): |
1825 def __checkUnnecessaryAssign(self, node): |