|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- # -*- coding: utf-8 -*-
- # Copyright (c) 2014 Google, Inc.
- # Copyright (c) 2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
- # Copyright (c) 2014 Holger Peters <email@holger-peters.de>
- # Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
- # Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
- # Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
- # Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
- # Copyright (c) 2016 Filipe Brandenburger <filbranden@google.com>
- # Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
-
- # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
- # For details: https://github.com/PyCQA/pylint/blob/master/COPYING
-
- """Unittest for the type checker."""
- import sys
-
- import pytest
-
- import astroid
-
- from pylint.checkers import typecheck
- from pylint.testutils import CheckerTestCase, Message, set_config
-
-
- def c_extension_missing():
- """Coverage module has C-extension, which we can reuse for test"""
- try:
- import coverage.tracer as _
- return False
- except ImportError:
- _ = None
- return True
-
-
- needs_c_extension = pytest.mark.skipif(c_extension_missing(),
- reason='Requires coverage (source of C-extension)')
-
-
- class TestTypeChecker(CheckerTestCase):
- "Tests for pylint.checkers.typecheck"
- CHECKER_CLASS = typecheck.TypeChecker
-
- def test_no_member_in_getattr(self):
- """Make sure that a module attribute access is checked by pylint.
- """
-
- node = astroid.extract_node("""
- import optparse
- optparse.THIS_does_not_EXIST
- """)
- with self.assertAddsMessages(
- Message(
- 'no-member',
- node=node,
- args=('Module', 'optparse', 'THIS_does_not_EXIST', ''))):
- self.checker.visit_attribute(node)
-
- @set_config(ignored_modules=('argparse',))
- def test_no_member_in_getattr_ignored(self):
- """Make sure that a module attribute access check is omitted with a
- module that is configured to be ignored.
- """
-
- node = astroid.extract_node("""
- import argparse
- argparse.THIS_does_not_EXIST
- """)
- with self.assertNoMessages():
- self.checker.visit_attribute(node)
-
- @set_config(ignored_classes=('xml.etree.', ))
- def test_ignored_modules_invalid_pattern(self):
- node = astroid.extract_node('''
- import xml
- xml.etree.Lala
- ''')
- message = Message('no-member', node=node,
- args=('Module', 'xml.etree', 'Lala', ''))
- with self.assertAddsMessages(message):
- self.checker.visit_attribute(node)
-
- @set_config(ignored_modules=('xml.etree*', ))
- def test_ignored_modules_patterns(self):
- node = astroid.extract_node('''
- import xml
- xml.etree.portocola #@
- ''')
- with self.assertNoMessages():
- self.checker.visit_attribute(node)
-
- @set_config(ignored_classes=('xml.*', ))
- def test_ignored_classes_no_recursive_pattern(self):
- node = astroid.extract_node('''
- import xml
- xml.etree.ElementTree.Test
- ''')
- message = Message('no-member', node=node,
- args=('Module', 'xml.etree.ElementTree', 'Test', ''))
- with self.assertAddsMessages(message):
- self.checker.visit_attribute(node)
-
- @set_config(ignored_classes=('optparse.Values', ))
- def test_ignored_classes_qualified_name(self):
- """Test that ignored-classes supports qualified name for ignoring."""
- node = astroid.extract_node('''
- import optparse
- optparse.Values.lala
- ''')
- with self.assertNoMessages():
- self.checker.visit_attribute(node)
-
- @set_config(ignored_classes=('Values', ))
- def test_ignored_classes_only_name(self):
- """Test that ignored_classes works with the name only."""
- node = astroid.extract_node('''
- import optparse
- optparse.Values.lala
- ''')
- with self.assertNoMessages():
- self.checker.visit_attribute(node)
-
- @set_config(suggestion_mode=False)
- @needs_c_extension
- def test_nomember_on_c_extension_error_msg(self):
- node = astroid.extract_node('''
- from coverage import tracer
- tracer.CTracer #@
- ''')
- message = Message('no-member', node=node,
- args=('Module', 'coverage.tracer', 'CTracer', ''))
- with self.assertAddsMessages(message):
- self.checker.visit_attribute(node)
-
- @set_config(suggestion_mode=True)
- @needs_c_extension
- def test_nomember_on_c_extension_info_msg(self):
- node = astroid.extract_node('''
- from coverage import tracer
- tracer.CTracer #@
- ''')
- message = Message('c-extension-no-member', node=node,
- args=('Module', 'coverage.tracer', 'CTracer', ''))
- with self.assertAddsMessages(message):
- self.checker.visit_attribute(node)
-
- @set_config(contextmanager_decorators=('contextlib.contextmanager',
- '.custom_contextmanager'))
- def test_custom_context_manager(self):
- """Test that @custom_contextmanager is recognized as configured."""
- node = astroid.extract_node('''
- from contextlib import contextmanager
- def custom_contextmanager(f):
- return contextmanager(f)
- @custom_contextmanager
- def dec():
- yield
- with dec():
- pass
- ''')
- with self.assertNoMessages():
- self.checker.visit_with(node)
-
- def test_invalid_metaclass(self):
- module = astroid.parse('''
- import six
-
- class InvalidAsMetaclass(object):
- pass
-
- @six.add_metaclass(int)
- class FirstInvalid(object):
- pass
-
- @six.add_metaclass(InvalidAsMetaclass)
- class SecondInvalid(object):
- pass
-
- @six.add_metaclass(2)
- class ThirdInvalid(object):
- pass
- ''')
- for class_obj, metaclass_name in (('ThirdInvalid', '2'),
- ('SecondInvalid', 'InvalidAsMetaclass'),
- ('FirstInvalid', 'int')):
- classdef = module[class_obj]
- message = Message('invalid-metaclass', node=classdef, args=(metaclass_name, ))
- with self.assertAddsMessages(message):
- self.checker.visit_classdef(classdef)
-
- @pytest.mark.skipif(sys.version_info[0] < 3, reason='Needs Python 3.')
- def test_invalid_metaclass_function_metaclasses(self):
- module = astroid.parse('''
- def invalid_metaclass_1(name, bases, attrs):
- return int
- def invalid_metaclass_2(name, bases, attrs):
- return 1
- class Invalid(metaclass=invalid_metaclass_1):
- pass
- class InvalidSecond(metaclass=invalid_metaclass_2):
- pass
- ''')
- for class_obj, metaclass_name in (('Invalid', 'int'), ('InvalidSecond', '1')):
- classdef = module[class_obj]
- message = Message('invalid-metaclass', node=classdef, args=(metaclass_name, ))
- with self.assertAddsMessages(message):
- self.checker.visit_classdef(classdef)
-
- @pytest.mark.skipif(sys.version_info < (3, 5), reason='Needs Python 3.5.')
- def test_typing_namedtuple_not_callable_issue1295(self):
- module = astroid.parse("""
- import typing
- Named = typing.NamedTuple('Named', [('foo', int), ('bar', int)])
- named = Named(1, 2)
- """)
- call = module.body[-1].value
- callables = call.func.infered()
- assert len(callables) == 1
- assert callables[0].callable()
- with self.assertNoMessages():
- self.checker.visit_call(call)
-
- @pytest.mark.skipif(sys.version_info < (3, 5), reason='Needs Python 3.5.')
- def test_typing_namedtuple_unsubscriptable_object_issue1295(self):
- module = astroid.parse("""
- import typing
- MyType = typing.Tuple[str, str]
- """)
- subscript = module.body[-1].value
- with self.assertNoMessages():
- self.checker.visit_subscript(subscript)
|