src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Simplify/SimplifyNodeVisitor.py

branch
eric7
changeset 10169
0f70a4ef4592
parent 10116
4a619fb7bd09
child 10175
57ed3cb66e9a
equal deleted inserted replaced
10168:8312e0e76795 10169:0f70a4ef4592
16 try: 16 try:
17 from ast import unparse 17 from ast import unparse
18 except ImportError: 18 except ImportError:
19 # Python < 3.9 19 # Python < 3.9
20 from ast_unparse import unparse 20 from ast_unparse import unparse
21
22 import AstUtilities
21 23
22 ############################################################################### 24 ###############################################################################
23 ## The following code is derived from the flake8-simplify package (v0.20.0). 25 ## The following code is derived from the flake8-simplify package (v0.20.0).
24 ## 26 ##
25 ## Original License: 27 ## Original License:
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)
1862 return 1842 return
1863 1843
1864 hasNone = False 1844 hasNone = False
1865 others = [] 1845 others = []
1866 for elt in tupleVar.elts: 1846 for elt in tupleVar.elts:
1867 if isinstance(elt, BOOL_CONST_TYPES) and elt.value is None: 1847 if isinstance(elt, ast.Constant) and elt.value is None:
1868 hasNone = True 1848 hasNone = True
1869 else: 1849 else:
1870 others.append(elt) 1850 others.append(elt)
1871 1851
1872 if len(others) == 1 and hasNone: 1852 if len(others) == 1 and hasNone:
1907 return 1887 return
1908 1888
1909 # check the argument value 1889 # check the argument value
1910 if not ( 1890 if not (
1911 len(node.args) == 2 1891 len(node.args) == 2
1912 and isinstance(node.args[1], BOOL_CONST_TYPES) 1892 and isinstance(node.args[1], ast.Constant)
1913 and node.args[1].value is None 1893 and node.args[1].value is None
1914 ): 1894 ):
1915 return 1895 return
1916 1896
1917 actual = unparse(node) 1897 actual = unparse(node)

eric ide

mercurial