eric6/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/MiscellaneousChecker.py

branch
maintenance
changeset 8273
698ae46f40a4
parent 8176
31965986ecd1
parent 8243
cc717c2ae956
equal deleted inserted replaced
8190:fb0ef164f536 8273:698ae46f40a4
12 import re 12 import re
13 import itertools 13 import itertools
14 from string import Formatter 14 from string import Formatter
15 from collections import defaultdict 15 from collections import defaultdict
16 import tokenize 16 import tokenize
17 import copy
18 import contextlib
17 19
18 import AstUtilities 20 import AstUtilities
19 21
20 from .eradicate import Eradicator 22 from .eradicate import Eradicator
21 23
30 @type ast.Node 32 @type ast.Node
31 @yield call path components 33 @yield call path components
32 @ytype str 34 @ytype str
33 """ 35 """
34 if isinstance(node, ast.Attribute): 36 if isinstance(node, ast.Attribute):
35 for v in composeCallPath(node.value): 37 yield from composeCallPath(node.value)
36 yield v
37 yield node.attr 38 yield node.attr
38 elif isinstance(node, ast.Name): 39 elif isinstance(node, ast.Name):
39 yield node.id 40 yield node.id
40 41
41 42
42 class MiscellaneousChecker(object): 43 class MiscellaneousChecker:
43 """ 44 """
44 Class implementing a checker for miscellaneous checks. 45 Class implementing a checker for miscellaneous checks.
45 """ 46 """
46 Codes = [ 47 Codes = [
47 ## Coding line 48 ## Coding line
108 ## line continuation 109 ## line continuation
109 "M841", 110 "M841",
110 111
111 ## commented code 112 ## commented code
112 "M891", 113 "M891",
113
114 ## syntax error
115 "M901",
116 ] 114 ]
117 115
118 Formatter = Formatter() 116 Formatter = Formatter()
119 FormatFieldRegex = re.compile(r'^((?:\s|.)*?)(\..*|\[.*\])?$') 117 FormatFieldRegex = re.compile(r'^((?:\s|.)*?)(\..*|\[.*\])?$')
120 118
122 "__name__", 120 "__name__",
123 "__doc__", 121 "__doc__",
124 "credits", 122 "credits",
125 ] 123 ]
126 124
127 def __init__(self, source, filename, select, ignore, expected, repeat, 125 def __init__(self, source, filename, tree, select, ignore, expected,
128 args): 126 repeat, args):
129 """ 127 """
130 Constructor 128 Constructor
131 129
132 @param source source code to be checked 130 @param source source code to be checked
133 @type list of str 131 @type list of str
134 @param filename name of the source file 132 @param filename name of the source file
135 @type str 133 @type str
134 @param tree AST tree of the source code
135 @type ast.Module
136 @param select list of selected codes 136 @param select list of selected codes
137 @type list of str 137 @type list of str
138 @param ignore list of codes to be ignored 138 @param ignore list of codes to be ignored
139 @type list of str 139 @type list of str
140 @param expected list of expected codes 140 @param expected list of expected codes
148 self.__ignore = ('',) if select else tuple(ignore) 148 self.__ignore = ('',) if select else tuple(ignore)
149 self.__expected = expected[:] 149 self.__expected = expected[:]
150 self.__repeat = repeat 150 self.__repeat = repeat
151 self.__filename = filename 151 self.__filename = filename
152 self.__source = source[:] 152 self.__source = source[:]
153 self.__tree = copy.deepcopy(tree)
153 self.__args = args 154 self.__args = args
154 155
155 self.__pep3101FormatRegex = re.compile( 156 self.__pep3101FormatRegex = re.compile(
156 r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%') 157 r'^(?:[^\'"]*[\'"][^\'"]*[\'"])*\s*%|^\s*%')
157 158
265 "code": code, 266 "code": code,
266 "args": args, 267 "args": args,
267 } 268 }
268 ) 269 )
269 270
270 def __reportInvalidSyntax(self):
271 """
272 Private method to report a syntax error.
273 """
274 exc_type, exc = sys.exc_info()[:2]
275 if len(exc.args) > 1:
276 offset = exc.args[1]
277 if len(offset) > 2:
278 offset = offset[1:3]
279 else:
280 offset = (1, 0)
281 self.__error(offset[0] - 1, offset[1] or 0,
282 'M901', exc_type.__name__, exc.args[0])
283
284 def __generateTree(self):
285 """
286 Private method to generate an AST for our source.
287
288 @return generated AST
289 @rtype ast.AST
290 """
291 return ast.parse("".join(self.__source), self.__filename)
292
293 def run(self): 271 def run(self):
294 """ 272 """
295 Public method to check the given source against miscellaneous 273 Public method to check the given source against miscellaneous
296 conditions. 274 conditions.
297 """ 275 """
299 # don't do anything, if essential data is missing 277 # don't do anything, if essential data is missing
300 return 278 return
301 279
302 if not self.__checkers: 280 if not self.__checkers:
303 # don't do anything, if no codes were selected 281 # don't do anything, if no codes were selected
304 return
305
306 try:
307 self.__tree = self.__generateTree()
308 except (SyntaxError, TypeError):
309 self.__reportInvalidSyntax()
310 return 282 return
311 283
312 for check in self.__checkers: 284 for check in self.__checkers:
313 check() 285 check()
314 286
478 break 450 break
479 451
480 if isinstance(node, ast.Module) or not hasCode: 452 if isinstance(node, ast.Module) or not hasCode:
481 return 453 return
482 454
483 if not (imports >= expectedImports): 455 if imports < expectedImports:
484 if imports: 456 if imports:
485 self.__error(node.lineno - 1, node.col_offset, "M701", 457 self.__error(node.lineno - 1, node.col_offset, "M701",
486 ", ".join(expectedImports), ", ".join(imports)) 458 ", ".join(expectedImports), ", ".join(imports))
487 else: 459 else:
488 self.__error(node.lineno - 1, node.col_offset, "M702", 460 self.__error(node.lineno - 1, node.col_offset, "M702",
570 if hasStarArgs: 542 if hasStarArgs:
571 numArgs -= 1 543 numArgs -= 1
572 544
573 # if starargs or kwargs is not None, it can't count the 545 # if starargs or kwargs is not None, it can't count the
574 # parameters but at least check if the args are used 546 # parameters but at least check if the args are used
575 if hasKwArgs: 547 if hasKwArgs and not names:
576 if not names: 548 # No names but kwargs
577 # No names but kwargs 549 self.__error(call.lineno - 1, call.col_offset, "M623")
578 self.__error(call.lineno - 1, call.col_offset, "M623") 550 if hasStarArgs and not numbers:
579 if hasStarArgs: 551 # No numbers but args
580 if not numbers: 552 self.__error(call.lineno - 1, call.col_offset, "M624")
581 # No numbers but args
582 self.__error(call.lineno - 1, call.col_offset, "M624")
583 553
584 if not hasKwArgs and not hasStarArgs: 554 if not hasKwArgs and not hasStarArgs:
585 # can actually verify numbers and names 555 # can actually verify numbers and names
586 for number in sorted(numbers): 556 for number in sorted(numbers):
587 if number >= numArgs: 557 if number >= numArgs:
640 def __checkBuiltins(self): 610 def __checkBuiltins(self):
641 """ 611 """
642 Private method to check, if built-ins are shadowed. 612 Private method to check, if built-ins are shadowed.
643 """ 613 """
644 functionDefs = [ast.FunctionDef] 614 functionDefs = [ast.FunctionDef]
645 try: 615 with contextlib.suppress(AttributeError):
646 functionDefs.append(ast.AsyncFunctionDef) 616 functionDefs.append(ast.AsyncFunctionDef)
647 except AttributeError:
648 pass
649 617
650 ignoreBuiltinAssignments = self.__args.get( 618 ignoreBuiltinAssignments = self.__args.get(
651 "BuiltinsChecker", 619 "BuiltinsChecker",
652 MiscellaneousCheckerDefaultArgs["BuiltinsChecker"]) 620 MiscellaneousCheckerDefaultArgs["BuiltinsChecker"])
653 621
828 immutableCalls = ( 796 immutableCalls = (
829 "tuple", 797 "tuple",
830 "frozenset", 798 "frozenset",
831 ) 799 )
832 functionDefs = [ast.FunctionDef] 800 functionDefs = [ast.FunctionDef]
833 try: 801 with contextlib.suppress(AttributeError):
834 functionDefs.append(ast.AsyncFunctionDef) 802 functionDefs.append(ast.AsyncFunctionDef)
835 except AttributeError:
836 pass
837 803
838 for node in ast.walk(self.__tree): 804 for node in ast.walk(self.__tree):
839 if any(isinstance(node, functionDef) 805 if any(isinstance(node, functionDef)
840 for functionDef in functionDefs): 806 for functionDef in functionDefs):
841 defaults = node.args.defaults[:] 807 defaults = node.args.defaults[:]
842 try: 808 with contextlib.suppress(AttributeError):
843 defaults += node.args.kw_defaults[:] 809 defaults += node.args.kw_defaults[:]
844 except AttributeError:
845 pass
846 for default in defaults: 810 for default in defaults:
847 if any(isinstance(default, mutableType) 811 if any(isinstance(default, mutableType)
848 for mutableType in mutableTypes): 812 for mutableType in mutableTypes):
849 typeName = type(default).__name__ 813 typeName = type(default).__name__
850 if isinstance(default, ast.Call): 814 if isinstance(default, ast.Call):
941 """ 905 """
942 Private method to check use of naive datetime functions. 906 Private method to check use of naive datetime functions.
943 """ 907 """
944 # step 1: generate an augmented node tree containing parent info 908 # step 1: generate an augmented node tree containing parent info
945 # for each child node 909 # for each child node
946 tree = self.__generateTree() 910 tree = copy.deepcopy(self.__tree)
947 for node in ast.walk(tree): 911 for node in ast.walk(tree):
948 for childNode in ast.iter_child_nodes(node): 912 for childNode in ast.iter_child_nodes(node):
949 childNode._dtCheckerParent = node 913 childNode._dtCheckerParent = node
950 914
951 # step 2: perform checks and report issues 915 # step 2: perform checks and report issues
979 943
980 def __init__(self): 944 def __init__(self):
981 """ 945 """
982 Constructor 946 Constructor
983 """ 947 """
984 super(TextVisitor, self).__init__() 948 super().__init__()
985 self.nodes = [] 949 self.nodes = []
986 self.calls = {} 950 self.calls = {}
987 951
988 def __addNode(self, node): 952 def __addNode(self, node):
989 """ 953 """
1023 """ 987 """
1024 if sys.version_info >= (3, 8, 0): 988 if sys.version_info >= (3, 8, 0):
1025 if AstUtilities.isBaseString(node): 989 if AstUtilities.isBaseString(node):
1026 self.__addNode(node) 990 self.__addNode(node)
1027 else: 991 else:
1028 super(TextVisitor, self).generic_visit(node) 992 super().generic_visit(node)
1029 else: 993 else:
1030 super(TextVisitor, self).generic_visit(node) 994 super().generic_visit(node)
1031 995
1032 def __visitDefinition(self, node): 996 def __visitDefinition(self, node):
1033 """ 997 """
1034 Private method handling class and function definitions. 998 Private method handling class and function definitions.
1035 999
1121 node.func.value.id == 'str' and 1085 node.func.value.id == 'str' and
1122 node.args and 1086 node.args and
1123 AstUtilities.isBaseString(node.args[0]) 1087 AstUtilities.isBaseString(node.args[0])
1124 ): 1088 ):
1125 self.calls[node.args[0]] = (node, True) 1089 self.calls[node.args[0]] = (node, True)
1126 super(TextVisitor, self).generic_visit(node) 1090 super().generic_visit(node)
1127 1091
1128 1092
1129 class LoggingVisitor(ast.NodeVisitor): 1093 class LoggingVisitor(ast.NodeVisitor):
1130 """ 1094 """
1131 Class implementing a node visitor to check logging statements. 1095 Class implementing a node visitor to check logging statements.
1141 1105
1142 def __init__(self): 1106 def __init__(self):
1143 """ 1107 """
1144 Constructor 1108 Constructor
1145 """ 1109 """
1146 super(LoggingVisitor, self).__init__() 1110 super().__init__()
1147 1111
1148 self.__currentLoggingCall = None 1112 self.__currentLoggingCall = None
1149 self.__currentLoggingArgument = None 1113 self.__currentLoggingArgument = None
1150 self.__currentLoggingLevel = None 1114 self.__currentLoggingLevel = None
1151 self.__currentExtraKeyword = None 1115 self.__currentExtraKeyword = None
1190 @param node reference to the node to be processed 1154 @param node reference to the node to be processed
1191 @type ast.Call 1155 @type ast.Call
1192 @return logging level 1156 @return logging level
1193 @rtype str or None 1157 @rtype str or None
1194 """ 1158 """
1195 try: 1159 with contextlib.suppress(AttributeError):
1196 if node.func.value.id == "warnings": 1160 if node.func.value.id == "warnings":
1197 return None 1161 return None
1198 1162
1199 if node.func.attr in LoggingVisitor.LoggingLevels: 1163 if node.func.attr in LoggingVisitor.LoggingLevels:
1200 return node.func.attr 1164 return node.func.attr
1201 except AttributeError:
1202 pass
1203 1165
1204 return None 1166 return None
1205 1167
1206 def __isFormatCall(self, node): 1168 def __isFormatCall(self, node):
1207 """ 1169 """
1226 1188
1227 @param node reference to the node to be processed 1189 @param node reference to the node to be processed
1228 @type ast.Call 1190 @type ast.Call
1229 """ 1191 """
1230 # we are in a logging statement 1192 # we are in a logging statement
1231 if self.__withinLoggingStatement(): 1193 if (
1232 if self.__withinLoggingArgument() and self.__isFormatCall(node): 1194 self.__withinLoggingStatement() and
1233 self.violations.append((node, "M651")) 1195 self.__withinLoggingArgument() and
1234 super(LoggingVisitor, self).generic_visit(node) 1196 self.__isFormatCall(node)
1235 return 1197 ):
1198 self.violations.append((node, "M651"))
1199 super().generic_visit(node)
1200 return
1236 1201
1237 loggingLevel = self.__detectLoggingLevel(node) 1202 loggingLevel = self.__detectLoggingLevel(node)
1238 1203
1239 if loggingLevel and self.__currentLoggingLevel is None: 1204 if loggingLevel and self.__currentLoggingLevel is None:
1240 self.__currentLoggingLevel = loggingLevel 1205 self.__currentLoggingLevel = loggingLevel
1241 1206
1242 # we are in some other statement 1207 # we are in some other statement
1243 if loggingLevel is None: 1208 if loggingLevel is None:
1244 super(LoggingVisitor, self).generic_visit(node) 1209 super().generic_visit(node)
1245 return 1210 return
1246 1211
1247 # we are entering a new logging statement 1212 # we are entering a new logging statement
1248 self.__currentLoggingCall = node 1213 self.__currentLoggingCall = node
1249 1214
1258 isinstance(child, ast.keyword) and 1223 isinstance(child, ast.keyword) and
1259 child.arg == "extra" 1224 child.arg == "extra"
1260 ): 1225 ):
1261 self.__currentExtraKeyword = child 1226 self.__currentExtraKeyword = child
1262 1227
1263 super(LoggingVisitor, self).visit(child) 1228 super().visit(child)
1264 1229
1265 self.__currentLoggingArgument = None 1230 self.__currentLoggingArgument = None
1266 self.__currentExtraKeyword = None 1231 self.__currentExtraKeyword = None
1267 1232
1268 self.__currentLoggingCall = None 1233 self.__currentLoggingCall = None
1283 1248
1284 # handle string concat 1249 # handle string concat
1285 if isinstance(node.op, ast.Add): 1250 if isinstance(node.op, ast.Add):
1286 self.violations.append((node, "M653")) 1251 self.violations.append((node, "M653"))
1287 1252
1288 super(LoggingVisitor, self).generic_visit(node) 1253 super().generic_visit(node)
1289 1254
1290 def visit_JoinedStr(self, node): 1255 def visit_JoinedStr(self, node):
1291 """ 1256 """
1292 Public method to handle f-string arguments. 1257 Public method to handle f-string arguments.
1293 1258
1294 @param node reference to the node to be processed 1259 @param node reference to the node to be processed
1295 @type ast.JoinedStr 1260 @type ast.JoinedStr
1296 """ 1261 """
1297 if self.__withinLoggingStatement(): 1262 if (
1298 if any(isinstance(i, ast.FormattedValue) for i in node.values): 1263 self.__withinLoggingStatement() and
1299 if self.__withinLoggingArgument(): 1264 any(isinstance(i, ast.FormattedValue) for i in node.values) and
1300 self.violations.append((node, "M654")) 1265 self.__withinLoggingArgument()
1301 1266 ):
1302 super(LoggingVisitor, self).generic_visit(node) 1267 self.violations.append((node, "M654"))
1268
1269 super().generic_visit(node)
1303 1270
1304 1271
1305 class BugBearVisitor(ast.NodeVisitor): 1272 class BugBearVisitor(ast.NodeVisitor):
1306 """ 1273 """
1307 Class implementing a node visitor to check for various topics. 1274 Class implementing a node visitor to check for various topics.
1315 1282
1316 def __init__(self): 1283 def __init__(self):
1317 """ 1284 """
1318 Constructor 1285 Constructor
1319 """ 1286 """
1320 super(BugBearVisitor, self).__init__() 1287 super().__init__()
1321 1288
1322 self.__nodeStack = [] 1289 self.__nodeStack = []
1323 self.__nodeWindow = [] 1290 self.__nodeWindow = []
1324 self.violations = [] 1291 self.violations = []
1325 1292
1332 """ 1299 """
1333 self.__nodeStack.append(node) 1300 self.__nodeStack.append(node)
1334 self.__nodeWindow.append(node) 1301 self.__nodeWindow.append(node)
1335 self.__nodeWindow = self.__nodeWindow[-BugBearVisitor.NodeWindowSize:] 1302 self.__nodeWindow = self.__nodeWindow[-BugBearVisitor.NodeWindowSize:]
1336 1303
1337 super(BugBearVisitor, self).visit(node) 1304 super().visit(node)
1338 1305
1339 self.__nodeStack.pop() 1306 self.__nodeStack.pop()
1340 1307
1341 def visit_UAdd(self, node): 1308 def visit_UAdd(self, node):
1342 """ 1309 """
1374 self.violations.append((node, code)) 1341 self.violations.append((node, code))
1375 break 1342 break
1376 else: 1343 else:
1377 self.__checkForM502(node) 1344 self.__checkForM502(node)
1378 else: 1345 else:
1379 try: 1346 with contextlib.suppress(AttributeError, IndexError):
1380 # bad super() call 1347 # bad super() call
1381 if isinstance(node.func, ast.Name) and node.func.id == "super": 1348 if isinstance(node.func, ast.Name) and node.func.id == "super":
1382 args = node.args 1349 args = node.args
1383 if ( 1350 if (
1384 len(args) == 2 and 1351 len(args) == 2 and
1405 node.func.id == "setattr" and 1372 node.func.id == "setattr" and
1406 len(node.args) == 3 and 1373 len(node.args) == 3 and
1407 AstUtilities.isString(node.args[1]) 1374 AstUtilities.isString(node.args[1])
1408 ): 1375 ):
1409 self.violations.append((node, "M513")) 1376 self.violations.append((node, "M513"))
1410 except (AttributeError, IndexError):
1411 pass
1412 1377
1413 self.generic_visit(node) 1378 self.generic_visit(node)
1414 1379
1415 def visit_Attribute(self, node): 1380 def visit_Attribute(self, node):
1416 """ 1381 """
1450 1415
1451 elif len(node.targets) == 1: 1416 elif len(node.targets) == 1:
1452 target = node.targets[0] 1417 target = node.targets[0]
1453 if ( 1418 if (
1454 isinstance(target, ast.Attribute) and 1419 isinstance(target, ast.Attribute) and
1455 isinstance(target.value, ast.Name) 1420 isinstance(target.value, ast.Name) and
1421 (target.value.id, target.attr) == ('os', 'environ')
1456 ): 1422 ):
1457 if (target.value.id, target.attr) == ('os', 'environ'): 1423 self.violations.append((node, "M506"))
1458 self.violations.append((node, "M506"))
1459 1424
1460 self.generic_visit(node) 1425 self.generic_visit(node)
1461 1426
1462 def visit_For(self, node): 1427 def visit_For(self, node):
1463 """ 1428 """
1557 """ 1522 """
1558 def __init__(self): 1523 def __init__(self):
1559 """ 1524 """
1560 Constructor 1525 Constructor
1561 """ 1526 """
1562 super(NameFinder, self).__init__() 1527 super().__init__()
1563 1528
1564 self.__names = {} 1529 self.__names = {}
1565 1530
1566 def visit_Name(self, node): 1531 def visit_Name(self, node):
1567 """ 1532 """
1579 @param node AST node to be traversed 1544 @param node AST node to be traversed
1580 @type ast.Node 1545 @type ast.Node
1581 """ 1546 """
1582 if isinstance(node, list): 1547 if isinstance(node, list):
1583 for elem in node: 1548 for elem in node:
1584 super(NameFinder, self).visit(elem) 1549 super().visit(elem)
1585 else: 1550 else:
1586 super(NameFinder, self).visit(node) 1551 super().visit(node)
1587 1552
1588 def getNames(self): 1553 def getNames(self):
1589 """ 1554 """
1590 Public method to return the extracted names and Name nodes. 1555 Public method to return the extracted names and Name nodes.
1591 1556
1605 1570
1606 def __init__(self): 1571 def __init__(self):
1607 """ 1572 """
1608 Constructor 1573 Constructor
1609 """ 1574 """
1610 super(ReturnVisitor, self).__init__() 1575 super().__init__()
1611 1576
1612 self.__stack = [] 1577 self.__stack = []
1613 self.violations = [] 1578 self.violations = []
1614 self.__loopCount = 0 1579 self.__loopCount = 0
1615 1580
1966 """ 1931 """
1967 def __init__(self): 1932 def __init__(self):
1968 """ 1933 """
1969 Constructor 1934 Constructor
1970 """ 1935 """
1971 super(DateTimeVisitor, self).__init__() 1936 super().__init__()
1972 1937
1973 self.violations = [] 1938 self.violations = []
1974 1939
1975 def __getFromKeywords(self, keywords, name): 1940 def __getFromKeywords(self, keywords, name):
1976 """ 1941 """
2124 2089
2125 elif node.func.attr == 'strptime': 2090 elif node.func.attr == 'strptime':
2126 # datetime.strptime(...).replace(tzinfo=UTC) 2091 # datetime.strptime(...).replace(tzinfo=UTC)
2127 parent = getattr(node, '_dtCheckerParent', None) 2092 parent = getattr(node, '_dtCheckerParent', None)
2128 pparent = getattr(parent, '_dtCheckerParent', None) 2093 pparent = getattr(parent, '_dtCheckerParent', None)
2129 if not (isinstance(parent, ast.Attribute) and 2094 if (
2130 parent.attr == 'replace'): 2095 not (isinstance(parent, ast.Attribute) and
2131 isCase1 = False 2096 parent.attr == 'replace') or
2132 elif not isinstance(pparent, ast.Call): 2097 not isinstance(pparent, ast.Call)
2098 ):
2133 isCase1 = False 2099 isCase1 = False
2134 else: 2100 else:
2135 tzinfoKeyword = self.__getFromKeywords(pparent.keywords, 2101 tzinfoKeyword = self.__getFromKeywords(pparent.keywords,
2136 'tzinfo') 2102 'tzinfo')
2137 isCase1 = ( 2103 isCase1 = (
2186 """ 2152 """
2187 def __init__(self): 2153 def __init__(self):
2188 """ 2154 """
2189 Constructor 2155 Constructor
2190 """ 2156 """
2191 super(SysVersionVisitor, self).__init__() 2157 super().__init__()
2192 2158
2193 self.violations = [] 2159 self.violations = []
2194 self.__fromImports = {} 2160 self.__fromImports = {}
2195 2161
2196 def visit_ImportFrom(self, node): 2162 def visit_ImportFrom(self, node):
2217 @return flag indicating a match 2183 @return flag indicating a match
2218 @rtype bool 2184 @rtype bool
2219 """ 2185 """
2220 match = False 2186 match = False
2221 if ( 2187 if (
2222 isinstance(node, ast.Attribute) and 2188 (isinstance(node, ast.Attribute) and
2223 isinstance(node.value, ast.Name) and 2189 isinstance(node.value, ast.Name) and
2224 node.value.id == "sys" and 2190 node.value.id == "sys" and
2225 node.attr == attr 2191 node.attr == attr) or
2226 ): 2192 (isinstance(node, ast.Name) and
2227 match = True 2193 node.id == attr and
2228 elif ( 2194 self.__fromImports.get(node.id) == "sys")
2229 isinstance(node, ast.Name) and
2230 node.id == attr and
2231 self.__fromImports.get(node.id) == "sys"
2232 ): 2195 ):
2233 match = True 2196 match = True
2234 2197
2235 return match 2198 return match
2236 2199

eric ide

mercurial