82 FUNCTION_TYPES = (ast.FunctionDef, ast.AsyncFunctionDef) |
83 FUNCTION_TYPES = (ast.FunctionDef, ast.AsyncFunctionDef) |
83 else: |
84 else: |
84 FOR_TYPES = (ast.For,) |
85 FOR_TYPES = (ast.For,) |
85 LOOP_TYPES = (ast.While, ast.For) |
86 LOOP_TYPES = (ast.While, ast.For) |
86 FUNCTION_TYPES = (ast.FunctionDef,) |
87 FUNCTION_TYPES = (ast.FunctionDef,) |
|
88 |
|
89 |
|
90 if PY38_PLUS: |
|
91 def _is_singleton(node): # type: (ast.AST) -> bool |
|
92 return ( |
|
93 isinstance(node, ast.Constant) and |
|
94 isinstance(node.value, (bool, type(Ellipsis), type(None))) |
|
95 ) |
|
96 elif not PY2: |
|
97 def _is_singleton(node): # type: (ast.AST) -> bool |
|
98 return isinstance(node, (ast.NameConstant, ast.Ellipsis)) |
|
99 else: |
|
100 def _is_singleton(node): # type: (ast.AST) -> bool |
|
101 return ( |
|
102 isinstance(node, ast.Name) and |
|
103 node.id in {'True', 'False', 'Ellipsis', 'None'} |
|
104 ) |
|
105 |
|
106 |
|
107 def _is_tuple_constant(node): # type: (ast.AST) -> bool |
|
108 return ( |
|
109 isinstance(node, ast.Tuple) and |
|
110 all(_is_constant(elt) for elt in node.elts) |
|
111 ) |
|
112 |
|
113 |
|
114 if PY38_PLUS: |
|
115 def _is_constant(node): |
|
116 return isinstance(node, ast.Constant) or _is_tuple_constant(node) |
|
117 else: |
|
118 _const_tps = (ast.Str, ast.Num) |
|
119 if not PY2: |
|
120 _const_tps += (ast.Bytes,) |
|
121 |
|
122 def _is_constant(node): |
|
123 return ( |
|
124 isinstance(node, _const_tps) or |
|
125 _is_singleton(node) or |
|
126 _is_tuple_constant(node) |
|
127 ) |
|
128 |
|
129 |
|
130 def _is_const_non_singleton(node): # type: (ast.AST) -> bool |
|
131 return _is_constant(node) and not _is_singleton(node) |
|
132 |
87 |
133 |
88 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L102-L104 |
134 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L102-L104 |
89 TYPE_COMMENT_RE = re.compile(r'^#\s*type:\s*') |
135 TYPE_COMMENT_RE = re.compile(r'^#\s*type:\s*') |
90 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L1408-L1413 |
136 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L1408-L1413 |
91 ASCII_NON_ALNUM = ''.join([chr(i) for i in range(128) if not chr(i).isalnum()]) |
137 ASCII_NON_ALNUM = ''.join([chr(i) for i in range(128) if not chr(i).isalnum()]) |
623 return node.id |
669 return node.id |
624 if hasattr(node, 'name'): # an ExceptHandler node |
670 if hasattr(node, 'name'): # an ExceptHandler node |
625 return node.name |
671 return node.name |
626 |
672 |
627 |
673 |
628 def is_typing_overload(value, scope_stack): |
674 TYPING_MODULES = frozenset(('typing', 'typing_extensions')) |
629 def name_is_typing_overload(name): # type: (str) -> bool |
675 |
|
676 |
|
677 def _is_typing_helper(node, is_name_match_fn, scope_stack): |
|
678 """ |
|
679 Internal helper to determine whether or not something is a member of a |
|
680 typing module. This is used as part of working out whether we are within a |
|
681 type annotation context. |
|
682 |
|
683 Note: you probably don't want to use this function directly. Instead see the |
|
684 utils below which wrap it (`_is_typing` and `_is_any_typing_member`). |
|
685 """ |
|
686 |
|
687 def _bare_name_is_attr(name): |
630 for scope in reversed(scope_stack): |
688 for scope in reversed(scope_stack): |
631 if name in scope: |
689 if name in scope: |
632 return ( |
690 return ( |
633 isinstance(scope[name], ImportationFrom) and |
691 isinstance(scope[name], ImportationFrom) and |
634 scope[name].fullName in ( |
692 scope[name].module in TYPING_MODULES and |
635 'typing.overload', 'typing_extensions.overload', |
693 is_name_match_fn(scope[name].real_name) |
636 ) |
|
637 ) |
694 ) |
638 |
695 |
639 return False |
696 return False |
640 |
697 |
641 def is_typing_overload_decorator(node): |
698 return ( |
642 return ( |
699 ( |
643 ( |
700 isinstance(node, ast.Name) and |
644 isinstance(node, ast.Name) and name_is_typing_overload(node.id) |
701 _bare_name_is_attr(node.id) |
645 ) or ( |
702 ) or ( |
646 isinstance(node, ast.Attribute) and |
703 isinstance(node, ast.Attribute) and |
647 isinstance(node.value, ast.Name) and |
704 isinstance(node.value, ast.Name) and |
648 node.value.id == 'typing' and |
705 node.value.id in TYPING_MODULES and |
649 node.attr == 'overload' |
706 is_name_match_fn(node.attr) |
650 ) |
|
651 ) |
707 ) |
652 |
708 ) |
|
709 |
|
710 |
|
711 def _is_typing(node, typing_attr, scope_stack): |
|
712 """ |
|
713 Determine whether `node` represents the member of a typing module specified |
|
714 by `typing_attr`. |
|
715 |
|
716 This is used as part of working out whether we are within a type annotation |
|
717 context. |
|
718 """ |
|
719 return _is_typing_helper(node, lambda x: x == typing_attr, scope_stack) |
|
720 |
|
721 |
|
722 def _is_any_typing_member(node, scope_stack): |
|
723 """ |
|
724 Determine whether `node` represents any member of a typing module. |
|
725 |
|
726 This is used as part of working out whether we are within a type annotation |
|
727 context. |
|
728 """ |
|
729 return _is_typing_helper(node, lambda x: True, scope_stack) |
|
730 |
|
731 |
|
732 def is_typing_overload(value, scope_stack): |
653 return ( |
733 return ( |
654 isinstance(value.source, FUNCTION_TYPES) and |
734 isinstance(value.source, FUNCTION_TYPES) and |
655 any( |
735 any( |
656 is_typing_overload_decorator(dec) |
736 _is_typing(dec, 'overload', scope_stack) |
657 for dec in value.source.decorator_list |
737 for dec in value.source.decorator_list |
658 ) |
738 ) |
659 ) |
739 ) |
|
740 |
|
741 |
|
742 def in_annotation(func): |
|
743 @functools.wraps(func) |
|
744 def in_annotation_func(self, *args, **kwargs): |
|
745 with self._enter_annotation(): |
|
746 return func(self, *args, **kwargs) |
|
747 return in_annotation_func |
660 |
748 |
661 |
749 |
662 def make_tokens(code): |
750 def make_tokens(code): |
663 # PY3: tokenize.tokenize requires readline of bytes |
751 # PY3: tokenize.tokenize requires readline of bytes |
664 if not isinstance(code, bytes): |
752 if not isinstance(code, bytes): |
738 ast.SetComp: GeneratorScope, |
826 ast.SetComp: GeneratorScope, |
739 ast.GeneratorExp: GeneratorScope, |
827 ast.GeneratorExp: GeneratorScope, |
740 ast.DictComp: GeneratorScope, |
828 ast.DictComp: GeneratorScope, |
741 } |
829 } |
742 if PY35_PLUS: |
830 if PY35_PLUS: |
743 _ast_node_scope[ast.AsyncFunctionDef] = FunctionScope, |
831 _ast_node_scope[ast.AsyncFunctionDef] = FunctionScope |
744 |
832 |
745 nodeDepth = 0 |
833 nodeDepth = 0 |
746 offset = None |
834 offset = None |
747 traceTree = False |
835 traceTree = False |
|
836 _in_annotation = False |
|
837 _in_typing_literal = False |
|
838 _in_deferred = False |
748 |
839 |
749 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS) |
840 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS) |
750 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS') |
841 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS') |
751 if _customBuiltIns: |
842 if _customBuiltIns: |
752 builtIns.update(_customBuiltIns.split(',')) |
843 builtIns.update(_customBuiltIns.split(',')) |
774 self.root = tree |
865 self.root = tree |
775 self._type_comments = _collect_type_comments(tree, file_tokens) |
866 self._type_comments = _collect_type_comments(tree, file_tokens) |
776 for builtin in self.builtIns: |
867 for builtin in self.builtIns: |
777 self.addBinding(None, Builtin(builtin)) |
868 self.addBinding(None, Builtin(builtin)) |
778 self.handleChildren(tree) |
869 self.handleChildren(tree) |
|
870 self._in_deferred = True |
779 self.runDeferred(self._deferredFunctions) |
871 self.runDeferred(self._deferredFunctions) |
780 # Set _deferredFunctions to None so that deferFunction will fail |
872 # Set _deferredFunctions to None so that deferFunction will fail |
781 # noisily if called after we've run through the deferred functions. |
873 # noisily if called after we've run through the deferred functions. |
782 self._deferredFunctions = None |
874 self._deferredFunctions = None |
783 self.runDeferred(self._deferredAssignments) |
875 self.runDeferred(self._deferredAssignments) |
1014 # then assume the rebound name is used as a global or within a loop |
1106 # then assume the rebound name is used as a global or within a loop |
1015 value.used = self.scope[value.name].used |
1107 value.used = self.scope[value.name].used |
1016 |
1108 |
1017 self.scope[value.name] = value |
1109 self.scope[value.name] = value |
1018 |
1110 |
|
1111 def _unknown_handler(self, node): |
|
1112 # this environment variable configures whether to error on unknown |
|
1113 # ast types. |
|
1114 # |
|
1115 # this is silent by default but the error is enabled for the pyflakes |
|
1116 # testsuite. |
|
1117 # |
|
1118 # this allows new syntax to be added to python without *requiring* |
|
1119 # changes from the pyflakes side. but will still produce an error |
|
1120 # in the pyflakes testsuite (so more specific handling can be added if |
|
1121 # needed). |
|
1122 if os.environ.get('PYFLAKES_ERROR_UNKNOWN'): |
|
1123 raise NotImplementedError('Unexpected type: {}'.format(type(node))) |
|
1124 else: |
|
1125 self.handleChildren(node) |
|
1126 |
1019 def getNodeHandler(self, node_class): |
1127 def getNodeHandler(self, node_class): |
1020 try: |
1128 try: |
1021 return self._nodeHandlers[node_class] |
1129 return self._nodeHandlers[node_class] |
1022 except KeyError: |
1130 except KeyError: |
1023 nodeType = getNodeType(node_class) |
1131 nodeType = getNodeType(node_class) |
1024 self._nodeHandlers[node_class] = handler = getattr(self, nodeType) |
1132 self._nodeHandlers[node_class] = handler = getattr( |
|
1133 self, nodeType, self._unknown_handler, |
|
1134 ) |
1025 return handler |
1135 return handler |
1026 |
1136 |
1027 def handleNodeLoad(self, node): |
1137 def handleNodeLoad(self, node): |
1028 name = getNodeName(node) |
1138 name = getNodeName(node) |
1029 if not name: |
1139 if not name: |
1123 parent_stmt != node._pyflakes_parent and |
1233 parent_stmt != node._pyflakes_parent and |
1124 not self.isLiteralTupleUnpacking(parent_stmt)): |
1234 not self.isLiteralTupleUnpacking(parent_stmt)): |
1125 binding = Binding(name, node) |
1235 binding = Binding(name, node) |
1126 elif name == '__all__' and isinstance(self.scope, ModuleScope): |
1236 elif name == '__all__' and isinstance(self.scope, ModuleScope): |
1127 binding = ExportBinding(name, node._pyflakes_parent, self.scope) |
1237 binding = ExportBinding(name, node._pyflakes_parent, self.scope) |
1128 elif isinstance(getattr(node, 'ctx', None), ast.Param): |
1238 elif PY2 and isinstance(getattr(node, 'ctx', None), ast.Param): |
1129 binding = Argument(name, self.getScopeNode(node)) |
1239 binding = Argument(name, self.getScopeNode(node)) |
1130 else: |
1240 else: |
1131 binding = Assignment(name, node) |
1241 binding = Assignment(name, node) |
1132 self.addBinding(node, binding) |
1242 self.addBinding(node, binding) |
1133 |
1243 |
1158 else: |
1268 else: |
1159 try: |
1269 try: |
1160 del self.scope[name] |
1270 del self.scope[name] |
1161 except KeyError: |
1271 except KeyError: |
1162 self.report(messages.UndefinedName, node, name) |
1272 self.report(messages.UndefinedName, node, name) |
|
1273 |
|
1274 @contextlib.contextmanager |
|
1275 def _enter_annotation(self): |
|
1276 orig, self._in_annotation = self._in_annotation, True |
|
1277 try: |
|
1278 yield |
|
1279 finally: |
|
1280 self._in_annotation = orig |
1163 |
1281 |
1164 def _handle_type_comments(self, node): |
1282 def _handle_type_comments(self, node): |
1165 for (lineno, col_offset), comment in self._type_comments.get(node, ()): |
1283 for (lineno, col_offset), comment in self._type_comments.get(node, ()): |
1166 comment = comment.split(':', 1)[1].strip() |
1284 comment = comment.split(':', 1)[1].strip() |
1167 func_match = TYPE_FUNC_RE.match(comment) |
1285 func_match = TYPE_FUNC_RE.match(comment) |
1286 self.handleChildren(tree) |
1404 self.handleChildren(tree) |
1287 self.offset = node_offset |
1405 self.offset = node_offset |
1288 self.popScope() |
1406 self.popScope() |
1289 self.scopeStack = saved_stack |
1407 self.scopeStack = saved_stack |
1290 |
1408 |
|
1409 @in_annotation |
1291 def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err): |
1410 def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err): |
1292 try: |
1411 try: |
1293 tree = ast.parse(s) |
1412 tree = ast.parse(s) |
1294 except SyntaxError: |
1413 except SyntaxError: |
1295 self.report(err, node, s) |
1414 self.report(err, node, s) |
1309 descendant.lineno = ref_lineno |
1428 descendant.lineno = ref_lineno |
1310 descendant.col_offset = ref_col_offset |
1429 descendant.col_offset = ref_col_offset |
1311 |
1430 |
1312 self.handleNode(parsed_annotation, node) |
1431 self.handleNode(parsed_annotation, node) |
1313 |
1432 |
|
1433 @in_annotation |
1314 def handleAnnotation(self, annotation, node): |
1434 def handleAnnotation(self, annotation, node): |
1315 if isinstance(annotation, ast.Str): |
1435 if isinstance(annotation, ast.Str): |
1316 # Defer handling forward annotation. |
1436 # Defer handling forward annotation. |
1317 self.deferFunction(functools.partial( |
1437 self.deferFunction(functools.partial( |
1318 self.handleStringAnnotation, |
1438 self.handleStringAnnotation, |
1321 annotation.lineno, |
1441 annotation.lineno, |
1322 annotation.col_offset, |
1442 annotation.col_offset, |
1323 messages.ForwardAnnotationSyntaxError, |
1443 messages.ForwardAnnotationSyntaxError, |
1324 )) |
1444 )) |
1325 elif self.annotationsFutureEnabled: |
1445 elif self.annotationsFutureEnabled: |
1326 self.deferFunction(lambda: self.handleNode(annotation, node)) |
1446 fn = in_annotation(Checker.handleNode) |
|
1447 self.deferFunction(lambda: fn(self, annotation, node)) |
1327 else: |
1448 else: |
1328 self.handleNode(annotation, node) |
1449 self.handleNode(annotation, node) |
1329 |
1450 |
1330 def ignore(self, node): |
1451 def ignore(self, node): |
1331 pass |
1452 pass |
1332 |
1453 |
1333 # "stmt" type nodes |
1454 # "stmt" type nodes |
1334 DELETE = PRINT = FOR = ASYNCFOR = WHILE = IF = WITH = WITHITEM = \ |
1455 DELETE = PRINT = FOR = ASYNCFOR = WHILE = WITH = WITHITEM = \ |
1335 ASYNCWITH = ASYNCWITHITEM = TRYFINALLY = EXEC = \ |
1456 ASYNCWITH = ASYNCWITHITEM = TRYFINALLY = EXEC = \ |
1336 EXPR = ASSIGN = handleChildren |
1457 EXPR = ASSIGN = handleChildren |
1337 |
1458 |
1338 PASS = ignore |
1459 PASS = ignore |
1339 |
1460 |
1340 # "expr" type nodes |
1461 # "expr" type nodes |
1341 BOOLOP = UNARYOP = IFEXP = SET = \ |
1462 BOOLOP = UNARYOP = SET = \ |
1342 REPR = ATTRIBUTE = SUBSCRIPT = \ |
1463 REPR = ATTRIBUTE = \ |
1343 STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren |
1464 STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren |
|
1465 |
|
1466 def SUBSCRIPT(self, node): |
|
1467 if ( |
|
1468 ( |
|
1469 isinstance(node.value, ast.Name) and |
|
1470 node.value.id == 'Literal' |
|
1471 ) or ( |
|
1472 isinstance(node.value, ast.Attribute) and |
|
1473 node.value.attr == 'Literal' |
|
1474 ) |
|
1475 ): |
|
1476 orig, self._in_typing_literal = self._in_typing_literal, True |
|
1477 try: |
|
1478 self.handleChildren(node) |
|
1479 finally: |
|
1480 self._in_typing_literal = orig |
|
1481 else: |
|
1482 if _is_any_typing_member(node.value, self.scopeStack): |
|
1483 with self._enter_annotation(): |
|
1484 self.handleChildren(node) |
|
1485 else: |
|
1486 self.handleChildren(node) |
1344 |
1487 |
1345 def _handle_string_dot_format(self, node): |
1488 def _handle_string_dot_format(self, node): |
1346 try: |
1489 try: |
1347 placeholders = tuple(parse_format_string(node.func.value.s)) |
1490 placeholders = tuple(parse_format_string(node.func.value.s)) |
1348 except ValueError as e: |
1491 except ValueError as e: |
1467 isinstance(node.func, ast.Attribute) and |
1610 isinstance(node.func, ast.Attribute) and |
1468 isinstance(node.func.value, ast.Str) and |
1611 isinstance(node.func.value, ast.Str) and |
1469 node.func.attr == 'format' |
1612 node.func.attr == 'format' |
1470 ): |
1613 ): |
1471 self._handle_string_dot_format(node) |
1614 self._handle_string_dot_format(node) |
|
1615 |
|
1616 if ( |
|
1617 _is_typing(node.func, 'cast', self.scopeStack) and |
|
1618 len(node.args) >= 1 and |
|
1619 isinstance(node.args[0], ast.Str) |
|
1620 ): |
|
1621 with self._enter_annotation(): |
|
1622 self.handleNode(node.args[0], node) |
|
1623 |
1472 self.handleChildren(node) |
1624 self.handleChildren(node) |
1473 |
1625 |
1474 def _handle_percent_format(self, node): |
1626 def _handle_percent_format(self, node): |
1475 try: |
1627 try: |
1476 placeholders = parse_percent_format(node.left.s) |
1628 placeholders = parse_percent_format(node.left.s) |
1580 isinstance(node.left, ast.Str) |
1732 isinstance(node.left, ast.Str) |
1581 ): |
1733 ): |
1582 self._handle_percent_format(node) |
1734 self._handle_percent_format(node) |
1583 self.handleChildren(node) |
1735 self.handleChildren(node) |
1584 |
1736 |
1585 NUM = STR = BYTES = ELLIPSIS = CONSTANT = ignore |
1737 def STR(self, node): |
|
1738 if self._in_annotation and not self._in_typing_literal: |
|
1739 fn = functools.partial( |
|
1740 self.handleStringAnnotation, |
|
1741 node.s, |
|
1742 node, |
|
1743 node.lineno, |
|
1744 node.col_offset, |
|
1745 messages.ForwardAnnotationSyntaxError, |
|
1746 ) |
|
1747 if self._in_deferred: |
|
1748 fn() |
|
1749 else: |
|
1750 self.deferFunction(fn) |
|
1751 |
|
1752 if PY38_PLUS: |
|
1753 def CONSTANT(self, node): |
|
1754 if isinstance(node.value, str): |
|
1755 return self.STR(node) |
|
1756 else: |
|
1757 NUM = BYTES = ELLIPSIS = CONSTANT = ignore |
1586 |
1758 |
1587 # "slice" type nodes |
1759 # "slice" type nodes |
1588 SLICE = EXTSLICE = INDEX = handleChildren |
1760 SLICE = EXTSLICE = INDEX = handleChildren |
1589 |
1761 |
1590 # expression contexts are node instances too, though being constants |
1762 # expression contexts are node instances too, though being constants |
1663 key_node, |
1835 key_node, |
1664 key, |
1836 key, |
1665 ) |
1837 ) |
1666 self.handleChildren(node) |
1838 self.handleChildren(node) |
1667 |
1839 |
|
1840 def IF(self, node): |
|
1841 if isinstance(node.test, ast.Tuple) and node.test.elts != []: |
|
1842 self.report(messages.IfTuple, node) |
|
1843 self.handleChildren(node) |
|
1844 |
|
1845 IFEXP = IF |
|
1846 |
1668 def ASSERT(self, node): |
1847 def ASSERT(self, node): |
1669 if isinstance(node.test, ast.Tuple) and node.test.elts != []: |
1848 if isinstance(node.test, ast.Tuple) and node.test.elts != []: |
1670 self.report(messages.AssertTuple, node) |
1849 self.report(messages.AssertTuple, node) |
1671 self.handleChildren(node) |
1850 self.handleChildren(node) |
1672 |
1851 |
1714 def NAME(self, node): |
1893 def NAME(self, node): |
1715 """ |
1894 """ |
1716 Handle occurrence of Name (which can be a load/store/delete access.) |
1895 Handle occurrence of Name (which can be a load/store/delete access.) |
1717 """ |
1896 """ |
1718 # Locate the name in locals / function / globals scopes. |
1897 # Locate the name in locals / function / globals scopes. |
1719 if isinstance(node.ctx, (ast.Load, ast.AugLoad)): |
1898 if isinstance(node.ctx, ast.Load): |
1720 self.handleNodeLoad(node) |
1899 self.handleNodeLoad(node) |
1721 if (node.id == 'locals' and isinstance(self.scope, FunctionScope) and |
1900 if (node.id == 'locals' and isinstance(self.scope, FunctionScope) and |
1722 isinstance(node._pyflakes_parent, ast.Call)): |
1901 isinstance(node._pyflakes_parent, ast.Call)): |
1723 # we are doing locals() call in current scope |
1902 # we are doing locals() call in current scope |
1724 self.scope.usesLocals = True |
1903 self.scope.usesLocals = True |
1725 elif isinstance(node.ctx, (ast.Store, ast.AugStore, ast.Param)): |
1904 elif isinstance(node.ctx, ast.Store): |
|
1905 self.handleNodeStore(node) |
|
1906 elif PY2 and isinstance(node.ctx, ast.Param): |
1726 self.handleNodeStore(node) |
1907 self.handleNodeStore(node) |
1727 elif isinstance(node.ctx, ast.Del): |
1908 elif isinstance(node.ctx, ast.Del): |
1728 self.handleNodeDelete(node) |
1909 self.handleNodeDelete(node) |
1729 else: |
1910 else: |
1730 # Unknown context |
1911 # Unknown context |
2059 if node.value: |
2240 if node.value: |
2060 # If the assignment has value, handle the *value* now. |
2241 # If the assignment has value, handle the *value* now. |
2061 self.handleNode(node.value, node) |
2242 self.handleNode(node.value, node) |
2062 |
2243 |
2063 def COMPARE(self, node): |
2244 def COMPARE(self, node): |
2064 literals = (ast.Str, ast.Num) |
|
2065 if not PY2: |
|
2066 literals += (ast.Bytes,) |
|
2067 |
|
2068 left = node.left |
2245 left = node.left |
2069 for op, right in zip(node.ops, node.comparators): |
2246 for op, right in zip(node.ops, node.comparators): |
2070 if (isinstance(op, (ast.Is, ast.IsNot)) and |
2247 if ( |
2071 (isinstance(left, literals) or isinstance(right, literals))): |
2248 isinstance(op, (ast.Is, ast.IsNot)) and ( |
|
2249 _is_const_non_singleton(left) or |
|
2250 _is_const_non_singleton(right) |
|
2251 ) |
|
2252 ): |
2072 self.report(messages.IsLiteral, node) |
2253 self.report(messages.IsLiteral, node) |
2073 left = right |
2254 left = right |
2074 |
2255 |
2075 self.handleChildren(node) |
2256 self.handleChildren(node) |
2076 |
2257 |