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() == []
示例#3
0
 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)
示例#9
0
 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)
示例#10
0
    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"
示例#13
0
    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)
示例#15
0
    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)
示例#16
0
    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)
示例#17
0
    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)
示例#18
0
    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)
示例#19
0
    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)
示例#20
0
 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)
示例#21
0
    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)
示例#22
0
 def test_ok_string_call(self):
     node = astroid.extract_node('''
     import string
     string.Foramtter() #@
     ''')
     with self.assertNoMessages():
         self.checker.visit_call(node)
示例#23
0
 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)
示例#24
0
 def test_ok_string_attribute(self):
     node = astroid.extract_node('''
     import string
     string.letters #@
     ''')
     with self.assertNoMessages():
         self.checker.visit_attribute(node)
示例#25
0
 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)
示例#26
0
 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)
示例#30
0
 def test_object_maxint(self):
     node = astroid.extract_node('''
     sys = object()
     sys.maxint #@
     ''')
     with self.assertNoMessages():
         self.checker.visit_attribute(node)
示例#31
0
    def test_unparse_List(self):
        node = extract_node("[2, 3, 5]")

        self.assertEqual(self.unparser.unparse_List(node), "[2,3,5]")
示例#32
0
    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)]")
示例#33
0
    def test_unparse_TryFinally(self):
        node = extract_node("try:pass\nfinally:pass")

        self.assertEqual(self.unparser.unparse_TryFinally(node),
                         "try:pass\nfinally:pass")
示例#34
0
 def test_len_set(self):
     node = astroid.extract_node("""
     len({'a'})
     """)
     inferred_node = next(node.infer())
     assert inferred_node.as_string() == '1'
示例#35
0
    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)
示例#38
0
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],
示例#39
0
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']
示例#40
0
    def test_unparse_Set(self):
        node = extract_node("{2, 3, 5}")

        self.assertEqual(self.unparser.unparse_Set(node), "{2,3,5}")
示例#41
0
 def test_len_failure_missing_variable(self):
     node = astroid.extract_node("""
     len(a)
     """)
     with pytest.raises(astroid.InferenceError):
         next(node.infer())
示例#42
0
 def test_len_string(self):
     node = astroid.extract_node("""
     len("uwu")
     """)
     assert next(node.infer()).as_string() == "3"
示例#43
0
    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")
示例#44
0
def _get_result_node(code):
    node = next(astroid.extract_node(code).infer())
    return node
示例#45
0
 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)
示例#46
0
 def test_len_tuple(self):
     node = astroid.extract_node("""
     len(('a','b','c'))
     """)
     node = next(node.infer())
     assert node.as_string() == '3'
示例#47
0
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
示例#48
0
 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)
示例#49
0
 def test_unparse_AssignName(self):
     node = extract_node("a = 2")
     self.assertEqual(self.unparser.unparse_Assign(node), "a=2")
示例#50
0
 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)
示例#51
0
 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")
示例#52
0
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))
示例#53
0
    def test_unparse_IfExp(self):
        node = extract_node("5 if {} else 2")

        self.assertEqual(self.unparser.unparse_IfExp(node), "5if{}else 2")
示例#54
0
 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)
示例#56
0
    def test_unparse_YieldFrom(self):
        node = extract_node("yield from True")

        self.assertEqual(self.unparser.unparse_YieldFrom(node),
                         "yield from True")
示例#57
0
    def test_unparse_Return(self):
        node = extract_node("return []")

        self.assertEqual(self.unparser.unparse_Return(node), "return[]")
示例#58
0
    def test_unparse_Nonlocal(self):
        node = extract_node("nonlocal a, b")

        self.assertEqual(self.unparser.unparse_Nonlocal(node), "nonlocal a,b")
示例#59
0
 def test_len_bytes(self):
     node = astroid.extract_node("""
     len(b'uwu')
     """)
     assert next(node.infer()).as_string() == '3'
示例#60
0
    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)}")