summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorsten Marek <shlomme@gmail.com>2014-07-24 15:07:51 +0200
committerTorsten Marek <shlomme@gmail.com>2014-07-24 15:07:51 +0200
commit7aa57622583caab7dc14d0458bc3c4082036b766 (patch)
tree8235d02ff30784eb6656abedba5daa71fbd8fd22
parenta7d1515daef1234b3bf3e3991299771bea3c56bc (diff)
downloadpylint-7aa57622583caab7dc14d0458bc3c4082036b766.tar.gz
Implement confidence levels.
- attach confidence levels to a number of messages - include confidence levels in the message object - if the confidence of a message is not HIGH or UNDEFINED, include it in the test output.
-rw-r--r--ChangeLog5
-rw-r--r--checkers/__init__.py6
-rw-r--r--checkers/base.py46
-rw-r--r--checkers/exceptions.py8
-rw-r--r--checkers/newstyle.py29
-rw-r--r--checkers/typecheck.py22
-rw-r--r--checkers/utils.py19
-rw-r--r--checkers/variables.py11
-rw-r--r--interfaces.py12
-rw-r--r--lint.py22
-rw-r--r--test/functional/access_to__name__.txt4
-rw-r--r--test/functional/class_members_py27.txt12
-rw-r--r--test/functional/class_members_py30.txt12
-rw-r--r--test/functional/confidence_filter.py14
-rw-r--r--test/functional/confidence_filter.rc3
-rw-r--r--test/functional/confidence_filter.txt1
-rw-r--r--test/functional/docstrings.txt8
-rw-r--r--test/functional/invalid_exceptions_caught.txt6
-rw-r--r--test/functional/invalid_exceptions_raised.txt12
-rw-r--r--test/functional/member_checks.py (renamed from test/input/func_typecheck_getattr.py)26
-rw-r--r--test/functional/member_checks.txt9
-rw-r--r--test/functional/name_styles.txt10
-rw-r--r--test/functional/namedtuple_member_inference.txt4
-rw-r--r--test/functional/newstyle__slots__.txt2
-rw-r--r--test/functional/newstyle_properties.txt2
-rw-r--r--test/functional/super_checks.txt14
-rw-r--r--test/messages/func_typecheck_getattr.txt10
-rw-r--r--test/test_functional.py26
-rw-r--r--test/unittest_lint.py20
-rw-r--r--test/unittest_utils.py2
-rw-r--r--testutils.py2
-rw-r--r--utils.py50
32 files changed, 276 insertions, 153 deletions
diff --git a/ChangeLog b/ChangeLog
index 3572212..2897f2f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,11 @@ ChangeLog for Pylint
====================
--
+ * Each message now comes with a confidence level attached, and
+ can be filtered base on this level. This allows to filter out
+ all messages that were emitted even though an inference failure
+ happened during checking.
+
* Improved presenting unused-import message. Closes issue #293.
* Add new checker for finding spelling errors. New messages:
diff --git a/checkers/__init__.py b/checkers/__init__.py
index 693a5ff..8710f4a 100644
--- a/checkers/__init__.py
+++ b/checkers/__init__.py
@@ -46,6 +46,8 @@ from logilab.common.configuration import OptionsProviderMixIn
from pylint.reporters import diff_string
from pylint.utils import register_plugins
+from pylint.interfaces import UNDEFINED
+
def table_lines_from_stats(stats, old_stats, columns):
"""get values listed in <columns> from <stats> and <old_stats>,
@@ -90,9 +92,9 @@ class BaseChecker(OptionsProviderMixIn):
OptionsProviderMixIn.__init__(self)
self.linter = linter
- def add_message(self, msg_id, line=None, node=None, args=None):
+ def add_message(self, msg_id, line=None, node=None, args=None, confidence=UNDEFINED):
"""add a message of a given type"""
- self.linter.add_message(msg_id, line, node, args)
+ self.linter.add_message(msg_id, line, node, args, confidence)
# dummy methods implementing the IChecker interface
diff --git a/checkers/base.py b/checkers/base.py
index d6553ed..298ac5f 100644
--- a/checkers/base.py
+++ b/checkers/base.py
@@ -24,7 +24,7 @@ from logilab.common.ureports import Table
from astroid import are_exclusive, InferenceError
import astroid.bases
-from pylint.interfaces import IAstroidChecker
+from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH
from pylint.utils import EmptyReport
from pylint.reporters import diff_string
from pylint.checkers import BaseChecker
@@ -36,6 +36,7 @@ from pylint.checkers.utils import (
overrides_a_method,
safe_infer,
get_argument_from_call,
+ has_known_bases,
NoSuchArgumentError,
is_import_error,
)
@@ -995,10 +996,15 @@ class NameChecker(_BasicChecker):
def visit_function(self, node):
# Do not emit any warnings if the method is just an implementation
# of a base class method.
- if node.is_method() and overrides_a_method(node.parent.frame(), node.name):
- return
+ confidence = HIGH
+ if node.is_method():
+ if overrides_a_method(node.parent.frame(), node.name):
+ return
+ confidence = (INFERENCE if has_known_bases(node.parent.frame())
+ else INFERENCE_FAILURE)
+
self._check_name(_determine_function_name_type(node),
- node.name, node)
+ node.name, node, confidence)
# Check argument names
args = node.args.args
if args is not None:
@@ -1047,20 +1053,22 @@ class NameChecker(_BasicChecker):
def _find_name_group(self, node_type):
return self._name_group.get(node_type, node_type)
- def _is_multi_naming_match(self, match):
+ def _is_multi_naming_match(self, match, node_type, confidence):
return (match is not None and
match.lastgroup is not None and
- match.lastgroup not in EXEMPT_NAME_CATEGORIES)
+ match.lastgroup not in EXEMPT_NAME_CATEGORIES
+ and (node_type != 'method' or confidence != INFERENCE_FAILURE))
- def _raise_name_warning(self, node, node_type, name):
+ def _raise_name_warning(self, node, node_type, name, confidence):
type_label = _NAME_TYPES[node_type][1]
hint = ''
if self.config.include_naming_hint:
hint = ' (hint: %s)' % (getattr(self.config, node_type + '_name_hint'))
- self.add_message('invalid-name', node=node, args=(type_label, name, hint))
+ self.add_message('invalid-name', node=node, args=(type_label, name, hint),
+ confidence=confidence)
self.stats['badname_' + node_type] += 1
- def _check_name(self, node_type, name, node):
+ def _check_name(self, node_type, name, node, confidence=HIGH):
"""check for a name using the type's regexp"""
if is_inside_except(node):
clobbering, _ = clobber_in_except(node)
@@ -1075,14 +1083,14 @@ class NameChecker(_BasicChecker):
regexp = getattr(self.config, node_type + '_rgx')
match = regexp.match(name)
- if self._is_multi_naming_match(match):
+ if self._is_multi_naming_match(match, node_type, confidence):
name_group = self._find_name_group(node_type)
bad_name_group = self._bad_names.setdefault(name_group, {})
warnings = bad_name_group.setdefault(match.lastgroup, [])
- warnings.append((node, node_type, name))
+ warnings.append((node, node_type, name, confidence))
if match is None:
- self._raise_name_warning(node, node_type, name)
+ self._raise_name_warning(node, node_type, name, confidence)
class DocStringChecker(_BasicChecker):
@@ -1133,6 +1141,8 @@ class DocStringChecker(_BasicChecker):
ftype = node.is_method() and 'method' or 'function'
if isinstance(node.parent.frame(), astroid.Class):
overridden = False
+ confidence = (INFERENCE if has_known_bases(node.parent.frame())
+ else INFERENCE_FAILURE)
# check if node is from a method overridden by its ancestor
for ancestor in node.parent.frame().ancestors():
if node.name in ancestor and \
@@ -1140,11 +1150,13 @@ class DocStringChecker(_BasicChecker):
overridden = True
break
self._check_docstring(ftype, node,
- report_missing=not overridden)
+ report_missing=not overridden,
+ confidence=confidence)
else:
self._check_docstring(ftype, node)
- def _check_docstring(self, node_type, node, report_missing=True):
+ def _check_docstring(self, node_type, node, report_missing=True,
+ confidence=HIGH):
"""check the node has a non empty docstring"""
docstring = node.doc
if docstring is None:
@@ -1170,10 +1182,12 @@ class DocStringChecker(_BasicChecker):
return
elif func.bound.name in ('str', 'unicode', 'bytes'):
return
- self.add_message('missing-docstring', node=node, args=(node_type,))
+ self.add_message('missing-docstring', node=node, args=(node_type,),
+ confidence=confidence)
elif not docstring.strip():
self.stats['undocumented_'+node_type] += 1
- self.add_message('empty-docstring', node=node, args=(node_type,))
+ self.add_message('empty-docstring', node=node, args=(node_type,),
+ confidence=confidence)
class PassChecker(_BasicChecker):
diff --git a/checkers/exceptions.py b/checkers/exceptions.py
index cd78719..9b1a071 100644
--- a/checkers/exceptions.py
+++ b/checkers/exceptions.py
@@ -25,8 +25,8 @@ from pylint.checkers import BaseChecker
from pylint.checkers.utils import (
is_empty, is_raising,
check_messages, inherit_from_std_ex,
- EXCEPTIONS_MODULE)
-from pylint.interfaces import IAstroidChecker
+ EXCEPTIONS_MODULE, has_known_bases)
+from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE
def _annotated_unpack_infer(stmt, context=None):
"""
@@ -234,7 +234,9 @@ class ExceptionsChecker(BaseChecker):
if expr.newstyle:
self.add_message('raising-non-exception', node=node)
else:
- self.add_message('nonstandard-exception', node=node)
+ self.add_message(
+ 'nonstandard-exception', node=node,
+ confidence=INFERENCE if has_known_bases(expr) else INFERENCE_FAILURE)
else:
value_found = False
else:
diff --git a/checkers/newstyle.py b/checkers/newstyle.py
index cf50229..1946f94 100644
--- a/checkers/newstyle.py
+++ b/checkers/newstyle.py
@@ -19,9 +19,9 @@ import sys
import astroid
-from pylint.interfaces import IAstroidChecker
+from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH
from pylint.checkers import BaseChecker
-from pylint.checkers.utils import check_messages
+from pylint.checkers.utils import check_messages, has_known_bases
MSGS = {
'E1001': ('Use of __slots__ on an old style class',
@@ -78,12 +78,17 @@ class NewStyleConflictChecker(BaseChecker):
style class definition.
"""
if '__slots__' in node and not node.newstyle:
- self.add_message('slots-on-old-class', node=node)
+ confidence = (INFERENCE if has_known_bases(node)
+ else INFERENCE_FAILURE)
+ self.add_message('slots-on-old-class', node=node,
+ confidence=confidence)
# The node type could be class, exception, metaclass, or
# interface. Presumably, the non-class-type nodes would always
# have an explicit base class anyway.
if not node.bases and node.type == 'class' and not node.metaclass():
- self.add_message('old-style-class', node=node)
+ # We use confidence HIGH here because this message should only ever
+ # be emitted for classes at the root of the inheritance hierarchyself.
+ self.add_message('old-style-class', node=node, confidence=HIGH)
@check_messages('property-on-old-class')
def visit_callfunc(self, node):
@@ -92,9 +97,12 @@ class NewStyleConflictChecker(BaseChecker):
if (isinstance(parent, astroid.Class) and
not parent.newstyle and
isinstance(node.func, astroid.Name)):
+ confidence = (INFERENCE if has_known_bases(parent)
+ else INFERENCE_FAILURE)
name = node.func.name
if name == 'property':
- self.add_message('property-on-old-class', node=node)
+ self.add_message('property-on-old-class', node=node,
+ confidence=confidence)
@check_messages('super-on-old-class', 'bad-super-call', 'missing-super-argument')
def visit_function(self, node):
@@ -112,9 +120,12 @@ class NewStyleConflictChecker(BaseChecker):
if isinstance(call, astroid.CallFunc) and \
isinstance(call.func, astroid.Name) and \
call.func.name == 'super':
+ confidence = (INFERENCE if has_known_bases(klass)
+ else INFERENCE_FAILURE)
if not klass.newstyle:
# super should not be used on an old style class
- self.add_message('super-on-old-class', node=node)
+ self.add_message('super-on-old-class', node=node,
+ confidence=confidence)
else:
# super first arg should be the class
if not call.args and sys.version_info[0] == 3:
@@ -128,7 +139,8 @@ class NewStyleConflictChecker(BaseChecker):
continue
if supcls is None:
- self.add_message('missing-super-argument', node=call)
+ self.add_message('missing-super-argument', node=call,
+ confidence=confidence)
continue
if klass is not supcls:
@@ -144,7 +156,8 @@ class NewStyleConflictChecker(BaseChecker):
if name is not None:
self.add_message('bad-super-call',
node=call,
- args=(name, ))
+ args=(name, ),
+ confidence=confidence)
def register(linter):
diff --git a/checkers/typecheck.py b/checkers/typecheck.py
index a8851a2..27ed9f5 100644
--- a/checkers/typecheck.py
+++ b/checkers/typecheck.py
@@ -23,23 +23,19 @@ import astroid
from astroid import InferenceError, NotFoundError, YES, Instance
from astroid.bases import BUILTINS
-from pylint.interfaces import IAstroidChecker
+from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE
from pylint.checkers import BaseChecker
from pylint.checkers.utils import safe_infer, is_super, check_messages
MSGS = {
'E1101': ('%s %r has no %r member',
'no-member',
- 'Used when a variable is accessed for an unexistent member.'),
+ 'Used when a variable is accessed for an unexistent member.',
+ {'old_names': [('E1103', 'maybe-no-member')]}),
'E1102': ('%s is not callable',
'not-callable',
'Used when an object being called has been inferred to a non \
callable object'),
- 'E1103': ('%s %r has no %r member (but some types could not be inferred)',
- 'maybe-no-member',
- 'Used when a variable is accessed for an unexistent member, but \
- astroid was not able to interpret all possible types of this \
- variable.'),
'E1111': ('Assigning to function call which doesn\'t return',
'assignment-from-no-return',
'Used when an assignment is done on a function call but the \
@@ -187,7 +183,7 @@ accessed. Python regular expressions are accepted.'}
def visit_delattr(self, node):
self.visit_getattr(node)
- @check_messages('no-member', 'maybe-no-member')
+ @check_messages('no-member')
def visit_getattr(self, node):
"""check that the accessed attribute exists
@@ -279,13 +275,11 @@ accessed. Python regular expressions are accepted.'}
if actual in done:
continue
done.add(actual)
- if inference_failure:
- msgid = 'maybe-no-member'
- else:
- msgid = 'no-member'
- self.add_message(msgid, node=node,
+ confidence = INFERENCE if not inference_failure else INFERENCE_FAILURE
+ self.add_message('no-member', node=node,
args=(owner.display_type(), name,
- node.attrname))
+ node.attrname),
+ confidence=confidence)
@check_messages('assignment-from-no-return', 'assignment-from-none')
def visit_assign(self, node):
diff --git a/checkers/utils.py b/checkers/utils.py
index 000aee7..b3ba51b 100644
--- a/checkers/utils.py
+++ b/checkers/utils.py
@@ -462,3 +462,22 @@ def is_import_error(handler):
return True
except astroid.InferenceError:
continue
+
+def has_known_bases(klass):
+ """Returns true if all base classes of a class could be inferred."""
+ try:
+ return klass._all_bases_known
+ except AttributeError:
+ pass
+ try:
+ for base in klass.bases:
+ result = base.infer().next()
+ # TODO: check for A->B->A->B pattern in class structure too?
+ if not isinstance(result, astroid.Class) or result is klass or not has_known_bases(result):
+ klass._all_bases_known = False
+ return False
+ except astroid.InferenceError:
+ klass._all_bases_known = False
+ return False
+ klass._all_bases_known = True
+ return True
diff --git a/checkers/variables.py b/checkers/variables.py
index a88bf3b..9c0b15c 100644
--- a/checkers/variables.py
+++ b/checkers/variables.py
@@ -25,14 +25,14 @@ from astroid import are_exclusive, builtin_lookup, AstroidBuildingException
from logilab.common.modutils import file_from_modpath
-from pylint.interfaces import IAstroidChecker
+from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH
from pylint.utils import get_global_option
from pylint.checkers import BaseChecker
from pylint.checkers.utils import (
PYMETHODS, is_ancestor_name, is_builtin,
is_defined_before, is_error, is_func_default, is_func_decorator,
assign_parent, check_messages, is_inside_except, clobber_in_except,
- get_all_elements)
+ get_all_elements, has_known_bases)
SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
@@ -486,6 +486,10 @@ builtins. Remember that you should avoid to define new builtins when possible.'
klass = node.parent.frame()
if is_method and (klass.type == 'interface' or node.is_abstract()):
return
+ if is_method and isinstance(klass, astroid.Class):
+ confidence = INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE
+ else:
+ confidence = HIGH
authorized_rgx = self.config.dummy_variables_rgx
called_overridden = False
argnames = node.argnames()
@@ -539,7 +543,8 @@ builtins. Remember that you should avoid to define new builtins when possible.'
# don't check callback arguments XXX should be configurable
if node.name.startswith('cb_') or node.name.endswith('_cb'):
continue
- self.add_message('unused-argument', args=name, node=stmt)
+ self.add_message('unused-argument', args=name, node=stmt,
+ confidence=confidence)
else:
if stmt.parent and isinstance(stmt.parent, astroid.Assign):
if name in nonlocal_names:
diff --git a/interfaces.py b/interfaces.py
index 50f2c83..ea3b40f 100644
--- a/interfaces.py
+++ b/interfaces.py
@@ -11,9 +11,21 @@
# this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""Interfaces for PyLint objects"""
+from collections import namedtuple
from logilab.common.interface import Interface
+Confidence = namedtuple('Confidence', ['name', 'description'])
+# Warning Certainties
+HIGH = Confidence('HIGH', 'No false positive possible.')
+INFERENCE = Confidence('INFERENCE', 'Warning based on inference result.')
+INFERENCE_FAILURE = Confidence('INFERENCE_FAILURE',
+ 'Warning based on inference with failures.')
+UNDEFINED = Confidence('UNDEFINED',
+ 'Warning without any associated confidence level.')
+
+CONFIDENCE_LEVELS = [HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED]
+
class IChecker(Interface):
"""This is an base interface, not designed to be used elsewhere than for
diff --git a/lint.py b/lint.py
index 5be37f3..0f5aa6f 100644
--- a/lint.py
+++ b/lint.py
@@ -51,7 +51,7 @@ from pylint.utils import (
PyLintASTWalker, UnknownMessage, MessagesHandlerMixIn, ReportsHandlerMixIn,
MessagesStore, FileState, EmptyReport,
expand_modules, tokenize_module)
-from pylint.interfaces import IRawChecker, ITokenChecker, IAstroidChecker
+from pylint.interfaces import IRawChecker, ITokenChecker, IAstroidChecker, CONFIDENCE_LEVELS
from pylint.checkers import (BaseTokenChecker,
table_lines_from_stats,
initialize as checkers_initialize)
@@ -238,6 +238,15 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
'help' : 'Add a comment according to your evaluation note. '
'This is used by the global evaluation report (RP0004).'}),
+ ('confidence',
+ {'type' : 'multiple_choice', 'metavar': '<levels>',
+ 'default': '',
+ 'choices': [c.name for c in CONFIDENCE_LEVELS],
+ 'group': 'Messages control',
+ 'help' : 'Only show warnings with the listed confidence levels.'
+ ' Leave empty to show all. Valid levels: %s' % (
+ ', '.join(c.name for c in CONFIDENCE_LEVELS),)}),
+
('enable',
{'type' : 'csv', 'metavar': '<msg ids>',
'short': 'e',
@@ -879,6 +888,12 @@ group are mutually exclusive.'),
'group': 'Commands', 'level': 1,
'help' : "Generate pylint's messages."}),
+ ('list-conf-levels',
+ {'action' : 'callback',
+ 'callback' : self.cb_list_confidence_levels,
+ 'group': 'Commands', 'level': 1,
+ 'help' : "Generate pylint's messages."}),
+
('full-documentation',
{'action' : 'callback', 'metavar': '<msg-id>',
'callback' : self.cb_full_documentation,
@@ -1037,6 +1052,11 @@ group are mutually exclusive.'),
self.linter.msgs_store.list_messages()
sys.exit(0)
+ def cb_list_confidence_levels(self, option, optname, value, parser):
+ for level in CONFIDENCE_LEVELS:
+ print '%-18s: %s' % level
+ sys.exit(0)
+
def cb_init_hook(optname, value):
"""exec arbitrary code to set sys.path for instance"""
exec value
diff --git a/test/functional/access_to__name__.txt b/test/functional/access_to__name__.txt
index 2c0a953..ecf5ffd 100644
--- a/test/functional/access_to__name__.txt
+++ b/test/functional/access_to__name__.txt
@@ -1,3 +1,3 @@
old-style-class:7:Aaaa:Old-style class defined.
-no-member:10:Aaaa.__init__:Instance of 'Aaaa' has no '__name__' member
-no-member:21:NewClass.__init__:Instance of 'NewClass' has no '__name__' member
+no-member:10:Aaaa.__init__:Instance of 'Aaaa' has no '__name__' member:INFERENCE
+no-member:21:NewClass.__init__:Instance of 'NewClass' has no '__name__' member:INFERENCE
diff --git a/test/functional/class_members_py27.txt b/test/functional/class_members_py27.txt
index c7b017e..e5e6005 100644
--- a/test/functional/class_members_py27.txt
+++ b/test/functional/class_members_py27.txt
@@ -1,6 +1,6 @@
-no-member:14:MyClass.test:Instance of 'MyClass' has no 'incorrect' member
-no-member:15:MyClass.test:Instance of 'MyClass' has no 'havenot' member
-no-member:16:MyClass.test:Instance of 'MyClass' has no 'nonexistent1' member
-no-member:17:MyClass.test:Instance of 'MyClass' has no 'nonexistent2' member
-no-member:50::Instance of 'TestMetaclass' has no 'register' member
-no-member:51::Instance of 'UsingMetaclass' has no 'test' member
+no-member:14:MyClass.test:Instance of 'MyClass' has no 'incorrect' member:INFERENCE
+no-member:15:MyClass.test:Instance of 'MyClass' has no 'havenot' member:INFERENCE
+no-member:16:MyClass.test:Instance of 'MyClass' has no 'nonexistent1' member:INFERENCE
+no-member:17:MyClass.test:Instance of 'MyClass' has no 'nonexistent2' member:INFERENCE
+no-member:50::Instance of 'TestMetaclass' has no 'register' member:INFERENCE
+no-member:51::Instance of 'UsingMetaclass' has no 'test' member:INFERENCE
diff --git a/test/functional/class_members_py30.txt b/test/functional/class_members_py30.txt
index 0cb808f..4696579 100644
--- a/test/functional/class_members_py30.txt
+++ b/test/functional/class_members_py30.txt
@@ -1,6 +1,6 @@
-no-member:14:MyClass.test:Instance of 'MyClass' has no 'incorrect' member
-no-member:15:MyClass.test:Instance of 'MyClass' has no 'havenot' member
-no-member:16:MyClass.test:Instance of 'MyClass' has no 'nonexistent1' member
-no-member:17:MyClass.test:Instance of 'MyClass' has no 'nonexistent2' member
-no-member:48::Instance of 'TestMetaclass' has no 'register' member
-no-member:49::Instance of 'UsingMetaclass' has no 'test' member
+no-member:14:MyClass.test:Instance of 'MyClass' has no 'incorrect' member:INFERENCE
+no-member:15:MyClass.test:Instance of 'MyClass' has no 'havenot' member:INFERENCE
+no-member:16:MyClass.test:Instance of 'MyClass' has no 'nonexistent1' member:INFERENCE
+no-member:17:MyClass.test:Instance of 'MyClass' has no 'nonexistent2' member:INFERENCE
+no-member:48::Instance of 'TestMetaclass' has no 'register' member:INFERENCE
+no-member:49::Instance of 'UsingMetaclass' has no 'test' member:INFERENCE
diff --git a/test/functional/confidence_filter.py b/test/functional/confidence_filter.py
new file mode 100644
index 0000000..64cc8c4
--- /dev/null
+++ b/test/functional/confidence_filter.py
@@ -0,0 +1,14 @@
+"""Test for the confidence filter."""
+
+class Client(object):
+ """use provider class"""
+
+ def __init__(self):
+ self.set_later = 0
+
+ def set_set_later(self, value):
+ """set set_later attribute (introduce an inference ambiguity)"""
+ self.set_later = value
+
+print Client().set_later.lower()
+print Client().foo # [no-member]
diff --git a/test/functional/confidence_filter.rc b/test/functional/confidence_filter.rc
new file mode 100644
index 0000000..5d21cb5
--- /dev/null
+++ b/test/functional/confidence_filter.rc
@@ -0,0 +1,3 @@
+[Messages Control]
+disable=no-init,too-few-public-methods,undefined-variable
+confidence=INFERENCE,HIGH,UNDEFINED
diff --git a/test/functional/confidence_filter.txt b/test/functional/confidence_filter.txt
new file mode 100644
index 0000000..308035d
--- /dev/null
+++ b/test/functional/confidence_filter.txt
@@ -0,0 +1 @@
+no-member:14::Instance of 'Client' has no 'foo' member:INFERENCE
diff --git a/test/functional/docstrings.txt b/test/functional/docstrings.txt
index 177d02a..e30e462 100644
--- a/test/functional/docstrings.txt
+++ b/test/functional/docstrings.txt
@@ -2,7 +2,7 @@ missing-docstring:1::Missing module docstring
empty-docstring:6:function0:Empty function docstring
missing-docstring:10:function1:Missing function docstring
missing-docstring:23:AAAA:Missing class docstring
-missing-docstring:40:AAAA.method1:Missing method docstring
-empty-docstring:48:AAAA.method3:Empty method docstring
-empty-docstring:62:DDDD.method2:Empty method docstring
-missing-docstring:70:DDDD.method4:Missing method docstring
+missing-docstring:40:AAAA.method1:Missing method docstring:INFERENCE
+empty-docstring:48:AAAA.method3:Empty method docstring:INFERENCE
+empty-docstring:62:DDDD.method2:Empty method docstring:INFERENCE
+missing-docstring:70:DDDD.method4:Missing method docstring:INFERENCE
diff --git a/test/functional/invalid_exceptions_caught.txt b/test/functional/invalid_exceptions_caught.txt
index 3156a0b..ce9ab2a 100644
--- a/test/functional/invalid_exceptions_caught.txt
+++ b/test/functional/invalid_exceptions_caught.txt
@@ -1,6 +1,6 @@
catching-non-exception:25::"Catching an exception which doesn't inherit from BaseException: MyException"
catching-non-exception:31::"Catching an exception which doesn't inherit from BaseException: MyException"
catching-non-exception:31::"Catching an exception which doesn't inherit from BaseException: MySecondException"
-catching-non-exception:52::Catching an exception which doesn't inherit from BaseException: None
-catching-non-exception:52::Catching an exception which doesn't inherit from BaseException: list()
-catching-non-exception:57::Catching an exception which doesn't inherit from BaseException: None
+catching-non-exception:52::"Catching an exception which doesn't inherit from BaseException: None"
+catching-non-exception:52::"Catching an exception which doesn't inherit from BaseException: list()"
+catching-non-exception:57::"Catching an exception which doesn't inherit from BaseException: None"
diff --git a/test/functional/invalid_exceptions_raised.txt b/test/functional/invalid_exceptions_raised.txt
index c40eca7..54ea0f4 100644
--- a/test/functional/invalid_exceptions_raised.txt
+++ b/test/functional/invalid_exceptions_raised.txt
@@ -1,13 +1,9 @@
-nonstandard-exception:23:bad_case0:"Exception doesn't inherit from standard ""Exception"" class"
-raising-non-exception:23:bad_case0:"""Raising a new style class which doesn't inherit from BaseException
-""
-"
+nonstandard-exception:23:bad_case0:"Exception doesn't inherit from standard ""Exception"" class":INFERENCE
+raising-non-exception:23:bad_case0:Raising a new style class which doesn't inherit from BaseException
raising-non-exception:27:bad_case1:Raising a new style class which doesn't inherit from BaseException
-nonstandard-exception:33:bad_case2:"Exception doesn't inherit from standard ""Exception"" class"
+nonstandard-exception:33:bad_case2:"Exception doesn't inherit from standard ""Exception"" class":INFERENCE
old-raise-syntax:33:bad_case2:Use raise ErrorClass(args) instead of raise ErrorClass, args.
-raising-non-exception:33:bad_case2:"""Raising a new style class which doesn't inherit from BaseException
-""
-"
+raising-non-exception:33:bad_case2:Raising a new style class which doesn't inherit from BaseException
raising-non-exception:37:bad_case3:Raising a new style class which doesn't inherit from BaseException
notimplemented-raised:42:bad_case4:NotImplemented raised - should raise NotImplementedError
old-raise-syntax:42:bad_case4:Use raise ErrorClass(args) instead of raise ErrorClass, args.
diff --git a/test/input/func_typecheck_getattr.py b/test/functional/member_checks.py
index b120ca4..fccf160 100644
--- a/test/input/func_typecheck_getattr.py
+++ b/test/functional/member_checks.py
@@ -1,7 +1,6 @@
# pylint: disable=
"""check getattr if inference succeed"""
-__revision__ = None
class Provider(object):
"""provide some attributes and method"""
@@ -16,13 +15,13 @@ class Provider(object):
print 'hop hop hop', self
-class Client:
+class Client(object):
"""use provider class"""
def __init__(self):
self._prov = Provider()
self._prov_attr = Provider.cattr
- self._prov_attr2 = Provider.cattribute
+ self._prov_attr2 = Provider.cattribute # [no-member]
self.set_later = 0
def set_set_later(self, value):
@@ -32,12 +31,12 @@ class Client:
def use_method(self):
"""use provider's method"""
self._prov.hophop()
- self._prov.hophophop()
+ self._prov.hophophop() # [no-member]
def use_attr(self):
"""use provider's attr"""
print self._prov.attr
- print self._prov.attribute
+ print self._prov.attribute # [no-member]
def debug(self):
"""print debug information"""
@@ -49,21 +48,16 @@ class Client:
def test_bt_types(self):
"""test access to unexistant member of builtin types"""
lis = []
- lis.apppend(self)
+ lis.apppend(self) # [no-member]
dic = {}
- dic.set(self)
+ dic.set(self) # [no-member]
tup = ()
- tup.append(self)
+ tup.append(self) # [no-member]
string = 'toto'
- print string.loower()
- # unicode : moved to func_3k_removed_stuff_py_30.py
- #
+ print string.loower() # [no-member]
integer = 1
- print integer.whatever
+ print integer.whatever # [no-member]
print object.__init__
print property.__init__
-print Client().set_later.lower()
-
-# should detect mixing new style / old style classes
-Client.__bases__ += (object,)
+print Client().set_later.lower() # [no-member]
diff --git a/test/functional/member_checks.txt b/test/functional/member_checks.txt
new file mode 100644
index 0000000..12fe6ee
--- /dev/null
+++ b/test/functional/member_checks.txt
@@ -0,0 +1,9 @@
+no-member:24:Client.__init__:Class 'Provider' has no 'cattribute' member:INFERENCE
+no-member:34:Client.use_method:Instance of 'Provider' has no 'hophophop' member:INFERENCE
+no-member:39:Client.use_attr:Instance of 'Provider' has no 'attribute' member:INFERENCE
+no-member:51:Client.test_bt_types:Instance of 'list' has no 'apppend' member:INFERENCE
+no-member:53:Client.test_bt_types:Instance of 'dict' has no 'set' member:INFERENCE
+no-member:55:Client.test_bt_types:Instance of 'tuple' has no 'append' member:INFERENCE
+no-member:57:Client.test_bt_types:Instance of 'str' has no 'loower' member:INFERENCE
+no-member:59:Client.test_bt_types:Instance of 'int' has no 'whatever' member:INFERENCE
+no-member:63::Instance of 'int' has no 'lower' member:INFERENCE_FAILURE
diff --git a/test/functional/name_styles.txt b/test/functional/name_styles.txt
index 985e9a2..e81d27a 100644
--- a/test/functional/name_styles.txt
+++ b/test/functional/name_styles.txt
@@ -6,12 +6,12 @@ invalid-name:25:bad_class_name:"Invalid class name ""bad_class_name"""
invalid-name:36:CorrectClassName.__init__:"Invalid attribute name ""_Bad_AtTR_name"""
invalid-name:37:CorrectClassName.__init__:"Invalid attribute name ""Bad_PUBLIC_name"""
invalid-name:39:CorrectClassName:"Invalid class attribute name ""zz"""
-invalid-name:42:CorrectClassName.BadMethodName:"Invalid method name ""BadMethodName"""
-invalid-name:48:CorrectClassName.__DunDER_IS_not_free_for_all__:"Invalid method name ""__DunDER_IS_not_free_for_all__"""
+invalid-name:42:CorrectClassName.BadMethodName:"Invalid method name ""BadMethodName""":INFERENCE
+invalid-name:48:CorrectClassName.__DunDER_IS_not_free_for_all__:"Invalid method name ""__DunDER_IS_not_free_for_all__""":INFERENCE
invalid-name:78::"Invalid class name ""BAD_NAME_FOR_CLASS"""
invalid-name:79::"Invalid class name ""NEXT_BAD_NAME_FOR_CLASS"""
invalid-name:86::"Invalid class name ""NOT_CORRECT"""
invalid-name:92:test_globals:"Invalid constant name ""AlsoCorrect"""
-invalid-name:105:FooClass.PROPERTY_NAME:"Invalid attribute name ""PROPERTY_NAME"""
-invalid-name:110:FooClass.ABSTRACT_PROPERTY_NAME:"Invalid attribute name ""ABSTRACT_PROPERTY_NAME"""
-invalid-name:115:FooClass.PROPERTY_NAME_SETTER:"Invalid attribute name ""PROPERTY_NAME_SETTER"""
+invalid-name:105:FooClass.PROPERTY_NAME:"Invalid attribute name ""PROPERTY_NAME""":INFERENCE
+invalid-name:110:FooClass.ABSTRACT_PROPERTY_NAME:"Invalid attribute name ""ABSTRACT_PROPERTY_NAME""":INFERENCE
+invalid-name:115:FooClass.PROPERTY_NAME_SETTER:"Invalid attribute name ""PROPERTY_NAME_SETTER""":INFERENCE
diff --git a/test/functional/namedtuple_member_inference.txt b/test/functional/namedtuple_member_inference.txt
index 87d9da4..308336f 100644
--- a/test/functional/namedtuple_member_inference.txt
+++ b/test/functional/namedtuple_member_inference.txt
@@ -1,3 +1,3 @@
-no-member:15:test:Class 'Thing' has no 'x' member
+no-member:15:test:Class 'Thing' has no 'x' member:INFERENCE
protected-access:19:test:Access to a protected member _replace of a client class
-no-member:21:test:Instance of 'Fantastic' has no 'foo' member
+no-member:21:test:Instance of 'Fantastic' has no 'foo' member:INFERENCE
diff --git a/test/functional/newstyle__slots__.txt b/test/functional/newstyle__slots__.txt
index afc9117..4320390 100644
--- a/test/functional/newstyle__slots__.txt
+++ b/test/functional/newstyle__slots__.txt
@@ -1,2 +1,2 @@
old-style-class:10:OldStyleClass:Old-style class defined.
-slots-on-old-class:10:OldStyleClass:Use of __slots__ on an old style class
+slots-on-old-class:10:OldStyleClass:Use of __slots__ on an old style class:INFERENCE
diff --git a/test/functional/newstyle_properties.txt b/test/functional/newstyle_properties.txt
index 9f060c0..a16686b 100644
--- a/test/functional/newstyle_properties.txt
+++ b/test/functional/newstyle_properties.txt
@@ -1,2 +1,2 @@
old-style-class:13:OldStyleClass:Old-style class defined.
-property-on-old-class:15:OldStyleClass:"Use of ""property"" on an old style class"
+property-on-old-class:15:OldStyleClass:"Use of ""property"" on an old style class":INFERENCE
diff --git a/test/functional/super_checks.txt b/test/functional/super_checks.txt
index 2d69098..79f6ae1 100644
--- a/test/functional/super_checks.txt
+++ b/test/functional/super_checks.txt
@@ -1,8 +1,8 @@
old-style-class:6:Aaaa:Old-style class defined.
-super-on-old-class:8:Aaaa.hop:Use of super on an old style class
-super-on-old-class:12:Aaaa.__init__:Use of super on an old style class
-bad-super-call:22:NewAaaa.__init__:Bad first argument 'object' given to super()
-missing-super-argument:27:Py3kAaaa.__init__:Missing argument to super()
-bad-super-call:32:Py3kWrongSuper.__init__:Bad first argument 'NewAaaa' given to super()
-bad-super-call:37:WrongNameRegression.__init__:Bad first argument 'Missing' given to super()
-bad-super-call:46:CrashSuper.__init__:Bad first argument 'NewAaaa' given to super()
+super-on-old-class:8:Aaaa.hop:Use of super on an old style class:INFERENCE
+super-on-old-class:12:Aaaa.__init__:Use of super on an old style class:INFERENCE
+bad-super-call:22:NewAaaa.__init__:Bad first argument 'object' given to super():INFERENCE
+missing-super-argument:27:Py3kAaaa.__init__:Missing argument to super():INFERENCE
+bad-super-call:32:Py3kWrongSuper.__init__:Bad first argument 'NewAaaa' given to super():INFERENCE
+bad-super-call:37:WrongNameRegression.__init__:Bad first argument 'Missing' given to super():INFERENCE
+bad-super-call:46:CrashSuper.__init__:Bad first argument 'NewAaaa' given to super():INFERENCE
diff --git a/test/messages/func_typecheck_getattr.txt b/test/messages/func_typecheck_getattr.txt
deleted file mode 100644
index bd91228..0000000
--- a/test/messages/func_typecheck_getattr.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-C: 19:Client: Old-style class defined.
-E: 25:Client.__init__: Class 'Provider' has no 'cattribute' member
-E: 35:Client.use_method: Instance of 'Provider' has no 'hophophop' member
-E: 40:Client.use_attr: Instance of 'Provider' has no 'attribute' member
-E: 52:Client.test_bt_types: Instance of 'list' has no 'apppend' member
-E: 54:Client.test_bt_types: Instance of 'dict' has no 'set' member
-E: 56:Client.test_bt_types: Instance of 'tuple' has no 'append' member
-E: 58:Client.test_bt_types: Instance of 'str' has no 'loower' member
-E: 62:Client.test_bt_types: Instance of 'int' has no 'whatever' member
-E: 66: Instance of 'int' has no 'lower' member (but some types could not be inferred)
diff --git a/test/test_functional.py b/test/test_functional.py
index 759a447..c2467b3 100644
--- a/test/test_functional.py
+++ b/test/test_functional.py
@@ -11,6 +11,7 @@ import sys
import unittest
from pylint import checkers
+from pylint import interfaces
from pylint import lint
from pylint import reporters
from pylint import utils
@@ -30,6 +31,10 @@ csv.register_dialect('test', test_dialect)
class NoFileError(Exception):
pass
+# Notes:
+# - for the purpose of this test, the confidence levels HIGH and UNDEFINED
+# are treated as the same.
+
# TODOs
# - implement exhaustivity tests
@@ -37,14 +42,24 @@ class NoFileError(Exception):
UPDATE = False
class OutputLine(collections.namedtuple('OutputLine',
- ['symbol', 'lineno', 'object', 'msg'])):
+ ['symbol', 'lineno', 'object', 'msg', 'confidence'])):
@classmethod
def from_msg(cls, msg):
- return cls(msg.symbol, msg.line, msg.obj or '', msg.msg)
+ return cls(
+ msg.symbol, msg.line, msg.obj or '', msg.msg,
+ msg.confidence.name
+ if msg.confidence != interfaces.UNDEFINED else interfaces.HIGH.name)
@classmethod
def from_csv(cls, row):
- return cls(row[0], int(row[1]), row[2], row[3])
+ confidence = row[4] if len(row) == 5 else interfaces.HIGH.name
+ return cls(row[0], int(row[1]), row[2], row[3], confidence)
+
+ def to_csv(self):
+ if self.confidence == interfaces.HIGH.name:
+ return self[:-1]
+ else:
+ return self
# Common sub-expressions.
@@ -292,7 +307,7 @@ class LintModuleOutputUpdate(LintModuleTest):
try:
return super(LintModuleOutputUpdate, self)._open_expected_file()
except IOError:
- return contextlib.closing(cStringIO.StringIO())
+ return io.StringIO()
def _check_output_text(self, expected_messages, expected_lines,
received_lines):
@@ -305,8 +320,7 @@ class LintModuleOutputUpdate(LintModuleTest):
with open(self._test_file.expected_output, 'w') as fobj:
writer = csv.writer(fobj, dialect='test')
for line in remaining:
- #fobj.write('{0}:{1}:{2}:{3}'.format(*line))
- writer.writerow(line)
+ writer.writerow(line.to_csv())
def suite():
input_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
diff --git a/test/unittest_lint.py b/test/unittest_lint.py
index b2022cc..7dc7090 100644
--- a/test/unittest_lint.py
+++ b/test/unittest_lint.py
@@ -26,12 +26,13 @@ from logilab.common.compat import reload
from pylint import config
from pylint.lint import PyLinter, Run, UnknownMessage, preprocess_options, \
ArgumentPreprocessingError
-from pylint.utils import MSG_STATE_SCOPE_CONFIG, MSG_STATE_SCOPE_MODULE, \
+from pylint.utils import MSG_STATE_SCOPE_CONFIG, MSG_STATE_SCOPE_MODULE, MSG_STATE_CONFIDENCE, \
MessagesStore, PyLintASTWalker, MessageDefinition, FileState, \
build_message_def, tokenize_module
from pylint.testutils import TestReporter
from pylint.reporters import text
from pylint import checkers
+from pylint import interfaces
if sys.platform == 'win32':
HOME = 'USERPROFILE'
@@ -139,19 +140,26 @@ class PyLinterTC(unittest.TestCase):
self.assertTrue(linter.is_message_enabled('C0121', line=1))
def test_message_state_scope(self):
+ class FakeConfig(object):
+ confidence = ['HIGH']
+
linter = self.init_linter()
- fs = linter.file_state
linter.disable('C0121')
self.assertEqual(MSG_STATE_SCOPE_CONFIG,
- fs._message_state_scope('C0121'))
+ linter.get_message_state_scope('C0121'))
linter.disable('W0101', scope='module', line=3)
self.assertEqual(MSG_STATE_SCOPE_CONFIG,
- fs._message_state_scope('C0121'))
+ linter.get_message_state_scope('C0121'))
self.assertEqual(MSG_STATE_SCOPE_MODULE,
- fs._message_state_scope('W0101', 3))
+ linter.get_message_state_scope('W0101', 3))
linter.enable('W0102', scope='module', line=3)
self.assertEqual(MSG_STATE_SCOPE_MODULE,
- fs._message_state_scope('W0102', 3))
+ linter.get_message_state_scope('W0102', 3))
+ linter.config = FakeConfig()
+ self.assertEqual(
+ MSG_STATE_CONFIDENCE,
+ linter.get_message_state_scope('this-is-bad',
+ confidence=interfaces.INFERENCE))
def test_enable_message_block(self):
linter = self.init_linter()
diff --git a/test/unittest_utils.py b/test/unittest_utils.py
index ef0cda2..d631dbb 100644
--- a/test/unittest_utils.py
+++ b/test/unittest_utils.py
@@ -15,6 +15,7 @@ import unittest
from astroid import test_utils
from pylint import utils
+from pylint import interfaces
from pylint.checkers.utils import check_messages
@@ -59,4 +60,3 @@ class PyLintASTWalkerTest(unittest.TestCase):
if __name__ == '__main__':
unittest.main()
-
diff --git a/testutils.py b/testutils.py
index e53ec70..86539ac 100644
--- a/testutils.py
+++ b/testutils.py
@@ -141,7 +141,7 @@ class UnittestLinter(object):
finally:
self._messages = []
- def add_message(self, msg_id, line=None, node=None, args=None):
+ def add_message(self, msg_id, line=None, node=None, args=None, confidence=None):
self._messages.append(Message(msg_id, line, node, args))
def is_message_enabled(self, *unused_args):
diff --git a/utils.py b/utils.py
index 0d2cd3c..4e65a56 100644
--- a/utils.py
+++ b/utils.py
@@ -35,7 +35,7 @@ from astroid import nodes, Module
from astroid.modutils import modpath_from_file, get_module_files, \
file_from_modpath, load_module_from_file
-from pylint.interfaces import IRawChecker, ITokenChecker
+from pylint.interfaces import IRawChecker, ITokenChecker, UNDEFINED
class UnknownMessage(Exception):
@@ -67,6 +67,7 @@ MSG_TYPES_STATUS = {
_MSG_ORDER = 'EWRCIF'
MSG_STATE_SCOPE_CONFIG = 0
MSG_STATE_SCOPE_MODULE = 1
+MSG_STATE_CONFIDENCE = 2
OPTION_RGX = re.compile(r'\s*#.*\bpylint:(.*)')
@@ -79,15 +80,16 @@ class WarningScope(object):
_MsgBase = collections.namedtuple(
'_MsgBase',
- ['msg_id', 'symbol', 'msg', 'C', 'category', 'abspath', 'module', 'obj',
- 'line', 'column'])
+ ['msg_id', 'symbol', 'msg', 'C', 'category', 'confidence',
+ 'abspath', 'module', 'obj', 'line', 'column'])
class Message(_MsgBase):
"""This class represent a message to be issued by the reporters"""
- def __new__(cls, msg_id, symbol, location, msg):
+ def __new__(cls, msg_id, symbol, location, msg, confidence):
return _MsgBase.__new__(
- cls, msg_id, symbol, msg, msg_id[0], MSG_TYPES[msg_id[0]], *location)
+ cls, msg_id, symbol, msg, msg_id[0], MSG_TYPES[msg_id[0]],
+ confidence, *location)
def format(self, template):
"""Format the message according to the given template.
@@ -302,12 +304,25 @@ class MessagesHandlerMixIn(object):
# sync configuration object
self.config.enable = [mid for mid, val in msgs.iteritems() if val]
- def is_message_enabled(self, msg_descr, line=None):
+ def get_message_state_scope(self, msgid, line=None, confidence=UNDEFINED):
+ """Returns the scope at which a message was enabled/disabled."""
+ if self.config.confidence and confidence.name not in self.config.confidence:
+ return MSG_STATE_CONFIDENCE
+ try:
+ if line in self.file_state._module_msgs_state[msgid]:
+ return MSG_STATE_SCOPE_MODULE
+ except (KeyError, TypeError):
+ return MSG_STATE_SCOPE_CONFIG
+
+ def is_message_enabled(self, msg_descr, line=None, confidence=None):
"""return true if the message associated to the given message id is
enabled
msgid may be either a numeric or symbolic message id.
"""
+ if self.config.confidence and confidence:
+ if confidence.name not in self.config.confidence:
+ return False
try:
msgid = self.msgs_store.check_message_id(msg_descr).msgid
except UnknownMessage:
@@ -322,7 +337,7 @@ class MessagesHandlerMixIn(object):
except KeyError:
return self._msgs_state.get(msgid, True)
- def add_message(self, msg_descr, line=None, node=None, args=None):
+ def add_message(self, msg_descr, line=None, node=None, args=None, confidence=UNDEFINED):
"""Adds a message given by ID or name.
If provided, the message string is expanded using args
@@ -352,8 +367,10 @@ class MessagesHandlerMixIn(object):
else:
col_offset = None
# should this message be displayed
- if not self.is_message_enabled(msgid, line):
- self.file_state.handle_ignored_message(msgid, line, node, args)
+ if not self.is_message_enabled(msgid, line, confidence):
+ self.file_state.handle_ignored_message(
+ self.get_message_state_scope(msgid, line, confidence),
+ msgid, line, node, args, confidence)
return
# update stats
msg_cat = MSG_TYPES[msgid[0]]
@@ -377,8 +394,8 @@ class MessagesHandlerMixIn(object):
path = node.root().file
# add the message
self.reporter.handle_message(
- Message(msgid, symbol,
- (path, module, obj, line or 1, col_offset or 0), msg))
+ Message(msgid, symbol,
+ (path, module, obj, line or 1, col_offset or 0), msg, confidence))
def print_full_documentation(self):
"""output a full documentation in ReST format"""
@@ -519,14 +536,13 @@ class FileState(object):
except KeyError:
self._module_msgs_state[msg.msgid] = {line: status}
- def handle_ignored_message(self, msgid, line, node, args):
+ def handle_ignored_message(self, state_scope, msgid, line, node, args, confidence):
"""Report an ignored message.
state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG,
depending on whether the message was disabled locally in the module,
or globally. The other arguments are the same as for add_message.
"""
- state_scope = self._message_state_scope(msgid, line)
if state_scope == MSG_STATE_SCOPE_MODULE:
try:
orig_line = self._suppression_mapping[(msgid, line)]
@@ -534,14 +550,6 @@ class FileState(object):
except KeyError:
pass
- def _message_state_scope(self, msgid, line=None):
- """Returns the scope at which a message was enabled/disabled."""
- try:
- if line in self._module_msgs_state[msgid]:
- return MSG_STATE_SCOPE_MODULE
- except KeyError:
- return MSG_STATE_SCOPE_CONFIG
-
def iter_spurious_suppression_messages(self, msgs_store):
for warning, lines in self._raw_module_msgs_state.iteritems():
for line, enable in lines.iteritems():