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

branch
eric7
changeset 11138
1f743bad6fd3
parent 11134
3243a66db84a
child 11140
b823386f7591
equal deleted inserted replaced
11137:a90284948331 11138:1f743bad6fd3
15 import math 15 import math
16 import re 16 import re
17 import sys 17 import sys
18 import tokenize 18 import tokenize
19 19
20 from collections import defaultdict, namedtuple 20 from collections import Counter, defaultdict, namedtuple
21 from dataclasses import dataclass 21 from dataclasses import dataclass
22 from keyword import iskeyword 22 from keyword import iskeyword
23 from string import Formatter 23 from string import Formatter
24 24
25 try: 25 try:
205 "M535", 205 "M535",
206 "M536", 206 "M536",
207 "M537", 207 "M537",
208 "M539", 208 "M539",
209 "M540", 209 "M540",
210 "M541",
210 ## Bugbear, opininonated 211 ## Bugbear, opininonated
211 "M569", 212 "M569",
212 ## Bugbear++ 213 ## Bugbear++
213 "M581", 214 "M581",
214 "M582", 215 "M582",
418 "M535", 419 "M535",
419 "M536", 420 "M536",
420 "M537", 421 "M537",
421 "M539", 422 "M539",
422 "M540", 423 "M540",
424 "M541",
423 "M569", 425 "M569",
424 "M581", 426 "M581",
425 "M582", 427 "M582",
426 ), 428 ),
427 ), 429 ),
1637 1639
1638 1640
1639 ####################################################################### 1641 #######################################################################
1640 ## BugBearVisitor 1642 ## BugBearVisitor
1641 ## 1643 ##
1642 ## adapted from: flake8-bugbear v24.8.19 1644 ## adapted from: flake8-bugbear v24.12.12
1643 ## 1645 ##
1644 ## Original: Copyright (c) 2016 Łukasz Langa 1646 ## Original: Copyright (c) 2016 Łukasz Langa
1645 ####################################################################### 1647 #######################################################################
1646 1648
1647 BugBearContext = namedtuple("BugBearContext", ["node", "stack"]) 1649 BugBearContext = namedtuple("BugBearContext", ["node", "stack"])
1653 Class to hold the data for a caught exception. 1655 Class to hold the data for a caught exception.
1654 """ 1656 """
1655 1657
1656 name: str 1658 name: str
1657 hasNote: bool 1659 hasNote: bool
1660
1661
1662 class M541UnhandledKeyType:
1663 """
1664 Class to hold a dictionary key of a type that we do not check for duplicates.
1665 """
1666
1667
1668 class M541VariableKeyType:
1669 """
1670 Class to hold the name of a variable key type.
1671 """
1672
1673 def __init__(self, name):
1674 """
1675 Constructor
1676
1677 @param name name of the variable key type
1678 @type str
1679 """
1680 self.name = name
1658 1681
1659 1682
1660 class BugBearVisitor(ast.NodeVisitor): 1683 class BugBearVisitor(ast.NodeVisitor):
1661 """ 1684 """
1662 Class implementing a node visitor to check for various topics. 1685 Class implementing a node visitor to check for various topics.
1694 1717
1695 self.__M523Seen = set() 1718 self.__M523Seen = set()
1696 self.__M505Imports = set() 1719 self.__M505Imports = set()
1697 self.__M540CaughtException = None 1720 self.__M540CaughtException = None
1698 1721
1722 self.__inTryStar = ""
1723
1699 @property 1724 @property
1700 def nodeStack(self): 1725 def nodeStack(self):
1701 """ 1726 """
1702 Public method to get a reference to the most recent node stack. 1727 Public method to get a reference to the most recent node stack.
1703 1728
1840 ): 1865 ):
1841 exprList.extend(expr.value.elts) 1866 exprList.extend(expr.value.elts)
1842 continue 1867 continue
1843 yield expr 1868 yield expr
1844 1869
1845 def __checkRedundantExcepthandlers(self, names, node): 1870 def __checkRedundantExcepthandlers(self, names, node, inTryStar):
1846 """ 1871 """
1847 Private method to check for redundant exception types in an exception handler. 1872 Private method to check for redundant exception types in an exception handler.
1848 1873
1849 @param names list of exception types to be checked 1874 @param names list of exception types to be checked
1850 @type list of ast.Name 1875 @type list of ast.Name
1851 @param node reference to the exception handler node 1876 @param node reference to the exception handler node
1852 @type ast.ExceptionHandler 1877 @type ast.ExceptionHandler
1878 @param inTryStar character indicating an 'except*' handler
1879 @type str
1853 @return tuple containing the error data 1880 @return tuple containing the error data
1854 @rtype tuple of (ast.Node, str, str, str, str) 1881 @rtype tuple of (ast.Node, str, str, str, str)
1855 """ 1882 """
1856 redundantExceptions = { 1883 redundantExceptions = {
1857 "OSError": { 1884 "OSError": {
1896 ): 1923 ):
1897 good.remove(name) 1924 good.remove(name)
1898 if good != names: 1925 if good != names:
1899 desc = good[0] if len(good) == 1 else "({0})".format(", ".join(good)) 1926 desc = good[0] if len(good) == 1 else "({0})".format(", ".join(good))
1900 as_ = " as " + node.name if node.name is not None else "" 1927 as_ = " as " + node.name if node.name is not None else ""
1901 return (node, "M514", ", ".join(names), as_, desc) 1928 return (node, "M514", ", ".join(names), as_, desc, inTryStar)
1902 1929
1903 return None 1930 return None
1904 1931
1905 def __walkList(self, nodes): 1932 def __walkList(self, nodes):
1906 """ 1933 """
2041 if node.name is None: 2068 if node.name is None:
2042 self.__M540CaughtException = None 2069 self.__M540CaughtException = None
2043 else: 2070 else:
2044 self.__M540CaughtException = M540CaughtException(node.name, False) 2071 self.__M540CaughtException = M540CaughtException(node.name, False)
2045 2072
2046 names = self.__checkForM513_M529_M530(node) 2073 names = self.__checkForM513_M514_M529_M530(node)
2047 2074
2048 if "BaseException" in names and not ExceptBaseExceptionVisitor(node).reRaised(): 2075 if "BaseException" in names and not ExceptBaseExceptionVisitor(node).reRaised():
2049 self.violations.append((node, "M536")) 2076 self.violations.append((node, "M536"))
2050 2077
2051 self.generic_visit(node) 2078 self.generic_visit(node)
2301 2328
2302 self.generic_visit(node) 2329 self.generic_visit(node)
2303 2330
2304 def visit_Try(self, node): 2331 def visit_Try(self, node):
2305 """ 2332 """
2306 Public method to handle 'try' statements'. 2333 Public method to handle 'try' statements.
2307 2334
2308 @param node reference to the node to be processed 2335 @param node reference to the node to be processed
2309 @type ast.Try 2336 @type ast.Try
2310 """ 2337 """
2311 self.__checkForM512(node) 2338 self.__checkForM512(node)
2312 self.__checkForM525(node) 2339 self.__checkForM525(node)
2313 2340
2314 self.generic_visit(node) 2341 self.generic_visit(node)
2342
2343 def visit_TryStar(self, node):
2344 """
2345 Public method to handle 'except*' statements.
2346
2347 @param node reference to the node to be processed
2348 @type ast.TryStar
2349 """
2350 outerTryStar = self.__inTryStar
2351 self.__inTryStar = "*"
2352 self.visit_Try(node)
2353 self.__inTryStar = outerTryStar
2315 2354
2316 def visit_Compare(self, node): 2355 def visit_Compare(self, node):
2317 """ 2356 """
2318 Public method to handle comparison statements. 2357 Public method to handle comparison statements.
2319 2358
2403 2442
2404 @param node reference to the node to be processed 2443 @param node reference to the node to be processed
2405 @type ast.Set 2444 @type ast.Set
2406 """ 2445 """
2407 self.__checkForM533(node) 2446 self.__checkForM533(node)
2447
2448 self.generic_visit(node)
2449
2450 def visit_Dict(self, node):
2451 """
2452 Public method to check a dictionary.
2453
2454 @param node reference to the node to be processed
2455 @type ast.Dict
2456 """
2457 self.__checkForM541(node)
2408 2458
2409 self.generic_visit(node) 2459 self.generic_visit(node)
2410 2460
2411 def __checkForM505(self, node): 2461 def __checkForM505(self, node):
2412 """ 2462 """
2486 2536
2487 if isinstance(node, (ast.While, ast.For)): 2537 if isinstance(node, (ast.While, ast.For)):
2488 badNodeTypes = (ast.Return,) 2538 badNodeTypes = (ast.Return,)
2489 2539
2490 elif isinstance(node, badNodeTypes): 2540 elif isinstance(node, badNodeTypes):
2491 self.violations.append((node, "M512")) 2541 self.violations.append((node, "M512", self.__inTryStar))
2492 2542
2493 for child in ast.iter_child_nodes(node): 2543 for child in ast.iter_child_nodes(node):
2494 _loop(child, badNodeTypes) 2544 _loop(child, badNodeTypes)
2495 2545
2496 for child in node.finalbody: 2546 for child in node.finalbody:
2497 _loop(child, (ast.Return, ast.Continue, ast.Break)) 2547 _loop(child, (ast.Return, ast.Continue, ast.Break))
2498 2548
2499 def __checkForM513_M529_M530(self, node): 2549 def __checkForM513_M514_M529_M530(self, node):
2500 """ 2550 """
2501 Private method to check various exception handler situations. 2551 Private method to check various exception handler situations.
2502 2552
2503 @param node reference to the node to be processed 2553 @param node reference to the node to be processed
2504 @type ast.ExceptHandler 2554 @type ast.ExceptHandler
2522 else: 2572 else:
2523 badHandlers.append(handler) 2573 badHandlers.append(handler)
2524 if badHandlers: 2574 if badHandlers:
2525 self.violations.append((node, "M530")) 2575 self.violations.append((node, "M530"))
2526 if len(names) == 0 and not badHandlers and not ignoredHandlers: 2576 if len(names) == 0 and not badHandlers and not ignoredHandlers:
2527 self.violations.append((node, "M529")) 2577 self.violations.append((node, "M529", self.__inTryStar))
2528 elif ( 2578 elif (
2529 len(names) == 1 2579 len(names) == 1
2530 and not badHandlers 2580 and not badHandlers
2531 and not ignoredHandlers 2581 and not ignoredHandlers
2532 and isinstance(node.type, ast.Tuple) 2582 and isinstance(node.type, ast.Tuple)
2533 ): 2583 ):
2534 self.violations.append((node, "M513", *names)) 2584 self.violations.append((node, "M513", *names, self.__inTryStar))
2535 else: 2585 else:
2536 maybeError = self.__checkRedundantExcepthandlers(names, node) 2586 maybeError = self.__checkRedundantExcepthandlers(
2587 names, node, self.__inTryStar
2588 )
2537 if maybeError is not None: 2589 if maybeError is not None:
2538 self.violations.append(maybeError) 2590 self.violations.append(maybeError)
2539 return names 2591 return names
2540 2592
2541 def __checkForM515(self, node): 2593 def __checkForM515(self, node):
2848 if not any(map(isAbcClass, (*node.bases, *node.keywords))): 2900 if not any(map(isAbcClass, (*node.bases, *node.keywords))):
2849 return 2901 return
2850 2902
2851 for stmt in node.body: 2903 for stmt in node.body:
2852 # Ignore abc's that declares a class attribute that must be set 2904 # Ignore abc's that declares a class attribute that must be set
2853 if isinstance(stmt, (ast.AnnAssign, ast.Assign)): 2905 if isinstance(stmt, ast.AnnAssign) and stmt.value is None:
2854 hasAbstractMethod = True 2906 hasAbstractMethod = True
2855 continue 2907 continue
2856 2908
2857 # only check function defs 2909 # only check function defs
2858 if not isinstance(stmt, (ast.FunctionDef, ast.AsyncFunctionDef)): 2910 if not isinstance(stmt, (ast.FunctionDef, ast.AsyncFunctionDef)):
2895 seen.extend(uniques) 2947 seen.extend(uniques)
2896 2948
2897 # sort to have a deterministic output 2949 # sort to have a deterministic output
2898 duplicates = sorted({x for x in seen if seen.count(x) > 1}) 2950 duplicates = sorted({x for x in seen if seen.count(x) > 1})
2899 for duplicate in duplicates: 2951 for duplicate in duplicates:
2900 self.violations.append((node, "M525", duplicate)) 2952 self.violations.append((node, "M525", duplicate, self.__inTryStar))
2901 2953
2902 def __checkForM526(self, node): 2954 def __checkForM526(self, node):
2903 """ 2955 """
2904 Private method to check for Star-arg unpacking after keyword argument. 2956 Private method to check for Star-arg unpacking after keyword argument.
2905 2957
2933 and node.func.attr == "warn" 2985 and node.func.attr == "warn"
2934 and isinstance(node.func.value, ast.Name) 2986 and isinstance(node.func.value, ast.Name)
2935 and node.func.value.id == "warnings" 2987 and node.func.value.id == "warnings"
2936 and not any(kw.arg == "stacklevel" for kw in node.keywords) 2988 and not any(kw.arg == "stacklevel" for kw in node.keywords)
2937 and len(node.args) < 3 2989 and len(node.args) < 3
2990 and not any(isinstance(a, ast.Starred) for a in node.args)
2991 and not any(kw.arg is None for kw in node.keywords)
2938 ): 2992 ):
2939 self.violations.append((node, "M528")) 2993 self.violations.append((node, "M528"))
2940 2994
2941 def __checkForM531(self, loopNode): 2995 def __checkForM531(self, loopNode):
2942 """ 2996 """
3139 3193
3140 for n in superwalk(node): 3194 for n in superwalk(node):
3141 if isinstance(n, ast.Name) and n.id == self.__M540CaughtException.name: 3195 if isinstance(n, ast.Name) and n.id == self.__M540CaughtException.name:
3142 self.__M540CaughtException = None 3196 self.__M540CaughtException = None
3143 break 3197 break
3198
3199 def __checkForM541(self, node):
3200 """
3201 Private method to check for duplicate key value pairs in a dictionary literal.
3202
3203 @param node reference to the node to be processed
3204 @type ast.Dict
3205 """ # noqa: D234r
3206
3207 def convertToValue(item):
3208 """
3209 Function to extract the value of a given item.
3210
3211 @param item node to extract value from
3212 @type ast.Ast
3213 @return value of the node
3214 @rtype Any
3215 """
3216 if isinstance(item, ast.Constant):
3217 return item.value
3218 elif isinstance(item, ast.Tuple):
3219 return tuple(convertToValue(i) for i in item.elts)
3220 elif isinstance(item, ast.Name):
3221 return M541VariableKeyType(item.id)
3222 else:
3223 return M541UnhandledKeyType()
3224
3225 keys = [convertToValue(key) for key in node.keys]
3226 keyCounts = Counter(keys)
3227 duplicateKeys = [key for key, count in keyCounts.items() if count > 1]
3228 for key in duplicateKeys:
3229 keyIndices = [i for i, iKey in enumerate(keys) if iKey == key]
3230 seen = set()
3231 for index in keyIndices:
3232 value = convertToValue(node.values[index])
3233 if value in seen:
3234 keyNode = node.keys[index]
3235 self.violations.append((keyNode, "M541"))
3236 seen.add(value)
3144 3237
3145 def __checkForM569(self, node): 3238 def __checkForM569(self, node):
3146 """ 3239 """
3147 Private method to check for changes to a loop's mutable iterable. 3240 Private method to check for changes to a loop's mutable iterable.
3148 3241

eric ide

mercurial