45 ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
47 ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
46 ## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
48 ## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
47 ## IN THE SOFTWARE. |
49 ## IN THE SOFTWARE. |
48 ############################################################################### |
50 ############################################################################### |
49 |
51 |
50 BOOL_CONST_TYPES = (ast.Constant, ast.NameConstant) |
|
51 AST_CONST_TYPES = (ast.Constant, ast.NameConstant, ast.Str, ast.Num) |
|
52 STR_TYPES = (ast.Constant, ast.Str) |
|
53 |
|
54 |
52 |
55 class SimplifyNodeVisitor(ast.NodeVisitor): |
53 class SimplifyNodeVisitor(ast.NodeVisitor): |
56 """ |
54 """ |
57 Class to traverse the AST node tree and check for code that can be |
55 Class to traverse the AST node tree and check for code that can be |
58 simplified. |
56 simplified. |
293 @return flag indicating a constant increase |
291 @return flag indicating a constant increase |
294 @rtype bool |
292 @rtype bool |
295 """ |
293 """ |
296 return isinstance(expr.op, ast.Add) and ( |
294 return isinstance(expr.op, ast.Add) and ( |
297 (isinstance(expr.value, ast.Constant) and expr.value.value == 1) |
295 (isinstance(expr.value, ast.Constant) and expr.value.value == 1) |
298 or (isinstance(expr.value, ast.Num) and expr.value.n == 1) |
|
299 ) |
296 ) |
300 |
297 |
301 def __getIfBodyPairs(self, node): |
298 def __getIfBodyPairs(self, node): |
302 """ |
299 """ |
303 Private method to extract a list of pairs of test and body for an |
300 Private method to extract a list of pairs of test and body for an |
524 # else: |
521 # else: |
525 # return False |
522 # return False |
526 if not ( |
523 if not ( |
527 len(node.body) != 1 |
524 len(node.body) != 1 |
528 or not isinstance(node.body[0], ast.Return) |
525 or not isinstance(node.body[0], ast.Return) |
529 or not isinstance(node.body[0].value, BOOL_CONST_TYPES) |
526 or not isinstance(node.body[0].value, ast.Constant) |
530 or not ( |
527 or not ( |
531 node.body[0].value.value is True or node.body[0].value.value is False |
528 node.body[0].value.value is True or node.body[0].value.value is False |
532 ) |
529 ) |
533 or len(node.orelse) != 1 |
530 or len(node.orelse) != 1 |
534 or not isinstance(node.orelse[0], ast.Return) |
531 or not isinstance(node.orelse[0], ast.Return) |
535 or not isinstance(node.orelse[0].value, BOOL_CONST_TYPES) |
532 or not isinstance(node.orelse[0].value, ast.Constant) |
536 or not ( |
533 or not ( |
537 node.orelse[0].value.value is True |
534 node.orelse[0].value.value is True |
538 or node.orelse[0].value.value is False |
535 or node.orelse[0].value.value is False |
539 ) |
536 ) |
540 ): |
537 ): |
778 if ( |
775 if ( |
779 len(node.body) == 1 |
776 len(node.body) == 1 |
780 and isinstance(node.body[0], ast.If) |
777 and isinstance(node.body[0], ast.If) |
781 and len(node.body[0].body) == 1 |
778 and len(node.body[0].body) == 1 |
782 and isinstance(node.body[0].body[0], ast.Return) |
779 and isinstance(node.body[0].body[0], ast.Return) |
783 and isinstance(node.body[0].body[0].value, BOOL_CONST_TYPES) |
780 and isinstance(node.body[0].body[0].value, ast.Constant) |
784 and hasattr(node.body[0].body[0].value, "value") |
781 and hasattr(node.body[0].body[0].value, "value") |
785 and isinstance(node.next_sibling, ast.Return) |
782 and isinstance(node.next_sibling, ast.Return) |
786 ): |
783 ): |
787 check = unparse(node.body[0].test) |
784 check = unparse(node.body[0].test) |
788 target = unparse(node.target) |
785 target = unparse(node.target) |
822 and node.value.value.value.id == "os" |
819 and node.value.value.value.id == "os" |
823 and node.value.value.attr == "environ" |
820 and node.value.value.attr == "environ" |
824 and ( |
821 and ( |
825 ( |
822 ( |
826 isinstance(node.value.slice, ast.Index) |
823 isinstance(node.value.slice, ast.Index) |
827 and isinstance(node.value.slice.value, STR_TYPES) |
824 and isinstance(node.value.slice.value, ast.Constant) |
828 ) |
825 ) |
829 or isinstance(node.value.slice, ast.Constant) |
826 or isinstance(node.value.slice, ast.Constant) |
830 ) |
827 ) |
831 ) |
828 ) |
832 if isIndexCall: |
829 if isIndexCall: |
833 subscript = node.value |
830 subscript = node.value |
834 slice_ = subscript.slice |
831 slice_ = subscript.slice |
835 if isinstance(slice_, ast.Index): |
832 if isinstance(slice_, ast.Index): |
836 # Python < 3.9 |
833 # Python < 3.9 |
837 stringPart = slice_.value # type: ignore |
834 stringPart = slice_.value # type: ignore |
838 if isinstance(stringPart, ast.Str): |
835 envName = stringPart.value |
839 envName = stringPart.s # Python 3.6 / 3.7 fallback |
|
840 else: |
|
841 envName = stringPart.value |
|
842 elif isinstance(slice_, ast.Constant): |
836 elif isinstance(slice_, ast.Constant): |
843 # Python 3.9 |
837 # Python 3.9 |
844 envName = slice_.value |
838 envName = slice_.value |
845 |
839 |
846 # Check if this has a change |
840 # Check if this has a change |
853 and isinstance(node.value.func.value.value, ast.Name) |
847 and isinstance(node.value.func.value.value, ast.Name) |
854 and node.value.func.value.value.id == "os" |
848 and node.value.func.value.value.id == "os" |
855 and node.value.func.value.attr == "environ" |
849 and node.value.func.value.attr == "environ" |
856 and node.value.func.attr == "get" |
850 and node.value.func.attr == "get" |
857 and len(node.value.args) in [1, 2] |
851 and len(node.value.args) in [1, 2] |
858 and isinstance(node.value.args[0], STR_TYPES) |
852 and isinstance(node.value.args[0], ast.Constant) |
859 ) |
853 ) |
860 if isGetCall: |
854 if isGetCall: |
861 call = node.value |
855 call = node.value |
862 stringPart = call.args[0] |
856 stringPart = call.args[0] |
863 if isinstance(stringPart, ast.Str): |
857 envName = stringPart.value |
864 envName = stringPart.s # Python 3.6 / 3.7 fallback |
|
865 else: |
|
866 envName = stringPart.value |
|
867 # Check if this has a change |
858 # Check if this has a change |
868 hasChange = envName != envName.upper() |
859 hasChange = envName != envName.upper() |
869 if not (isIndexCall or isGetCall) or not hasChange: |
860 if not (isIndexCall or isGetCall) or not hasChange: |
870 return |
861 return |
871 if isIndexCall: |
862 if isIndexCall: |
1004 isinstance(node.test, ast.Compare) |
995 isinstance(node.test, ast.Compare) |
1005 and isinstance(node.test.left, ast.Name) |
996 and isinstance(node.test.left, ast.Name) |
1006 and len(node.test.ops) == 1 |
997 and len(node.test.ops) == 1 |
1007 and isinstance(node.test.ops[0], ast.Eq) |
998 and isinstance(node.test.ops[0], ast.Eq) |
1008 and len(node.test.comparators) == 1 |
999 and len(node.test.comparators) == 1 |
1009 and isinstance(node.test.comparators[0], AST_CONST_TYPES) |
1000 and isinstance(node.test.comparators[0], ast.Constant) |
1010 and len(node.body) == 1 |
1001 and len(node.body) == 1 |
1011 and isinstance(node.body[0], ast.Return) |
1002 and isinstance(node.body[0], ast.Return) |
1012 and len(node.orelse) == 1 |
1003 and len(node.orelse) == 1 |
1013 and isinstance(node.orelse[0], ast.If) |
1004 and isinstance(node.orelse[0], ast.If) |
1014 ): |
1005 ): |
1017 elseValue = None |
1008 elseValue = None |
1018 if node.body[0].value is not None: |
1009 if node.body[0].value is not None: |
1019 bodyValueStr = unparse(node.body[0].value).strip("'") |
1010 bodyValueStr = unparse(node.body[0].value).strip("'") |
1020 else: |
1011 else: |
1021 bodyValueStr = "None" |
1012 bodyValueStr = "None" |
1022 if isinstance(node.test.comparators[0], ast.Str): |
1013 if AstUtilities.isString(node.test.comparators[0]): |
1023 value = ( |
1014 value = ( |
1024 bodyValueStr |
1015 bodyValueStr |
1025 if bodyValueStr[0] == '"' and bodyValueStr[-1] == '"' |
1016 if bodyValueStr[0] == '"' and bodyValueStr[-1] == '"' |
1026 else bodyValueStr[1:-1] |
1017 else bodyValueStr[1:-1] |
1027 ) |
1018 ) |
1028 keyValuePairs = {node.test.comparators[0].s: value} |
1019 keyValuePairs = {node.test.comparators[0].s: value} |
1029 elif isinstance(node.test.comparators[0], ast.Num): |
|
1030 keyValuePairs = {node.test.comparators[0].n: bodyValueStr} |
|
1031 else: |
1020 else: |
1032 keyValuePairs = {node.test.comparators[0].value: bodyValueStr} |
1021 keyValuePairs = {node.test.comparators[0].value: bodyValueStr} |
1033 while child: |
1022 while child: |
1034 if not ( |
1023 if not ( |
1035 isinstance(child.test, ast.Compare) |
1024 isinstance(child.test, ast.Compare) |
1036 and isinstance(child.test.left, ast.Name) |
1025 and isinstance(child.test.left, ast.Name) |
1037 and child.test.left.id == variable.id |
1026 and child.test.left.id == variable.id |
1038 and len(child.test.ops) == 1 |
1027 and len(child.test.ops) == 1 |
1039 and isinstance(child.test.ops[0], ast.Eq) |
1028 and isinstance(child.test.ops[0], ast.Eq) |
1040 and len(child.test.comparators) == 1 |
1029 and len(child.test.comparators) == 1 |
1041 and isinstance(child.test.comparators[0], AST_CONST_TYPES) |
1030 and isinstance(child.test.comparators[0], ast.Constant) |
1042 and len(child.body) == 1 |
1031 and len(child.body) == 1 |
1043 and isinstance(child.body[0], ast.Return) |
1032 and isinstance(child.body[0], ast.Return) |
1044 and len(child.orelse) <= 1 |
1033 and len(child.orelse) <= 1 |
1045 ): |
1034 ): |
1046 return |
1035 return |
1047 |
1036 |
1048 returnCall = child.body[0] |
1037 returnCall = child.body[0] |
1049 if isinstance(returnCall.value, ast.Call): |
1038 if isinstance(returnCall.value, ast.Call): |
1050 return |
1039 return |
1051 |
1040 |
1052 if isinstance(child.test.comparators[0], ast.Str): |
1041 key = child.test.comparators[0].value |
1053 key = child.test.comparators[0].s |
|
1054 elif isinstance(child.test.comparators[0], ast.Num): |
|
1055 key = child.test.comparators[0].n |
|
1056 else: |
|
1057 key = child.test.comparators[0].value |
|
1058 |
1042 |
1059 value = unparse(child.body[0].value) |
1043 value = unparse(child.body[0].value) |
1060 if value[0] == '"' and value[-1] == '"': |
1044 if value[0] == '"' and value[-1] == '"': |
1061 value = value[1:-1] |
1045 value = value[1:-1] |
1062 keyValuePairs[key] = value |
1046 keyValuePairs[key] = value |
1535 @param node reference to the AST node to be checked |
1519 @param node reference to the AST node to be checked |
1536 @type ast.IfExp |
1520 @type ast.IfExp |
1537 """ |
1521 """ |
1538 # True if a else False |
1522 # True if a else False |
1539 if ( |
1523 if ( |
1540 isinstance(node.body, BOOL_CONST_TYPES) |
1524 isinstance(node.body, ast.Constant) |
1541 and node.body.value is True |
1525 and node.body.value is True |
1542 and isinstance(node.orelse, BOOL_CONST_TYPES) |
1526 and isinstance(node.orelse, ast.Constant) |
1543 and node.orelse.value is False |
1527 and node.orelse.value is False |
1544 ): |
1528 ): |
1545 cond = unparse(node.test) |
1529 cond = unparse(node.test) |
1546 if isinstance(node.test, ast.Name): |
1530 if isinstance(node.test, ast.Name): |
1547 newCond = "bool({0})".format(cond) |
1531 newCond = "bool({0})".format(cond) |
1556 @param node reference to the AST node to be checked |
1540 @param node reference to the AST node to be checked |
1557 @type ast.IfExp |
1541 @type ast.IfExp |
1558 """ |
1542 """ |
1559 # False if a else True |
1543 # False if a else True |
1560 if ( |
1544 if ( |
1561 isinstance(node.body, BOOL_CONST_TYPES) |
1545 isinstance(node.body, ast.Constant) |
1562 and node.body.value is False |
1546 and node.body.value is False |
1563 and isinstance(node.orelse, BOOL_CONST_TYPES) |
1547 and isinstance(node.orelse, ast.Constant) |
1564 and node.orelse.value is True |
1548 and node.orelse.value is True |
1565 ): |
1549 ): |
1566 cond = unparse(node.test) |
1550 cond = unparse(node.test) |
1567 if isinstance(node.test, ast.Name): |
1551 if isinstance(node.test, ast.Name): |
1568 newCond = "not {0}".format(cond) |
1552 newCond = "not {0}".format(cond) |
1646 @type ast.BoolOp |
1630 @type ast.BoolOp |
1647 """ |
1631 """ |
1648 # a or True |
1632 # a or True |
1649 if isinstance(node.op, ast.Or): |
1633 if isinstance(node.op, ast.Or): |
1650 for exp in node.values: |
1634 for exp in node.values: |
1651 if isinstance(exp, BOOL_CONST_TYPES) and exp.value is True: |
1635 if isinstance(exp, ast.Constant) and exp.value is True: |
1652 self.__error(node.lineno - 1, node.col_offset, "Y223") |
1636 self.__error(node.lineno - 1, node.col_offset, "Y223") |
1653 |
1637 |
1654 def __check224(self, node): |
1638 def __check224(self, node): |
1655 """ |
1639 """ |
1656 Private method to check for calls of the type "... and False". |
1640 Private method to check for calls of the type "... and False". |
1659 @type ast.BoolOp |
1643 @type ast.BoolOp |
1660 """ |
1644 """ |
1661 # a and False |
1645 # a and False |
1662 if isinstance(node.op, ast.And): |
1646 if isinstance(node.op, ast.And): |
1663 for exp in node.values: |
1647 for exp in node.values: |
1664 if isinstance(exp, BOOL_CONST_TYPES) and exp.value is False: |
1648 if isinstance(exp, ast.Constant) and exp.value is False: |
1665 self.__error(node.lineno - 1, node.col_offset, "Y224") |
1649 self.__error(node.lineno - 1, node.col_offset, "Y224") |
1666 |
1650 |
1667 def __check301(self, node): |
1651 def __check301(self, node): |
1668 """ |
1652 """ |
1669 Private method to check for Yoda conditions. |
1653 Private method to check for Yoda conditions. |
1671 @param node reference to the AST node to be checked |
1655 @param node reference to the AST node to be checked |
1672 @type ast.Compare |
1656 @type ast.Compare |
1673 """ |
1657 """ |
1674 # 42 == age |
1658 # 42 == age |
1675 if ( |
1659 if ( |
1676 isinstance(node.left, AST_CONST_TYPES) |
1660 isinstance(node.left, ast.Constant) |
1677 and len(node.ops) == 1 |
1661 and len(node.ops) == 1 |
1678 and isinstance(node.ops[0], ast.Eq) |
1662 and isinstance(node.ops[0], ast.Eq) |
1679 ): |
1663 ): |
1680 left = unparse(node.left) |
1664 left = unparse(node.left) |
1681 isPy37Str = isinstance(node.left, ast.Str) |
1665 isStr = isinstance(node.left, ast.Constant) and isinstance( |
1682 isPy38Str = isinstance(node.left, ast.Constant) and isinstance( |
|
1683 node.left.value, str |
1666 node.left.value, str |
1684 ) |
1667 ) |
1685 if isPy37Str or isPy38Str: |
1668 if isStr: |
1686 left = f"'{left}'" |
1669 left = f"'{left}'" |
1687 right = unparse(node.comparators[0]) |
1670 right = unparse(node.comparators[0]) |
1688 self.__error(node.lineno - 1, node.col_offset, "Y301", left, right) |
1671 self.__error(node.lineno - 1, node.col_offset, "Y301", left, right) |
1689 |
1672 |
1690 def __check401(self, node): |
1673 def __check401(self, node): |
1775 @type ast.Call |
1758 @type ast.Call |
1776 """ |
1759 """ |
1777 if ( |
1760 if ( |
1778 isinstance(node.func, ast.Attribute) |
1761 isinstance(node.func, ast.Attribute) |
1779 and node.func.attr == "split" |
1762 and node.func.attr == "split" |
1780 and isinstance(node.func.value, (ast.Str, ast.Constant)) |
1763 and isinstance(node.func.value, ast.Constant) |
1781 ): |
1764 ): |
1782 if isinstance(node.func.value, ast.Constant): |
1765 value = node.func.value.value |
1783 value = node.func.value.value |
|
1784 else: |
|
1785 value = node.func.value.s |
|
1786 |
1766 |
1787 expected = json.dumps(value.split()) |
1767 expected = json.dumps(value.split()) |
1788 actual = unparse(node.func.value) + ".split()" |
1768 actual = unparse(node.func.value) + ".split()" |
1789 self.__error(node.lineno - 1, node.col_offset, "Y905", expected, actual) |
1769 self.__error(node.lineno - 1, node.col_offset, "Y905", expected, actual) |
1790 |
1770 |
1809 and arg.func.attr == "join" |
1789 and arg.func.attr == "join" |
1810 ): |
1790 ): |
1811 names += getOsPathJoinArgs(arg) |
1791 names += getOsPathJoinArgs(arg) |
1812 elif isinstance(arg, ast.Name): |
1792 elif isinstance(arg, ast.Name): |
1813 names.append(arg.id) |
1793 names.append(arg.id) |
1814 elif isinstance(arg, ast.Str): |
1794 elif AstUtilities.isString(arg): |
1815 names.append(f"'{arg.s}'") |
1795 names.append(f"'{arg.value}'") |
1816 return names |
1796 return names |
1817 |
1797 |
1818 if ( |
1798 if ( |
1819 isinstance(node.func, ast.Attribute) |
1799 isinstance(node.func, ast.Attribute) |
1820 and isinstance(node.func.value, ast.Attribute) |
1800 and isinstance(node.func.value, ast.Attribute) |