def test_module_level_names(self): assign = astroid.extract_node(""" import collections Class = collections.namedtuple("a", ("b", "c")) #@ """) with self.assertNoMessages(): self.checker.visit_assignname(assign.targets[0]) assign = astroid.extract_node(""" class ClassA(object): pass ClassB = ClassA """) with self.assertNoMessages(): self.checker.visit_assignname(assign.targets[0]) module = astroid.parse(""" def A(): return 1, 2, 3 CONSTA, CONSTB, CONSTC = A() CONSTD = A()""") with self.assertNoMessages(): self.checker.visit_assignname(module.body[1].targets[0].elts[0]) self.checker.visit_assignname(module.body[2].targets[0]) assign = astroid.extract_node(""" CONST = "12 34 ".rstrip().split()""") with self.assertNoMessages(): self.checker.visit_assignname(assign.targets[0])
def test_skip_camel_cased_words(self): stmt = astroid.extract_node( 'class ComentAbc(object):\n """comentAbc with a bad coment"""\n pass') with self.assertAddsMessages( Message('wrong-spelling-in-docstring', line=2, args=('coment', 'comentAbc with a bad coment', ' ^^^^^^', self._get_msg_suggestions('coment')))): self.checker.visit_classdef(stmt) # With just a single upper case letter in the end stmt = astroid.extract_node( 'class ComentAbc(object):\n """argumentN with a bad coment"""\n pass') with self.assertAddsMessages( Message('wrong-spelling-in-docstring', line=2, args=('coment', 'argumentN with a bad coment', ' ^^^^^^', self._get_msg_suggestions('coment')))): self.checker.visit_classdef(stmt) for ccn in ('xmlHttpRequest', 'newCustomer', 'newCustomerId', 'innerStopwatch', 'supportsIpv6OnIos', 'affine3D'): stmt = astroid.extract_node( 'class TestClass(object):\n """{} comment"""\n pass'.format(ccn)) self.checker.visit_classdef(stmt) assert self.linter.release_messages() == []
def test_not_next_method(self): arg_node = astroid.extract_node('x.next(x) #@') stararg_node = astroid.extract_node('x.next(*x) #@') kwarg_node = astroid.extract_node('x.next(y=x) #@') with self.assertNoMessages(): for node in (arg_node, stararg_node, kwarg_node): self.checker.visit_call(node)
def test_skip_camel_cased_words(self): stmt = astroid.extract_node( 'class ComentAbc(object):\n """comentAbc with a bad coment"""\n pass') with self.assertAddsMessages( Message('wrong-spelling-in-docstring', line=2, args=('coment', 'comentAbc with a bad coment', ' ^^^^^^', self._get_msg_suggestions('coment')))): self.checker.visit_classdef(stmt) # With just a single upper case letter in the end stmt = astroid.extract_node( 'class ComentAbc(object):\n """argumentN with a bad coment"""\n pass') with self.assertAddsMessages( Message('wrong-spelling-in-docstring', line=2, args=('coment', 'argumentN with a bad coment', ' ^^^^^^', self._get_msg_suggestions('coment')))): self.checker.visit_classdef(stmt) # With just a single lower and upper case letter is not good stmt = astroid.extract_node( 'class ComentAbc(object):\n """zN with a bad comment"""\n pass') with self.assertAddsMessages( Message('wrong-spelling-in-docstring', line=2, args=('zN', 'zN with a bad comment', '^^', self._get_msg_suggestions('zN')))): self.checker.visit_classdef(stmt)
def test_custom_callback_string(self): """ Test the --calbacks option works. """ node = astroid.extract_node(""" def callback_one(abc): ''' should not emit unused-argument. ''' """) with self.assertNoMessages(): self.checker.visit_functiondef(node) self.checker.leave_functiondef(node) node = astroid.extract_node(""" def two_callback(abc, defg): ''' should not emit unused-argument. ''' """) with self.assertNoMessages(): self.checker.visit_functiondef(node) self.checker.leave_functiondef(node) node = astroid.extract_node(""" def normal_func(abc): ''' should emit unused-argument. ''' """) with self.assertAddsMessages( Message('unused-argument', node=node['abc'], args='abc')): self.checker.visit_functiondef(node) self.checker.leave_functiondef(node) node = astroid.extract_node(""" def cb_func(abc): ''' Previous callbacks are overridden. ''' """) with self.assertAddsMessages( Message('unused-argument', node=node['abc'], args='abc')): self.checker.visit_functiondef(node) self.checker.leave_functiondef(node)
def test_dict_not_iter_method(self): arg_node = astroid.extract_node("x.iterkeys(x) #@") stararg_node = astroid.extract_node("x.iterkeys(*x) #@") kwarg_node = astroid.extract_node("x.iterkeys(y=x) #@") with self.assertNoMessages(): for node in (arg_node, stararg_node, kwarg_node): self.checker.visit_call(node)
def testSingleLineClassStmts(self): stmt = astroid.extract_node(""" class MyError(Exception): pass #@ """) self.checker.config.single_line_class_stmt = False with self.assertAddsMessages(Message('multiple-statements', node=stmt.body[0])): self.visitFirst(stmt) self.checker.config.single_line_class_stmt = True with self.assertNoMessages(): self.visitFirst(stmt) stmt = astroid.extract_node(""" class MyError(Exception): a='a' #@ """) self.checker.config.single_line_class_stmt = False with self.assertAddsMessages(Message('multiple-statements', node=stmt.body[0])): self.visitFirst(stmt) self.checker.config.single_line_class_stmt = True with self.assertNoMessages(): self.visitFirst(stmt) stmt = astroid.extract_node(""" class MyError(Exception): a='a'; b='b' #@ """) self.checker.config.single_line_class_stmt = False with self.assertAddsMessages(Message('multiple-statements', node=stmt.body[0])): self.visitFirst(stmt) self.checker.config.single_line_class_stmt = True with self.assertAddsMessages(Message('multiple-statements', node=stmt.body[0])): self.visitFirst(stmt)
def test_check_bad_docstring(self): stmt = astroid.extract_node('def fff():\n """bad coment"""\n pass') with self.assertAddsMessages( Message( "wrong-spelling-in-docstring", line=2, args=( "coment", "bad coment", " ^^^^^^", self._get_msg_suggestions("coment"), ), ) ): self.checker.visit_functiondef(stmt) stmt = astroid.extract_node('class Abc(object):\n """bad coment"""\n pass') with self.assertAddsMessages( Message( "wrong-spelling-in-docstring", line=2, args=( "coment", "bad coment", " ^^^^^^", self._get_msg_suggestions("coment"), ), ) ): self.checker.visit_classdef(stmt)
def test_dict_not_view_method(self): arg_node = astroid.extract_node('x.viewkeys(x) #@') stararg_node = astroid.extract_node('x.viewkeys(*x) #@') kwarg_node = astroid.extract_node('x.viewkeys(y=x) #@') non_dict_node = astroid.extract_node('x=[]\nx.viewkeys() #@') with self.assertNoMessages(): for node in (arg_node, stararg_node, kwarg_node, non_dict_node): self.checker.visit_call(node)
def test_wrong_name_of_func_params_in_numpy_docstring(self): """Example of functions with inconsistent parameter names in the signature and in the Numpy style documentation """ node = astroid.extract_node(""" def function_foo(xarg, yarg, zarg): '''function foo ... Parameters ---------- xarg1: int bla xarg yarg: float bla yarg zarg1: str bla zarg ''' return xarg + yarg """) with self.assertAddsMessages( Message( msg_id='missing-param-doc', node=node, args=('xarg, xarg1, zarg, zarg1',)), Message( msg_id='missing-type-doc', node=node, args=('xarg, xarg1, zarg, zarg1',)), ): self.checker.visit_functiondef(node) node = astroid.extract_node(""" def function_foo(xarg, yarg): '''function foo ... Parameters ---------- yarg1: float bla yarg For the other parameters, see bla. ''' return xarg + yarg """) with self.assertAddsMessages( Message( msg_id='missing-param-doc', node=node, args=('yarg1',)), Message( msg_id='missing-type-doc', node=node, args=('yarg1',)) ): self.checker.visit_functiondef(node)
def test_old_raise_syntax(self): node = astroid.extract_node('raise Exception, "test"') message = testutils.Message('old-raise-syntax', node=node) with self.assertAddsMessages(message): self.checker.visit_raise(node) node = astroid.extract_node('raise Exception, "test", tb') message = testutils.Message('old-raise-syntax', node=node) with self.assertAddsMessages(message): self.checker.visit_raise(node)
def testGetArgumentFromCall(): node = astroid.extract_node("foo(a, not_this_one=1, this_one=2)") arg = utils.get_argument_from_call(node, position=2, keyword="this_one") assert 2 == arg.value node = astroid.extract_node("foo(a)") with pytest.raises(utils.NoSuchArgumentError): utils.get_argument_from_call(node, position=1) with pytest.raises(ValueError): utils.get_argument_from_call(node, None, None) name = utils.get_argument_from_call(node, position=0) assert name.name == "a"
def test_custom_callback_string(self): """ Test the --calbacks option works. """ def cleanup(): self.checker._to_consume = _to_consume _to_consume = self.checker._to_consume self.checker._to_consume = [] self.addCleanup(cleanup) node = astroid.extract_node( """ def callback_one(abc): ''' should not emit unused-argument. ''' """ ) with self.assertNoMessages(): self.checker.visit_functiondef(node) self.checker.leave_functiondef(node) node = astroid.extract_node( """ def two_callback(abc, defg): ''' should not emit unused-argument. ''' """ ) with self.assertNoMessages(): self.checker.visit_functiondef(node) self.checker.leave_functiondef(node) node = astroid.extract_node( """ def normal_func(abc): ''' should emit unused-argument. ''' """ ) with self.assertAddsMessages(Message("unused-argument", node=node["abc"], args="abc")): self.checker.visit_functiondef(node) self.checker.leave_functiondef(node) node = astroid.extract_node( """ def cb_func(abc): ''' Previous callbacks are overriden. ''' """ ) with self.assertAddsMessages(Message("unused-argument", node=node["abc"], args="abc")): self.checker.visit_functiondef(node) self.checker.leave_functiondef(node)
def test_ignored_modules_patterns(self): node = astroid.extract_node(''' import xml xml.etree.portocola #@ ''') with self.assertNoMessages(): self.checker.visit_attribute(node)
def test_existing_func_params_in_numpy_docstring(self): """Example of a function with correctly documented parameters and return values (Numpy style) """ node = astroid.extract_node(""" def function_foo(xarg, yarg, zarg, warg): '''function foo ... Parameters ---------- xarg: int bla xarg yarg: my.qualified.type bla yarg zarg: int bla zarg warg: my.qualified.type bla warg Returns ------- float sum ''' return xarg + yarg """) with self.assertNoMessages(): self.checker.visit_functiondef(node)
def test_constr_params_in_init_numpy(self): """Example of a class with missing constructor parameter documentation (Numpy style) Everything is completely analogous to functions. """ node = astroid.extract_node(""" class ClassFoo(object): def __init__(self, x, y): '''docstring foo constructor Parameters ---------- y: bla missing constructor parameter documentation ''' pass """) constructor_node = node.body[0] with self.assertAddsMessages( Message( msg_id='missing-param-doc', node=constructor_node, args=('x',)), Message( msg_id='missing-type-doc', node=constructor_node, args=('x, y',)) ): self._visit_methods_of_class(node)
def test_existing_func_params_in_sphinx_docstring(self): """Example of a function with correctly documented parameters and return values (Sphinx style) """ node = astroid.extract_node(""" def function_foo(xarg, yarg, zarg, warg): '''function foo ... :param xarg: bla xarg :type xarg: int :parameter yarg: bla yarg :type yarg: my.qualified.type :arg int zarg: bla zarg :keyword my.qualified.type warg: bla warg :return: sum :rtype: float ''' return xarg + yarg """) with self.assertNoMessages(): self.checker.visit_functiondef(node)
def test_missing_func_params_in_numpy_docstring(self): """Example of a function with missing NumPy style parameter documentation in the docstring """ node = astroid.extract_node(""" def function_foo(x, y, z): '''docstring ... Parameters ---------- x: bla z: int bar some other stuff ''' pass """) with self.assertAddsMessages( Message( msg_id='missing-param-doc', node=node, args=('y',)), Message( msg_id='missing-type-doc', node=node, args=('x, y',)) ): self.checker.visit_functiondef(node)
def test_missing_func_params_in_sphinx_docstring(self): """Example of a function with missing Sphinx parameter documentation in the docstring """ node = astroid.extract_node(""" def function_foo(x, y, z): '''docstring ... :param x: bla :param int z: bar ''' pass """) with self.assertAddsMessages( Message( msg_id='missing-param-doc', node=node, args=('y',)), Message( msg_id='missing-type-doc', node=node, args=('x, y',)) ): self.checker.visit_functiondef(node)
def test_ok_str_translate_call_not_str(self): node = astroid.extract_node(''' foobar = {} foobar.translate(None, 'foobar') #@ ''') with self.assertNoMessages(): self.checker.visit_call(node)
def test_missing_method_params_in_numpy_docstring(self): """Example of a class method with missing parameter documentation in the Numpy style docstring """ node = astroid.extract_node(""" class Foo(object): def method_foo(self, x, y): '''docstring ... missing parameter documentation Parameters ---------- x: bla ''' pass """) method_node = node.body[0] with self.assertAddsMessages( Message( msg_id='missing-param-doc', node=method_node, args=('y',)), Message( msg_id='missing-type-doc', node=method_node, args=('x, y',)) ): self._visit_methods_of_class(node)
def test_ok_string_call(self): node = astroid.extract_node(''' import string string.Foramtter() #@ ''') with self.assertNoMessages(): self.checker.visit_call(node)
def test_ok_string_import_from(self): node = astroid.extract_node(''' from string import digits #@ ''') absolute_import_message = testutils.Message('no-absolute-import', node=node) with self.assertAddsMessages(absolute_import_message): self.checker.visit_importfrom(node)
def test_ok_string_attribute(self): node = astroid.extract_node(''' import string string.letters #@ ''') with self.assertNoMessages(): self.checker.visit_attribute(node)
def test_ok_shadowed_call(self): node = astroid.extract_node(''' import six.moves.configparser six.moves.configparser.ConfigParser() #@ ''') with self.assertNoMessages(): self.checker.visit_call(node)
def test_metaclass_assignment(self): node = astroid.extract_node(""" class Foo(object): #@ __metaclass__ = type""") message = testutils.Message('metaclass-assignment', node=node) with self.assertAddsMessages(message): self.checker.visit_classdef(node)
def test_node_ignores_exception(): nodes = astroid.extract_node( """ try: 1/0 #@ except ZeroDivisionError: pass try: 1/0 #@ except Exception: pass try: 2/0 #@ except: pass try: 1/0 #@ except ValueError: pass """ ) assert utils.node_ignores_exception(nodes[0], ZeroDivisionError) assert not utils.node_ignores_exception(nodes[1], ZeroDivisionError) assert utils.node_ignores_exception(nodes[2], ZeroDivisionError) assert not utils.node_ignores_exception(nodes[3], ZeroDivisionError)
def test_invalid_docstring_characters(self): stmt = astroid.extract_node( 'def fff():\n """test\\x00"""\n pass') with self.assertAddsMessages( Message('invalid-characters-in-docstring', line=2, args=('test\x00',))): self.checker.visit_functiondef(stmt)
def test_all_elements_without_parent(self): node = astroid.extract_node('__all__ = []') node.value.elts.append(astroid.Const('test')) root = node.root() with self.assertNoMessages(): self.checker.visit_module(root) self.checker.leave_module(root)
def test_object_maxint(self): node = astroid.extract_node(''' sys = object() sys.maxint #@ ''') with self.assertNoMessages(): self.checker.visit_attribute(node)
def test_unparse_List(self): node = extract_node("[2, 3, 5]") self.assertEqual(self.unparser.unparse_List(node), "[2,3,5]")
def test_unparse_ListComp(self): node = extract_node("[2 for x in range(10)]") self.assertEqual(self.unparser.unparse_ListComp(node), "[2for x in range(10)]")
def test_unparse_TryFinally(self): node = extract_node("try:pass\nfinally:pass") self.assertEqual(self.unparser.unparse_TryFinally(node), "try:pass\nfinally:pass")
def test_len_set(self): node = astroid.extract_node(""" len({'a'}) """) inferred_node = next(node.infer()) assert inferred_node.as_string() == '1'
def test_unparse_Import(self): node = extract_node("import foo as foo, bar") self.assertEqual(self.unparser.unparse_Import(node), "import foo as foo,bar")
def test_print_statement(self): node = astroid.extract_node('print "Hello, World!" #@') message = testutils.Message('print-statement', node=node) with self.assertAddsMessages(message): self.checker.visit_print(node)
def as_iterable_in_unpacking(self, fxn): node = astroid.extract_node(""" a, b = __({}()) """.format(fxn)) with self.assertNoMessages(): self.checker.visit_call(node)
import astroid from pylint_enums.checker import EXCLUDED_SIMPLE_TYPES IMPORT_TEST_CASES = ((astroid.extract_node('import foo'), False), (astroid.extract_node('import enum'), True), (astroid.extract_node('import foo as enum'), False), (astroid.extract_node('import enum as foo'), True), (astroid.extract_node('import foo, enum'), True), (astroid.extract_node('import foo, bar'), False), (astroid.extract_node('import foo, bar, enum as baz'), True)) IMPORTFROM_TEST_CASES = ((astroid.extract_node('from enum import Enum'), True), (astroid.extract_node('from enum import *'), True), (astroid.extract_node('from enum import EnumMeta'), False), (astroid.extract_node('from foo import bar'), False), (astroid.extract_node('from foo import bar as enum'), False)) CLASSDEF_TEST_CASES = ((astroid.extract_node(""" class Foo(Enum): #@ A = 'a' B = 'b' """), ('pylint-enums-no-annotated-value', )), *[(astroid.extract_node(f""" class Foo(Enum): #@ value: {excluded_type} A = 'a' B = 'b' """), ()) for excluded_type in EXCLUDED_SIMPLE_TYPES],
def test_infer_dict_from_keys(): bad_nodes = astroid.extract_node(''' dict.fromkeys() #@ dict.fromkeys(1, 2, 3) #@ dict.fromkeys(a=1) #@ ''') for node in bad_nodes: with pytest.raises(astroid.InferenceError): next(node.infer()) # Test uninferable values good_nodes = astroid.extract_node(''' from unknown import Unknown dict.fromkeys(some_value) #@ dict.fromkeys(some_other_value) #@ dict.fromkeys([Unknown(), Unknown()]) #@ dict.fromkeys([Unknown(), Unknown()]) #@ ''') for node in good_nodes: inferred = next(node.infer()) assert isinstance(inferred, astroid.Dict) assert inferred.items == [] # Test inferrable values # from a dictionary's keys from_dict = astroid.extract_node(''' dict.fromkeys({'a':2, 'b': 3, 'c': 3}) #@ ''') inferred = next(from_dict.infer()) assert isinstance(inferred, astroid.Dict) itered = inferred.itered() assert all(isinstance(elem, astroid.Const) for elem in itered) actual_values = [elem.value for elem in itered] assert sorted(actual_values) == ['a', 'b', 'c'] # from a string from_string = astroid.extract_node(''' dict.fromkeys('abc') ''') inferred = next(from_string.infer()) assert isinstance(inferred, astroid.Dict) itered = inferred.itered() assert all(isinstance(elem, astroid.Const) for elem in itered) actual_values = [elem.value for elem in itered] assert sorted(actual_values) == ['a', 'b', 'c'] # from bytes from_bytes = astroid.extract_node(''' dict.fromkeys(b'abc') ''') inferred = next(from_bytes.infer()) assert isinstance(inferred, astroid.Dict) itered = inferred.itered() assert all(isinstance(elem, astroid.Const) for elem in itered) actual_values = [elem.value for elem in itered] assert sorted(actual_values) == [97, 98, 99] # From list/set/tuple from_others = astroid.extract_node(''' dict.fromkeys(('a', 'b', 'c')) #@ dict.fromkeys(['a', 'b', 'c']) #@ dict.fromkeys({'a', 'b', 'c'}) #@ ''') for node in from_others: inferred = next(node.infer()) assert isinstance(inferred, astroid.Dict) itered = inferred.itered() assert all(isinstance(elem, astroid.Const) for elem in itered) actual_values = [elem.value for elem in itered] assert sorted(actual_values) == ['a', 'b', 'c']
def test_unparse_Set(self): node = extract_node("{2, 3, 5}") self.assertEqual(self.unparser.unparse_Set(node), "{2,3,5}")
def test_len_failure_missing_variable(self): node = astroid.extract_node(""" len(a) """) with pytest.raises(astroid.InferenceError): next(node.infer())
def test_len_string(self): node = astroid.extract_node(""" len("uwu") """) assert next(node.infer()).as_string() == "3"
def test_unparse_Lambda(self): node = extract_node("lambda x, y: (x, y)") self.assertEqual(self.unparser.unparse_Lambda(node), "lambda x,y:x,y")
def _get_result_node(code): node = next(astroid.extract_node(code).infer()) return node
def test_builtins_inference_after_clearing_cache(self) -> None: astroid.MANAGER.clear_cache() isinstance_call = astroid.extract_node("isinstance(1, int)") inferred = next(isinstance_call.infer()) self.assertIs(inferred.value, True)
def test_len_tuple(self): node = astroid.extract_node(""" len(('a','b','c')) """) node = next(node.infer()) assert node.as_string() == '3'
class Python3Checker(checkers.BaseChecker): __implements__ = interfaces.IAstroidChecker enabled = False name = 'python3' msgs = { # Errors for what will syntactically break in Python 3, warnings for # everything else. 'E1601': ('print statement used', 'print-statement', 'Used when a print statement is used ' '(`print` is a function in Python 3)'), 'E1602': ('Parameter unpacking specified', 'parameter-unpacking', 'Used when parameter unpacking is specified for a function' "(Python 3 doesn't allow it)"), 'E1603': ('Implicit unpacking of exceptions is not supported ' 'in Python 3', 'unpacking-in-except', 'Python3 will not allow implicit unpacking of ' 'exceptions in except clauses. ' 'See http://www.python.org/dev/peps/pep-3110/', { 'old_names': [('W0712', 'unpacking-in-except')] }), 'E1604': ('Use raise ErrorClass(args) instead of ' 'raise ErrorClass, args.', 'old-raise-syntax', "Used when the alternate raise syntax " "'raise foo, bar' is used " "instead of 'raise foo(bar)'.", { 'old_names': [('W0121', 'old-raise-syntax')] }), 'E1605': ('Use of the `` operator', 'backtick', 'Used when the deprecated "``" (backtick) operator is used ' 'instead of the str() function.', { 'scope': WarningScope.NODE, 'old_names': [('W0333', 'backtick')] }), 'E1609': ('Import * only allowed at module level', 'import-star-module-level', 'Used when the import star syntax is used somewhere ' 'else than the module level.', { 'maxversion': (3, 0) }), 'W1601': ('apply built-in referenced', 'apply-builtin', 'Used when the apply built-in function is referenced ' '(missing from Python 3)'), 'W1602': ('basestring built-in referenced', 'basestring-builtin', 'Used when the basestring built-in function is referenced ' '(missing from Python 3)'), 'W1603': ('buffer built-in referenced', 'buffer-builtin', 'Used when the buffer built-in function is referenced ' '(missing from Python 3)'), 'W1604': ('cmp built-in referenced', 'cmp-builtin', 'Used when the cmp built-in function is referenced ' '(missing from Python 3)'), 'W1605': ('coerce built-in referenced', 'coerce-builtin', 'Used when the coerce built-in function is referenced ' '(missing from Python 3)'), 'W1606': ('execfile built-in referenced', 'execfile-builtin', 'Used when the execfile built-in function is referenced ' '(missing from Python 3)'), 'W1607': ('file built-in referenced', 'file-builtin', 'Used when the file built-in function is referenced ' '(missing from Python 3)'), 'W1608': ('long built-in referenced', 'long-builtin', 'Used when the long built-in function is referenced ' '(missing from Python 3)'), 'W1609': ('raw_input built-in referenced', 'raw_input-builtin', 'Used when the raw_input built-in function is referenced ' '(missing from Python 3)'), 'W1610': ('reduce built-in referenced', 'reduce-builtin', 'Used when the reduce built-in function is referenced ' '(missing from Python 3)'), 'W1611': ('StandardError built-in referenced', 'standarderror-builtin', 'Used when the StandardError built-in function is referenced ' '(missing from Python 3)'), 'W1612': ('unicode built-in referenced', 'unicode-builtin', 'Used when the unicode built-in function is referenced ' '(missing from Python 3)'), 'W1613': ('xrange built-in referenced', 'xrange-builtin', 'Used when the xrange built-in function is referenced ' '(missing from Python 3)'), 'W1614': ('__coerce__ method defined', 'coerce-method', 'Used when a __coerce__ method is defined ' '(method is not used by Python 3)'), 'W1615': ('__delslice__ method defined', 'delslice-method', 'Used when a __delslice__ method is defined ' '(method is not used by Python 3)'), 'W1616': ('__getslice__ method defined', 'getslice-method', 'Used when a __getslice__ method is defined ' '(method is not used by Python 3)'), 'W1617': ('__setslice__ method defined', 'setslice-method', 'Used when a __setslice__ method is defined ' '(method is not used by Python 3)'), 'W1618': ('import missing `from __future__ import absolute_import`', 'no-absolute-import', 'Used when an import is not accompanied by ' '``from __future__ import absolute_import`` ' '(default behaviour in Python 3)'), 'W1619': ('division w/o __future__ statement', 'old-division', 'Used for non-floor division w/o a float literal or ' '``from __future__ import division`` ' '(Python 3 returns a float for int division unconditionally)'), 'W1620': ('Calling a dict.iter*() method', 'dict-iter-method', 'Used for calls to dict.iterkeys(), itervalues() or iteritems() ' '(Python 3 lacks these methods)'), 'W1621': ('Calling a dict.view*() method', 'dict-view-method', 'Used for calls to dict.viewkeys(), viewvalues() or viewitems() ' '(Python 3 lacks these methods)'), 'W1622': ('Called a next() method on an object', 'next-method-called', "Used when an object's next() method is called " '(Python 3 uses the next() built-in function)'), 'W1623': ("Assigning to a class's __metaclass__ attribute", 'metaclass-assignment', "Used when a metaclass is specified by assigning to __metaclass__ " '(Python 3 specifies the metaclass as a class statement argument)'), 'W1624': ('Indexing exceptions will not work on Python 3', 'indexing-exception', 'Indexing exceptions will not work on Python 3. Use ' '`exception.args[index]` instead.', { 'old_names': [('W0713', 'indexing-exception')] }), 'W1625': ('Raising a string exception', 'raising-string', 'Used when a string exception is raised. This will not ' 'work on Python 3.', { 'old_names': [('W0701', 'raising-string')] }), 'W1626': ('reload built-in referenced', 'reload-builtin', 'Used when the reload built-in function is referenced ' '(missing from Python 3). You can use instead imp.reload ' 'or importlib.reload.'), 'W1627': ('__oct__ method defined', 'oct-method', 'Used when an __oct__ method is defined ' '(method is not used by Python 3)'), 'W1628': ('__hex__ method defined', 'hex-method', 'Used when a __hex__ method is defined ' '(method is not used by Python 3)'), 'W1629': ('__nonzero__ method defined', 'nonzero-method', 'Used when a __nonzero__ method is defined ' '(method is not used by Python 3)'), 'W1630': ('__cmp__ method defined', 'cmp-method', 'Used when a __cmp__ method is defined ' '(method is not used by Python 3)'), # 'W1631': replaced by W1636 'W1632': ('input built-in referenced', 'input-builtin', 'Used when the input built-in is referenced ' '(backwards-incompatible semantics in Python 3)'), 'W1633': ('round built-in referenced', 'round-builtin', 'Used when the round built-in is referenced ' '(backwards-incompatible semantics in Python 3)'), 'W1634': ('intern built-in referenced', 'intern-builtin', 'Used when the intern built-in is referenced ' '(Moved to sys.intern in Python 3)'), 'W1635': ('unichr built-in referenced', 'unichr-builtin', 'Used when the unichr built-in is referenced ' '(Use chr in Python 3)'), 'W1636': ('map built-in referenced when not iterating', 'map-builtin-not-iterating', 'Used when the map built-in is referenced in a non-iterating ' 'context (returns an iterator in Python 3)', { 'old_names': [('W1631', 'implicit-map-evaluation')] }), 'W1637': ('zip built-in referenced when not iterating', 'zip-builtin-not-iterating', 'Used when the zip built-in is referenced in a non-iterating ' 'context (returns an iterator in Python 3)'), 'W1638': ('range built-in referenced when not iterating', 'range-builtin-not-iterating', 'Used when the range built-in is referenced in a non-iterating ' 'context (returns an iterator in Python 3)'), 'W1639': ('filter built-in referenced when not iterating', 'filter-builtin-not-iterating', 'Used when the filter built-in is referenced in a non-iterating ' 'context (returns an iterator in Python 3)'), 'W1640': ('Using the cmp argument for list.sort / sorted', 'using-cmp-argument', 'Using the cmp argument for list.sort or the sorted ' 'builtin should be avoided, since it was removed in ' 'Python 3. Using either `key` or `functools.cmp_to_key` ' 'should be preferred.'), 'W1641': ('Implementing __eq__ without also implementing __hash__', 'eq-without-hash', 'Used when a class implements __eq__ but not __hash__. In Python 2, objects ' 'get object.__hash__ as the default implementation, in Python 3 objects get ' 'None as their default __hash__ implementation if they also implement __eq__.' ), 'W1642': ('__div__ method defined', 'div-method', 'Used when a __div__ method is defined. Using `__truediv__` and setting' '__div__ = __truediv__ should be preferred.' '(method is not used by Python 3)'), 'W1643': ('__idiv__ method defined', 'idiv-method', 'Used when an __idiv__ method is defined. Using `__itruediv__` and setting' '__idiv__ = __itruediv__ should be preferred.' '(method is not used by Python 3)'), 'W1644': ('__rdiv__ method defined', 'rdiv-method', 'Used when a __rdiv__ method is defined. Using `__rtruediv__` and setting' '__rdiv__ = __rtruediv__ should be preferred.' '(method is not used by Python 3)'), 'W1645': ('Exception.message removed in Python 3', 'exception-message-attribute', 'Used when the message attribute is accessed on an Exception. Use ' 'str(exception) instead.'), 'W1646': ('non-text encoding used in str.decode', 'invalid-str-codec', 'Used when using str.encode or str.decode with a non-text encoding. Use ' 'codecs module to handle arbitrary codecs.'), 'W1647': ('sys.maxint removed in Python 3', 'sys-max-int', 'Used when accessing sys.maxint. Use sys.maxsize instead.'), 'W1648': ('Module moved in Python 3', 'bad-python3-import', 'Used when importing a module that no longer exists in Python 3.'), 'W1649': ('Accessing a deprecated function on the string module', 'deprecated-string-function', 'Used when accessing a string function that has been deprecated in Python 3.' ), 'W1650': ('Using str.translate with deprecated deletechars parameters', 'deprecated-str-translate-call', 'Used when using the deprecated deletechars parameters from str.translate. Use ' 're.sub to remove the desired characters '), 'W1651': ('Accessing a deprecated function on the itertools module', 'deprecated-itertools-function', 'Used when accessing a function on itertools that has been removed in Python 3.' ), 'W1652': ('Accessing a deprecated fields on the types module', 'deprecated-types-field', 'Used when accessing a field on types that has been removed in Python 3.' ), 'W1653': ( 'next method defined', 'next-method-defined', 'Used when a next method is defined that would be an iterator in Python 2 but ' 'is treated as a normal function in Python 3.', ), 'W1654': ( 'dict.items referenced when not iterating', 'dict-items-not-iterating', 'Used when dict.items is referenced in a non-iterating ' 'context (returns an iterator in Python 3)', ), 'W1655': ( 'dict.keys referenced when not iterating', 'dict-keys-not-iterating', 'Used when dict.keys is referenced in a non-iterating ' 'context (returns an iterator in Python 3)', ), 'W1656': ( 'dict.values referenced when not iterating', 'dict-values-not-iterating', 'Used when dict.values is referenced in a non-iterating ' 'context (returns an iterator in Python 3)', ), 'W1657': ( 'Accessing a removed attribute on the operator module', 'deprecated-operator-function', 'Used when accessing a field on operator module that has been ' 'removed in Python 3.', ), 'W1658': ( 'Accessing a removed attribute on the urllib module', 'deprecated-urllib-function', 'Used when accessing a field on urllib module that has been ' 'removed or moved in Python 3.', ), 'W1659': ( 'Accessing a removed xreadlines attribute', 'xreadlines-attribute', 'Used when accessing the xreadlines() function on a file stream, ' 'removed in Python 3.', ), 'W1660': ( 'Accessing a removed attribute on the sys module', 'deprecated-sys-function', 'Used when accessing a field on sys module that has been ' 'removed in Python 3.', ), 'W1661': ('Using an exception object that was bound by an except handler', 'exception-escape', 'Emitted when using an exception, that was bound in an except ' 'handler, outside of the except handler. On Python 3 these ' 'exceptions will be deleted once they get out ' 'of the except handler.'), 'W1662': ('Using a variable that was bound inside a comprehension', 'comprehension-escape', 'Emitted when using a variable, that was bound in a comprehension ' 'handler, outside of the comprehension itself. On Python 3 these ' 'variables will be deleted outside of the ' 'comprehension.'), } _bad_builtins = frozenset([ 'apply', 'basestring', 'buffer', 'cmp', 'coerce', 'execfile', 'file', 'input', # Not missing, but incompatible semantics 'intern', 'long', 'raw_input', 'reduce', 'round', # Not missing, but incompatible semantics 'StandardError', 'unichr', 'unicode', 'xrange', 'reload', ]) _unused_magic_methods = frozenset([ '__coerce__', '__delslice__', '__getslice__', '__setslice__', '__oct__', '__hex__', '__nonzero__', '__cmp__', '__div__', '__idiv__', '__rdiv__', ]) _invalid_encodings = frozenset([ 'base64_codec', 'base64', 'base_64', 'bz2_codec', 'bz2', 'hex_codec', 'hex', 'quopri_codec', 'quopri', 'quotedprintable', 'quoted_printable', 'uu_codec', 'uu', 'zlib_codec', 'zlib', 'zip', 'rot13', 'rot_13', ]) _bad_python3_module_map = { 'sys-max-int': { 'sys': frozenset(['maxint']) }, 'deprecated-itertools-function': { 'itertools': frozenset( ['izip', 'ifilter', 'imap', 'izip_longest', 'ifilterfalse']) }, 'deprecated-types-field': { 'types': frozenset([ 'EllipsisType', 'XRangeType', 'ComplexType', 'StringType', 'TypeType', 'LongType', 'UnicodeType', 'ClassType', 'BufferType', 'StringTypes', 'NotImplementedType', 'NoneType', 'InstanceType', 'FloatType', 'SliceType', 'UnboundMethodType', 'ObjectType', 'IntType', 'TupleType', 'ListType', 'DictType', 'FileType', 'DictionaryType', 'BooleanType', 'DictProxyType' ]) }, 'bad-python3-import': frozenset([ 'anydbm', 'BaseHTTPServer', '__builtin__', 'CGIHTTPServer', 'ConfigParser', 'copy_reg', 'cPickle', 'cStringIO', 'Cookie', 'cookielib', 'dbhash', 'dbm', 'dumbdbm', 'dumbdb', 'Dialog', 'DocXMLRPCServer', 'FileDialog', 'FixTk', 'gdbm', 'htmlentitydefs', 'HTMLParser', 'httplib', 'markupbase', 'Queue', 'repr', 'robotparser', 'ScrolledText', 'SimpleDialog', 'SimpleHTTPServer', 'SimpleXMLRPCServer', 'StringIO', 'dummy_thread', 'SocketServer', 'test.test_support', 'Tkinter', 'Tix', 'Tkconstants', 'tkColorChooser', 'tkCommonDialog', 'Tkdnd', 'tkFileDialog', 'tkFont', 'tkMessageBox', 'tkSimpleDialog', 'turtle', 'UserList', 'UserString', 'whichdb', '_winreg', 'xmlrpclib', 'audiodev', 'Bastion', 'bsddb185', 'bsddb3', 'Canvas', 'cfmfile', 'cl', 'commands', 'compiler', 'dircache', 'dl', 'exception', 'fpformat', 'htmllib', 'ihooks', 'imageop', 'imputil', 'linuxaudiodev', 'md5', 'mhlib', 'mimetools', 'MimeWriter', 'mimify', 'multifile', 'mutex', 'new', 'popen2', 'posixfile', 'pure', 'rexec', 'rfc822', 'sets', 'sha', 'sgmllib', 'sre', 'stringold', 'sunaudio', 'sv', 'test.testall', 'thread', 'timing', 'toaiff', 'user', 'urllib2', 'urlparse' ]), 'deprecated-string-function': { 'string': frozenset([ 'maketrans', 'atof', 'atoi', 'atol', 'capitalize', 'expandtabs', 'find', 'rfind', 'index', 'rindex', 'count', 'lower', 'letters', 'split', 'rsplit', 'splitfields', 'join', 'joinfields', 'lstrip', 'rstrip', 'strip', 'swapcase', 'translate', 'upper', 'ljust', 'rjust', 'center', 'zfill', 'replace', 'lowercase', 'letters', 'uppercase', 'atol_error', 'atof_error', 'atoi_error', 'index_error' ]) }, 'deprecated-operator-function': { 'operator': frozenset({'div'}), }, 'deprecated-urllib-function': { 'urllib': frozenset({ 'addbase', 'addclosehook', 'addinfo', 'addinfourl', 'always_safe', 'basejoin', 'ftpcache', 'ftperrors', 'ftpwrapper', 'getproxies', 'getproxies_environment', 'getproxies_macosx_sysconf', 'main', 'noheaders', 'pathname2url', 'proxy_bypass', 'proxy_bypass_environment', 'proxy_bypass_macosx_sysconf', 'quote', 'quote_plus', 'reporthook', 'splitattr', 'splithost', 'splitnport', 'splitpasswd', 'splitport', 'splitquery', 'splittag', 'splittype', 'splituser', 'splitvalue', 'unquote', 'unquote_plus', 'unwrap', 'url2pathname', 'urlcleanup', 'urlencode', 'urlopen', 'urlretrieve' }), }, 'deprecated-sys-function': { 'sys': frozenset({'exc_clear'}), } } if (3, 4) <= sys.version_info < (3, 4, 4): # Python 3.4.0 -> 3.4.3 has a bug which breaks `repr_tree()`: # https://bugs.python.org/issue23572 _python_2_tests = frozenset() else: _python_2_tests = frozenset([ astroid.extract_node(x).repr_tree() for x in [ 'sys.version_info[0] == 2', 'sys.version_info[0] < 3', 'sys.version_info == (2, 7)', 'sys.version_info <= (2, 7)', 'sys.version_info < (3, 0)', ] ]) def __init__(self, *args, **kwargs): self._future_division = False self._future_absolute_import = False self._modules_warned_about = set() self._branch_stack = [] super(Python3Checker, self).__init__(*args, **kwargs) # pylint: disable=keyword-arg-before-vararg def add_message( self, msg_id, always_warn=False, # pylint: disable=arguments-differ *args, **kwargs): if always_warn or not (self._branch_stack and self._branch_stack[-1].is_py2_only): super(Python3Checker, self).add_message(msg_id, *args, **kwargs) def _is_py2_test(self, node): if isinstance(node.test, astroid.Attribute) and isinstance( node.test.expr, astroid.Name): if node.test.expr.name == 'six' and node.test.attrname == 'PY2': return True elif (isinstance(node.test, astroid.Compare) and node.test.repr_tree() in self._python_2_tests): return True return False def visit_if(self, node): self._branch_stack.append(Branch(node, self._is_py2_test(node))) def leave_if(self, node): assert self._branch_stack.pop().node == node def visit_ifexp(self, node): self._branch_stack.append(Branch(node, self._is_py2_test(node))) def leave_ifexp(self, node): assert self._branch_stack.pop().node == node def visit_module(self, node): # pylint: disable=unused-argument """Clear checker state after previous module.""" self._future_division = False self._future_absolute_import = False def visit_functiondef(self, node): if node.is_method(): if node.name in self._unused_magic_methods: method_name = node.name if node.name.startswith('__'): method_name = node.name[2:-2] self.add_message(method_name + '-method', node=node) elif node.name == 'next': # If there is a method named `next` declared, if it is invokable # with zero arguments then it implements the Iterator protocol. # This means if the method is an instance method or a # classmethod 1 argument should cause a failure, if it is a # staticmethod 0 arguments should cause a failure. failing_arg_count = 1 if utils.decorated_with(node, [bases.BUILTINS + ".staticmethod"]): failing_arg_count = 0 if len(node.args.args) == failing_arg_count: self.add_message('next-method-defined', node=node) @utils.check_messages('parameter-unpacking') def visit_arguments(self, node): for arg in node.args: if isinstance(arg, astroid.Tuple): self.add_message('parameter-unpacking', node=arg) @utils.check_messages('comprehension-escape') def visit_listcomp(self, node): names = { generator.target.name for generator in node.generators if isinstance(generator.target, astroid.AssignName) } scope = node.parent.scope() scope_names = scope.nodes_of_class( astroid.Name, skip_klass=astroid.FunctionDef, ) has_redefined_assign_name = any( assign_name for assign_name in scope.nodes_of_class( astroid.AssignName, skip_klass=astroid.FunctionDef, ) if assign_name.name in names and assign_name.lineno > node.lineno) if has_redefined_assign_name: return emitted_for_names = set() scope_names = list(scope_names) for scope_name in scope_names: if (scope_name.name not in names or scope_name.lineno <= node.lineno or scope_name.name in emitted_for_names or scope_name.scope() == node): continue emitted_for_names.add(scope_name.name) self.add_message('comprehension-escape', node=scope_name) def visit_name(self, node): """Detect when a "bad" built-in is referenced.""" found_node, _ = node.lookup(node.name) if _is_builtin(found_node): if node.name in self._bad_builtins: message = node.name.lower() + '-builtin' self.add_message(message, node=node) @utils.check_messages('print-statement') def visit_print(self, node): self.add_message('print-statement', node=node, always_warn=True) def _warn_if_deprecated(self, node, module, attributes, report_on_modules=True): for message, module_map in self._bad_python3_module_map.items(): if module in module_map and module not in self._modules_warned_about: if isinstance(module_map, frozenset): if report_on_modules: self._modules_warned_about.add(module) self.add_message(message, node=node) elif attributes and module_map[module].intersection( attributes): self.add_message(message, node=node) def visit_importfrom(self, node): if node.modname == '__future__': for name, _ in node.names: if name == 'division': self._future_division = True elif name == 'absolute_import': self._future_absolute_import = True else: if not self._future_absolute_import: if self.linter.is_message_enabled('no-absolute-import'): self.add_message('no-absolute-import', node=node) self._future_absolute_import = True if not _is_conditional_import(node) and not node.level: self._warn_if_deprecated(node, node.modname, {x[0] for x in node.names}) if node.names[0][0] == '*': if self.linter.is_message_enabled('import-star-module-level'): if not isinstance(node.scope(), astroid.Module): self.add_message('import-star-module-level', node=node) def visit_import(self, node): if not self._future_absolute_import: if self.linter.is_message_enabled('no-absolute-import'): self.add_message('no-absolute-import', node=node) self._future_absolute_import = True if not _is_conditional_import(node): for name, _ in node.names: self._warn_if_deprecated(node, name, None) @utils.check_messages('metaclass-assignment') def visit_classdef(self, node): if '__metaclass__' in node.locals: self.add_message('metaclass-assignment', node=node) locals_and_methods = set(node.locals).union(x.name for x in node.mymethods()) if '__eq__' in locals_and_methods and '__hash__' not in locals_and_methods: self.add_message('eq-without-hash', node=node) @utils.check_messages('old-division') def visit_binop(self, node): if not self._future_division and node.op == '/': for arg in (node.left, node.right): if isinstance(arg, astroid.Const) and isinstance( arg.value, float): break else: self.add_message('old-division', node=node) def _check_cmp_argument(self, node): # Check that the `cmp` argument is used kwargs = [] if (isinstance(node.func, astroid.Attribute) and node.func.attrname == 'sort'): inferred = utils.safe_infer(node.func.expr) if not inferred: return builtins_list = "{}.list".format(bases.BUILTINS) if (isinstance(inferred, astroid.List) or inferred.qname() == builtins_list): kwargs = node.keywords elif (isinstance(node.func, astroid.Name) and node.func.name == 'sorted'): inferred = utils.safe_infer(node.func) if not inferred: return builtins_sorted = "{}.sorted".format(bases.BUILTINS) if inferred.qname() == builtins_sorted: kwargs = node.keywords for kwarg in kwargs or []: if kwarg.arg == 'cmp': self.add_message('using-cmp-argument', node=node) return @staticmethod def _is_constant_string_or_name(node): if isinstance(node, astroid.Const): return isinstance(node.value, str) return isinstance(node, astroid.Name) @staticmethod def _is_none(node): return isinstance(node, astroid.Const) and node.value is None @staticmethod def _has_only_n_positional_args(node, number_of_args): return len(node.args) == number_of_args and all( node.args) and not node.keywords @staticmethod def _could_be_string(inferred_types): confidence = INFERENCE if inferred_types else INFERENCE_FAILURE for inferred_type in inferred_types: if inferred_type is astroid.Uninferable: confidence = INFERENCE_FAILURE elif not (isinstance(inferred_type, astroid.Const) and isinstance(inferred_type.value, str)): return None return confidence def visit_call(self, node): self._check_cmp_argument(node) if isinstance(node.func, astroid.Attribute): inferred_types = set() try: for inferred_receiver in node.func.expr.infer(): if inferred_receiver is astroid.Uninferable: continue inferred_types.add(inferred_receiver) if isinstance(inferred_receiver, astroid.Module): self._warn_if_deprecated(node, inferred_receiver.name, {node.func.attrname}, report_on_modules=False) if (_inferred_value_is_dict(inferred_receiver) and node.func.attrname in DICT_METHODS): if not _in_iterating_context(node): checker = 'dict-{}-not-iterating'.format( node.func.attrname) self.add_message(checker, node=node) except astroid.InferenceError: pass if node.args: is_str_confidence = self._could_be_string(inferred_types) if is_str_confidence: if (node.func.attrname in ('encode', 'decode') and len(node.args) >= 1 and node.args[0]): first_arg = node.args[0] self._validate_encoding(first_arg, node) if (node.func.attrname == 'translate' and self._has_only_n_positional_args(node, 2) and self._is_none(node.args[0]) and self._is_constant_string_or_name(node.args[1])): # The above statement looking for calls of the form: # # foo.translate(None, 'abc123') # # or # # foo.translate(None, some_variable) # # This check is somewhat broad and _may_ have some false positives, but # after checking several large codebases it did not have any false # positives while finding several real issues. This call pattern seems # rare enough that the trade off is worth it. self.add_message('deprecated-str-translate-call', node=node, confidence=is_str_confidence) return if node.keywords: return if node.func.attrname == 'next': self.add_message('next-method-called', node=node) else: if _check_dict_node(node.func.expr): if node.func.attrname in ('iterkeys', 'itervalues', 'iteritems'): self.add_message('dict-iter-method', node=node) elif node.func.attrname in ('viewkeys', 'viewvalues', 'viewitems'): self.add_message('dict-view-method', node=node) elif isinstance(node.func, astroid.Name): found_node = node.func.lookup(node.func.name)[0] if _is_builtin(found_node): if node.func.name in ('filter', 'map', 'range', 'zip'): if not _in_iterating_context(node): checker = '{}-builtin-not-iterating'.format( node.func.name) self.add_message(checker, node=node) if node.func.name == 'open' and node.keywords: kwargs = node.keywords for kwarg in kwargs or []: if kwarg.arg == 'encoding': self._validate_encoding(kwarg.value, node) break def _validate_encoding(self, encoding, node): if isinstance(encoding, astroid.Const): value = encoding.value if value in self._invalid_encodings: self.add_message('invalid-str-codec', node=node) @utils.check_messages('indexing-exception') def visit_subscript(self, node): """ Look for indexing exceptions. """ try: for inferred in node.value.infer(): if not isinstance(inferred, astroid.Instance): continue if utils.inherit_from_std_ex(inferred): self.add_message('indexing-exception', node=node) except astroid.InferenceError: return def visit_assignattr(self, node): if isinstance(node.assign_type(), astroid.AugAssign): self.visit_attribute(node) def visit_delattr(self, node): self.visit_attribute(node) @utils.check_messages('exception-message-attribute', 'xreadlines-attribute') def visit_attribute(self, node): """Look for removed attributes""" if node.attrname == 'xreadlines': self.add_message('xreadlines-attribute', node=node) return exception_message = 'message' try: for inferred in node.expr.infer(): if (isinstance(inferred, astroid.Instance) and utils.inherit_from_std_ex(inferred)): if node.attrname == exception_message: # Exceptions with .message clearly defined are an exception if exception_message in inferred.instance_attrs: continue self.add_message('exception-message-attribute', node=node) if isinstance(inferred, astroid.Module): self._warn_if_deprecated(node, inferred.name, {node.attrname}, report_on_modules=False) except astroid.InferenceError: return @utils.check_messages('unpacking-in-except', 'comprehension-escape') def visit_excepthandler(self, node): """Visit an except handler block and check for exception unpacking.""" def _is_used_in_except_block(node): scope = node.scope() current = node while current and current != scope and not isinstance( current, astroid.ExceptHandler): current = current.parent return isinstance(current, astroid.ExceptHandler) and current.type != node if isinstance(node.name, (astroid.Tuple, astroid.List)): self.add_message('unpacking-in-except', node=node) return if not node.name: return # Find any names scope = node.parent.scope() scope_names = scope.nodes_of_class( astroid.Name, skip_klass=astroid.FunctionDef, ) scope_names = list(scope_names) potential_leaked_names = [ scope_name for scope_name in scope_names if scope_name.name == node.name.name and scope_name.lineno > node.lineno and not _is_used_in_except_block(scope_name) ] reassignments_for_same_name = { assign_name.lineno for assign_name in scope.nodes_of_class( astroid.AssignName, skip_klass=astroid.FunctionDef, ) if assign_name.name == node.name.name } for leaked_name in potential_leaked_names: if any(node.lineno < elem < leaked_name.lineno for elem in reassignments_for_same_name): continue self.add_message('exception-escape', node=leaked_name) @utils.check_messages('backtick') def visit_repr(self, node): self.add_message('backtick', node=node) @utils.check_messages('raising-string', 'old-raise-syntax') def visit_raise(self, node): """Visit a raise statement and check for raising strings or old-raise-syntax. """ # Ignore empty raise. if node.exc is None: return expr = node.exc if self._check_raise_value(node, expr): return try: value = next(astroid.unpack_infer(expr)) except astroid.InferenceError: return self._check_raise_value(node, value) def _check_raise_value(self, node, expr): if isinstance(expr, astroid.Const): value = expr.value if isinstance(value, str): self.add_message('raising-string', node=node) return True return None
def test_builtins_inference_after_clearing_cache_manually(self) -> None: # Not recommended to manipulate this, so we detect it and call clear_cache() instead astroid.MANAGER.brain["astroid_cache"].clear() isinstance_call = astroid.extract_node("isinstance(1, int)") inferred = next(isinstance_call.infer()) self.assertIs(inferred.value, True)
def test_unparse_AssignName(self): node = extract_node("a = 2") self.assertEqual(self.unparser.unparse_Assign(node), "a=2")
def test_brain_plugins_reloaded_after_clearing_cache(self) -> None: astroid.MANAGER.clear_cache() format_call = astroid.extract_node("''.format()") inferred = next(format_call.infer()) self.assertIsInstance(inferred, Const)
def test_unparse_Arguments(self): node = extract_node("def f(foo, bar=None): pass").args self.assertEqual(self.unparser.unparse_Arguments(node), "foo,bar=None")
def transform(f): if f.name == 'logger': for prop in ['debug', 'info', 'warning', 'error', 'addHandler']: f.instance_attrs[prop] = extract_node( 'def {name}(arg): return'.format(name=prop))
def test_unparse_IfExp(self): node = extract_node("5 if {} else 2") self.assertEqual(self.unparser.unparse_IfExp(node), "5if{}else 2")
def test_unparse_AssignAttr(self): node = extract_node("self.attribute = 2") self.assertEqual(self.unparser.unparse_Assign(node), "self.attribute=2")
def test_backtick(self): node = astroid.extract_node('`test`') message = testutils.Message('backtick', node=node) with self.assertAddsMessages(message): self.checker.visit_repr(node)
def test_unparse_YieldFrom(self): node = extract_node("yield from True") self.assertEqual(self.unparser.unparse_YieldFrom(node), "yield from True")
def test_unparse_Return(self): node = extract_node("return []") self.assertEqual(self.unparser.unparse_Return(node), "return[]")
def test_unparse_Nonlocal(self): node = extract_node("nonlocal a, b") self.assertEqual(self.unparser.unparse_Nonlocal(node), "nonlocal a,b")
def test_len_bytes(self): node = astroid.extract_node(""" len(b'uwu') """) assert next(node.infer()).as_string() == '3'
def test_unparse_SetComp(self): node = extract_node("{2 for x in range(10)}") self.assertEqual(self.unparser.unparse_SetComp(node), "{2for x in range(10)}")