src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py

branch
eric7
changeset 10362
cfa7034cccf6
parent 10361
e6ff9a4f6ee5
child 10371
1f54843e8152
equal deleted inserted replaced
10361:e6ff9a4f6ee5 10362:cfa7034cccf6
180 "M623", 180 "M623",
181 "M624", 181 "M624",
182 "M625", 182 "M625",
183 "M631", 183 "M631",
184 "M632", 184 "M632",
185 ## Logging
186 "M651",
187 "M652",
188 "M653",
189 "M654",
190 "M655",
191 ## Future statements 185 ## Future statements
192 "M701", 186 "M701",
193 "M702", 187 "M702",
194 ## Gettext 188 ## Gettext
195 "M711", 189 "M711",
398 "M625", 392 "M625",
399 "M631", 393 "M631",
400 "M632", 394 "M632",
401 ), 395 ),
402 ), 396 ),
403 (self.__checkLogging, ("M651", "M652", "M653", "M654", "M655")),
404 (self.__checkFuture, ("M701", "M702")), 397 (self.__checkFuture, ("M701", "M702")),
405 (self.__checkGettext, ("M711",)), 398 (self.__checkGettext, ("M711",)),
406 (self.__checkPrintStatements, ("M801",)), 399 (self.__checkPrintStatements, ("M801",)),
407 (self.__checkTuple, ("M811",)), 400 (self.__checkTuple, ("M811",)),
408 (self.__checkMutableDefault, ("M821", "M822")), 401 (self.__checkMutableDefault, ("M821", "M822")),
1276 "M201", 1269 "M201",
1277 key2.value, 1270 key2.value,
1278 key1.value, 1271 key1.value,
1279 ) 1272 )
1280 1273
1281 def __checkLogging(self):
1282 """
1283 Private method to check logging statements.
1284 """
1285 visitor = LoggingVisitor()
1286 visitor.visit(self.__tree)
1287 for node, reason in visitor.violations:
1288 self.__error(node.lineno - 1, node.col_offset, reason)
1289
1290 def __checkGettext(self): 1274 def __checkGettext(self):
1291 """ 1275 """
1292 Private method to check the 'gettext' import statement. 1276 Private method to check the 'gettext' import statement.
1293 """ 1277 """
1294 for node in ast.walk(self.__tree): 1278 for node in ast.walk(self.__tree):
1435 1419
1436 if propertyCount > 1: 1420 if propertyCount > 1:
1437 self.__error(node.lineno - 1, node.col_offset, "M217", node.name) 1421 self.__error(node.lineno - 1, node.col_offset, "M217", node.name)
1438 1422
1439 ####################################################################### 1423 #######################################################################
1440 ## The following method check for implicitly concatenated strings 1424 ## The following methods check for implicitly concatenated strings.
1441 ## 1425 ##
1442 ## These methods are adapted from: flake8-implicit-str-concat v0.4.0 1426 ## These methods are adapted from: flake8-implicit-str-concat v0.4.0
1443 ## Original: Copyright (c) 2023 Dylan Turner 1427 ## Original: Copyright (c) 2023 Dylan Turner
1444 ####################################################################### 1428 #######################################################################
1445 1429
1656 and node.args 1640 and node.args
1657 and AstUtilities.isBaseString(node.args[0]) 1641 and AstUtilities.isBaseString(node.args[0])
1658 ): 1642 ):
1659 self.calls[node.args[0]] = (node, True) 1643 self.calls[node.args[0]] = (node, True)
1660 super().generic_visit(node) 1644 super().generic_visit(node)
1661
1662
1663 class LoggingVisitor(ast.NodeVisitor):
1664 """
1665 Class implementing a node visitor to check logging statements.
1666 """
1667
1668 LoggingLevels = {
1669 "debug",
1670 "critical",
1671 "error",
1672 "info",
1673 "warn",
1674 "warning",
1675 }
1676
1677 def __init__(self):
1678 """
1679 Constructor
1680 """
1681 super().__init__()
1682
1683 self.__currentLoggingCall = None
1684 self.__currentLoggingArgument = None
1685 self.__currentLoggingLevel = None
1686 self.__currentExtraKeyword = None
1687 self.violations = []
1688
1689 def __withinLoggingStatement(self):
1690 """
1691 Private method to check, if we are inside a logging statement.
1692
1693 @return flag indicating we are inside a logging statement
1694 @rtype bool
1695 """
1696 return self.__currentLoggingCall is not None
1697
1698 def __withinLoggingArgument(self):
1699 """
1700 Private method to check, if we are inside a logging argument.
1701
1702 @return flag indicating we are inside a logging argument
1703 @rtype bool
1704 """
1705 return self.__currentLoggingArgument is not None
1706
1707 def __withinExtraKeyword(self, node):
1708 """
1709 Private method to check, if we are inside the extra keyword.
1710
1711 @param node reference to the node to be checked
1712 @type ast.keyword
1713 @return flag indicating we are inside the extra keyword
1714 @rtype bool
1715 """
1716 return (
1717 self.__currentExtraKeyword is not None
1718 and self.__currentExtraKeyword != node
1719 )
1720
1721 def __detectLoggingLevel(self, node):
1722 """
1723 Private method to decide whether an AST Call is a logging call.
1724
1725 @param node reference to the node to be processed
1726 @type ast.Call
1727 @return logging level
1728 @rtype str or None
1729 """
1730 with contextlib.suppress(AttributeError):
1731 if node.func.value.id == "warnings":
1732 return None
1733
1734 if node.func.attr in LoggingVisitor.LoggingLevels:
1735 return node.func.attr
1736
1737 return None
1738
1739 def __isFormatCall(self, node):
1740 """
1741 Private method to check if a function call uses format.
1742
1743 @param node reference to the node to be processed
1744 @type ast.Call
1745 @return flag indicating the function call uses format
1746 @rtype bool
1747 """
1748 try:
1749 return node.func.attr == "format"
1750 except AttributeError:
1751 return False
1752
1753 def visit_Call(self, node):
1754 """
1755 Public method to handle a function call.
1756
1757 Every logging statement and string format is expected to be a function
1758 call.
1759
1760 @param node reference to the node to be processed
1761 @type ast.Call
1762 """
1763 # we are in a logging statement
1764 if (
1765 self.__withinLoggingStatement()
1766 and self.__withinLoggingArgument()
1767 and self.__isFormatCall(node)
1768 ):
1769 self.violations.append((node, "M651"))
1770 super().generic_visit(node)
1771 return
1772
1773 loggingLevel = self.__detectLoggingLevel(node)
1774
1775 if loggingLevel and self.__currentLoggingLevel is None:
1776 self.__currentLoggingLevel = loggingLevel
1777
1778 # we are in some other statement
1779 if loggingLevel is None:
1780 super().generic_visit(node)
1781 return
1782
1783 # we are entering a new logging statement
1784 self.__currentLoggingCall = node
1785
1786 if loggingLevel == "warn":
1787 self.violations.append((node, "M655"))
1788
1789 for index, child in enumerate(ast.iter_child_nodes(node)):
1790 if index == 1:
1791 self.__currentLoggingArgument = child
1792 if index > 1 and isinstance(child, ast.keyword) and child.arg == "extra":
1793 self.__currentExtraKeyword = child
1794
1795 super().visit(child)
1796
1797 self.__currentLoggingArgument = None
1798 self.__currentExtraKeyword = None
1799
1800 self.__currentLoggingCall = None
1801 self.__currentLoggingLevel = None
1802
1803 def visit_BinOp(self, node):
1804 """
1805 Public method to handle binary operations while processing the first
1806 logging argument.
1807
1808 @param node reference to the node to be processed
1809 @type ast.BinOp
1810 """
1811 if self.__withinLoggingStatement() and self.__withinLoggingArgument():
1812 # handle percent format
1813 if isinstance(node.op, ast.Mod):
1814 self.violations.append((node, "M652"))
1815
1816 # handle string concat
1817 if isinstance(node.op, ast.Add):
1818 self.violations.append((node, "M653"))
1819
1820 super().generic_visit(node)
1821
1822 def visit_JoinedStr(self, node):
1823 """
1824 Public method to handle f-string arguments.
1825
1826 @param node reference to the node to be processed
1827 @type ast.JoinedStr
1828 """
1829 if (
1830 self.__withinLoggingStatement()
1831 and any(isinstance(i, ast.FormattedValue) for i in node.values)
1832 and self.__withinLoggingArgument()
1833 ):
1834 self.violations.append((node, "M654"))
1835
1836 super().generic_visit(node)
1837 1645
1838 1646
1839 ####################################################################### 1647 #######################################################################
1840 ## BugBearVisitor 1648 ## BugBearVisitor
1841 ## 1649 ##

eric ide

mercurial