diff options
| author | Cosmin Poieana <cmin@ropython.org> | 2015-05-08 11:57:29 +0300 |
|---|---|---|
| committer | Cosmin Poieana <cmin@ropython.org> | 2015-05-08 11:57:29 +0300 |
| commit | 86b3f90914712dfda6dc1cc66e76fd28e03c9c50 (patch) | |
| tree | 3b477b254fe9f8d7378a8b92d00b77daf5c61b07 | |
| parent | 87ecb9516898b2ad1dfc93d4672c90acec34130a (diff) | |
| parent | 28a2583708f2ca51d3d237dd69e91b91aafdafd2 (diff) | |
| download | pylint-86b3f90914712dfda6dc1cc66e76fd28e03c9c50.tar.gz | |
Merge
35 files changed, 359 insertions, 393 deletions
@@ -2,6 +2,10 @@ ChangeLog for Pylint -------------------- -- + * Don't emit import-error for ignored modules. PyLint will not emit import + errors for any import which is, or is a subpackage of, a module in + the ignored-modules list. Closes issue #223. + * Fix unused-import false positive when the import is used in a class assignment. Closes issue #475 @@ -63,7 +67,35 @@ ChangeLog for Pylint Since the __new__ comes from builtins, it will not have attached any information regarding what parameters it expects, so the check will be useless. Retrieving __init__ in that case will at least - detect a couple of false negatives. Closes issue #429. + detect a couple of false negatives. Closes issue #429. + + * Don't emit no-member for classes with unknown bases. + + Since we don't know what those bases might add, we simply ignore + the error in this case. + + * Lookup in the implicit metaclass when checking for no-member, + if the class in question has an implicit metaclass, which is + True for new style classes. Closes issue #438. + + * Add two new warnings, duplicate-bases and inconsistent-mro. + + duplicate-bases is emitted when a class has the same bases + listed more than once in its bases definition, while inconsistent-mro + is emitted when no sane mro hierarchy can be determined. Closes issue #526. + + * Remove interface-not-implemented warning. Closes issue #532. + + * Remove the rest of interface checks: interface-is-not-class, + missing-interface-method, unresolved-interface. The reason is that + its better to start recommending ABCs instead of the old Zope era + of interfaces. One side effect of this change is that ignore-iface-methods + becomes a noop, it's deprecated and it will be removed at some time. + + * Emit a proper deprecation warning for reporters.BaseReporter.add_message. + + The alternative way is to use handle_message. add_message will be removed in + Pylint 1.6. 2015-03-14 -- 1.4.3 diff --git a/doc/faq.rst b/doc/faq.rst index 1ce9a3e..2f7dfbb 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -208,27 +208,14 @@ tricks like: :: 5. Classes and Inheritance ========================== -5.1 When is Pylint considering a class as an interface? -------------------------------------------------------- - -A class is considered as an interface if there is a class named "Interface" -somewhere in its inheritance tree. - -5.2 When is Pylint considering that a class is implementing a given interface? --------------------------------------------------------------------------------- - -Pylint is using the Zope 2 interfaces conventions, and so is -considering that a class is implementing interfaces listed in its -__implements__ attribute. - -5.3 When is Pylint considering a class as an abstract class? +5.1 When is Pylint considering a class as an abstract class? ------------------------------------------------------------- A class is considered as an abstract class if at least one of its methods is doing nothing but raising NotImplementedError. -5.4 How do I avoid "access to undefined member" messages in my mixin classes? +5.2 How do I avoid "access to undefined member" messages in my mixin classes? ------------------------------------------------------------------------------- To do so you have to set the ignore-mixin-members option to diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 250ca0c..b1fe95b 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -699,7 +699,7 @@ functions, methods if value is default: msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()] - elif type(value) is astroid.Instance or is_iterable(value): + elif isinstance(value, astroid.Instance) or is_iterable(value): # We are here in the following situation(s): # * a dict/set/list/tuple call which wasn't inferred # to a syntax node ({}, () etc.). This can happen diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 080abd9..7099b0d 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -23,7 +23,7 @@ from collections import defaultdict import astroid from astroid import YES, Instance, are_exclusive, AssAttr, Class from astroid.bases import Generator, BUILTINS -from astroid.inference import InferenceContext +from astroid.exceptions import InconsistentMroError, DuplicateBasesError from pylint.interfaces import IAstroidChecker from pylint.checkers import BaseChecker @@ -31,6 +31,7 @@ from pylint.checkers.utils import ( PYMETHODS, overrides_a_method, check_messages, is_attr_private, is_attr_protected, node_frame_class, safe_infer, is_builtin_object, decorated_with_property, unimplemented_abstract_methods) +from pylint.utils import deprecated_option import six if sys.version_info >= (3, 0): @@ -170,15 +171,6 @@ MSGS = { 'Used when a method doesn\'t use its bound instance, and so could ' 'be written as a function.' ), - - 'E0221': ('Interface resolved to %s is not a class', - 'interface-is-not-class', - 'Used when a class claims to implement an interface which is not ' - 'a class.'), - 'E0222': ('Missing method %r from %s interface', - 'missing-interface-method', - 'Used when a method declared in an interface is missing from a ' - 'class implementing this interface'), 'W0221': ('Arguments number differs from %s %r method', 'arguments-differ', 'Used when a method has a different number of arguments than in ' @@ -192,12 +184,6 @@ MSGS = { 'Used when an abstract method (i.e. raise NotImplementedError) is ' 'not overridden in concrete class.' ), - 'F0220': ('failed to resolve interfaces implemented by %s (%s)', - 'unresolved-interface', - 'Used when a Pylint as failed to find interfaces implemented by ' - ' a class'), - - 'W0231': ('__init__ method from base class %r is not called', 'super-init-not-called', 'Used when an ancestor class method has an __init__ method ' @@ -236,7 +222,12 @@ MSGS = { 'inherit-non-class', 'Used when a class inherits from something which is not a ' 'class.'), - + 'E0240': ('Inconsistent method resolution order for class %r', + 'inconsistent-mro', + 'Used when a class has an inconsistent method resolutin order.'), + 'E0241': ('Duplicate bases for class %r', + 'duplicate-bases', + 'Used when a class has duplicate bases.'), } @@ -247,7 +238,6 @@ class ClassChecker(BaseChecker): * overridden methods signature * access only to existent members via self * attributes not defined in the __init__ method - * supported interfaces implementation * unreachable code """ @@ -260,21 +250,10 @@ class ClassChecker(BaseChecker): priority = -2 # configuration options options = (('ignore-iface-methods', - {'default' : (#zope interface - 'isImplementedBy', 'deferred', 'extends', 'names', - 'namesAndDescriptions', 'queryDescriptionFor', 'getBases', - 'getDescriptionFor', 'getDoc', 'getName', 'getTaggedValue', - 'getTaggedValueTags', 'isEqualOrExtendedBy', 'setTaggedValue', - 'isImplementedByInstancesOf', - # twisted - 'adaptWith', - # logilab.common interface - 'is_implemented_by'), - 'type' : 'csv', - 'metavar' : '<method names>', - 'help' : 'List of interface methods to ignore, \ -separated by a comma. This is used for instance to not check methods defines \ -in Zope\'s Interface base class.'} + # TODO(cpopa): remove this in Pylint 1.7. + deprecated_option(opt_type="csv", + help_msg="This is deprecated, because " + "it is not used anymore.") ), ('defining-attr-methods', {'default' : ('__init__', '__new__', 'setUp'), @@ -315,12 +294,11 @@ a metaclass class method.'} self._meth_could_be_func = None def visit_class(self, node): - """init visit variable _accessed and check interfaces + """init visit variable _accessed """ self._accessed.append(defaultdict(list)) self._check_bases_classes(node) - self._check_interfaces(node) - # if not an interface, exception, metaclass + # if not an exception or a metaclass if node.type == 'class': try: node.local_attr('__init__') @@ -328,6 +306,20 @@ a metaclass class method.'} self.add_message('no-init', args=node, node=node) self._check_slots(node) self._check_proper_bases(node) + self._check_consistent_mro(node) + + @check_messages('inconsistent-mro', 'duplicate-bases') + def _check_consistent_mro(self, node): + """Detect that a class has a consistent mro or duplicate bases.""" + try: + node.mro() + except InconsistentMroError: + self.add_message('inconsistent-mro', args=node.name, node=node) + except DuplicateBasesError: + self.add_message('duplicate-bases', args=node.name, node=node) + except NotImplementedError: + # Old style class, there's no mro so don't do anything. + pass @check_messages('inherit-non-class') def _check_proper_bases(self, node): @@ -538,8 +530,7 @@ a metaclass class method.'} """on method node, check if this method couldn't be a function ignore class, static and abstract methods, initializer, - methods overridden from a parent class and any - kind of method defined in an interface for this warning + methods overridden from a parent class. """ if node.is_method(): if node.args.args is not None: @@ -551,8 +542,7 @@ a metaclass class method.'} and not node.name in PYMETHODS and not (node.is_abstract() or overrides_a_method(class_node, node.name) or - decorated_with_property(node)) - and class_node.type != 'interface'): + decorated_with_property(node))): self.add_message('no-self-use', node=node) def visit_getattr(self, node): @@ -823,55 +813,6 @@ a metaclass class method.'} self.add_message('abstract-method', node=node, args=(name, owner.name)) - def _check_interfaces(self, node): - """check that the given class node really implements declared - interfaces - """ - e0221_hack = [False] - def iface_handler(obj): - """filter interface objects, it should be classes""" - if not isinstance(obj, astroid.Class): - e0221_hack[0] = True - self.add_message('interface-is-not-class', node=node, - args=(obj.as_string(),)) - return False - return True - ignore_iface_methods = self.config.ignore_iface_methods - try: - for iface in node.interfaces(handler_func=iface_handler): - for imethod in iface.methods(): - name = imethod.name - if name.startswith('_') or name in ignore_iface_methods: - # don't check method beginning with an underscore, - # usually belonging to the interface implementation - continue - # get class method astroid - try: - method = node_method(node, name) - except astroid.NotFoundError: - self.add_message('missing-interface-method', - args=(name, iface.name), - node=node) - continue - # ignore inherited methods - if method.parent.frame() is not node: - continue - # check signature - self._check_signature(method, imethod, - '%s interface' % iface.name) - except astroid.InferenceError: - if e0221_hack[0]: - return - implements = Instance(node).getattr('__implements__')[0] - assignment = implements.parent - assert isinstance(assignment, astroid.Assign) - # assignment.expr can be a Name or a Tuple or whatever. - # Use as_string() for the message - # FIXME: in case of multiple interfaces, find which one could not - # be resolved - self.add_message('unresolved-interface', node=implements, - args=(node.name, assignment.value.as_string())) - def _check_init(self, node): """check that the __init__ method call super or ancestors'__init__ method @@ -923,8 +864,6 @@ a metaclass class method.'} def _check_signature(self, method1, refmethod, class_type): """check that the signature of the two given methods match - - class_type is in 'class', 'interface' """ if not (isinstance(method1, astroid.Function) and isinstance(refmethod, astroid.Function)): diff --git a/pylint/checkers/design_analysis.py b/pylint/checkers/design_analysis.py index 9ff10bf..99d5b32 100644 --- a/pylint/checkers/design_analysis.py +++ b/pylint/checkers/design_analysis.py @@ -18,7 +18,7 @@ import re from collections import defaultdict -from astroid import If, InferenceError +from astroid import If from pylint.interfaces import IAstroidChecker from pylint.checkers import BaseChecker @@ -64,9 +64,6 @@ MSGS = { 'too-many-statements', 'Used when a function or method has too many statements. You \ should then split it in smaller functions / methods.'), - 'R0923': ('Interface not implemented', - 'interface-not-implemented', - 'Used when an interface class is not implemented anywhere.'), } @@ -146,8 +143,6 @@ class MisdesignChecker(BaseChecker): self.stats = None self._returns = None self._branches = None - self._used_ifaces = None - self._ifaces = None self._stmts = 0 def open(self): @@ -155,18 +150,9 @@ class MisdesignChecker(BaseChecker): self.stats = self.linter.add_stats() self._returns = [] self._branches = defaultdict(int) - self._used_ifaces = {} - self._ifaces = [] - - def close(self): - """check that interface classes are used""" - for iface in self._ifaces: - if not iface in self._used_ifaces: - self.add_message('interface-not-implemented', node=iface) @check_messages('too-many-ancestors', 'too-many-instance-attributes', - 'too-few-public-methods', 'too-many-public-methods', - 'interface-not-implemented') + 'too-few-public-methods', 'too-many-public-methods') def visit_class(self, node): """check size of inheritance hierarchy and number of instance attributes """ @@ -182,19 +168,6 @@ class MisdesignChecker(BaseChecker): self.add_message('too-many-instance-attributes', node=node, args=(len(node.instance_attrs), self.config.max_attributes)) - # update interface classes structures - if node.type == 'interface' and node.name != 'Interface': - self._ifaces.append(node) - for parent in node.ancestors(False): - if parent.name == 'Interface': - continue - self._used_ifaces[parent] = 1 - try: - for iface in node.interfaces(): - self._used_ifaces[iface] = 1 - except InferenceError: - # XXX log ? - pass @check_messages('too-few-public-methods', 'too-many-public-methods') def leave_class(self, node): diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py index b195297..54c3413 100644 --- a/pylint/checkers/imports.py +++ b/pylint/checkers/imports.py @@ -29,7 +29,7 @@ from astroid import are_exclusive from astroid.modutils import get_module_part, is_standard_module from pylint.interfaces import IAstroidChecker -from pylint.utils import EmptyReport +from pylint.utils import EmptyReport, get_global_option from pylint.checkers import BaseChecker from pylint.checkers.utils import check_messages, is_import_error @@ -291,9 +291,27 @@ given file (report RP0402 must not be disabled)'} args = '%r (%s)' % (modname, ex) else: args = repr(modname) + + ignored_modules = get_global_option(self, 'ignored-modules', default=[]) + for submodule in self._qualified_names(modname): + if submodule in ignored_modules: + return None + if not _except_import_error(importnode.parent): self.add_message("import-error", args=args, node=importnode) + @staticmethod + def _qualified_names(modname): + """Split the names of the given module into subparts + + For example, + _qualified_names('pylint.checkers.ImportsChecker') + returns + ['pylint', 'pylint.checkers', 'pylint.checkers.ImportsChecker'] + """ + names = modname.split('.') + return ['.'.join(names[0:i+1]) for i in range(len(names))] + def _check_relative_import(self, modnode, importnode, importedmodnode, importedasname): """check relative import. node is either an Import or From node, modname diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index cd5d506..beac18e 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -28,7 +28,7 @@ from pylint.checkers import BaseChecker from pylint.checkers.utils import ( safe_infer, is_super, check_messages, decorated_with_property, - decorated_with) + decorated_with, has_known_bases) MSGS = { 'E1101': ('%s %r has no %r member', @@ -88,6 +88,53 @@ MSGS = { SEQUENCE_TYPES = set(['str', 'unicode', 'list', 'tuple', 'bytearray', 'xrange', 'range', 'bytes', 'memoryview']) + +def _emit_no_member(owner, owner_name, attrname, + ignored_modules, ignored_mixins, ignored_classes): + """Try to see if no-member should be emitted for the given owner. + + The following cases are ignored: + + * the owner is a function and it has decorators. + * the owner is an instance and it has __getattr__, __getattribute__ implemented + * the module is explicitly ignored from no-member checks + * the owner is a class and the name can be found in its metaclass. + """ + if owner_name in ignored_classes: + return False + # skip None anyway + if isinstance(owner, astroid.Const) and owner.value is None: + return False + # TODO(cpopa): This should be removed when we'll understand "super" + if is_super(owner) or getattr(owner, 'type', None) == 'metaclass': + return False + if ignored_mixins and owner_name[-5:].lower() == 'mixin': + return False + if isinstance(owner, astroid.Function) and owner.decorators: + return False + if isinstance(owner, Instance): + if owner.has_dynamic_getattr() or not has_known_bases(owner): + return False + # explicit skipping of module member access + if owner.root().name in ignored_modules: + return False + if isinstance(owner, astroid.Class): + # Look up in the metaclass only if the owner is itself + # a class. + # TODO: getattr doesn't return by default members + # from the metaclass, because handling various cases + # of methods accessible from the metaclass itself + # and/or subclasses only is too complicated for little to + # no benefit. + metaclass = owner.metaclass() or owner.implicit_metaclass() + try: + if metaclass and metaclass.getattr(attrname): + return False + except NotFoundError: + pass + return True + + def _determine_callable(callable_obj): # Ordering is important, since BoundMethod is a subclass of UnboundMethod, # and Function inherits Lambda. @@ -229,24 +276,13 @@ accessed. Python regular expressions are accepted.'} return # list of (node, nodename) which are missing the attribute missingattr = set() - ignoremim = self.config.ignore_mixin_members inference_failure = False for owner in infered: # skip yes object if owner is YES: inference_failure = True continue - # skip None anyway - if isinstance(owner, astroid.Const) and owner.value is None: - continue - # XXX "super" / metaclass call - if is_super(owner) or getattr(owner, 'type', None) == 'metaclass': - continue name = getattr(owner, 'name', 'None') - if name in self.config.ignored_classes: - continue - if ignoremim and name[-5:].lower() == 'mixin': - continue try: if not [n for n in owner.getattr(node.attrname) if not isinstance(n.statement(), astroid.AugAssign)]: @@ -256,27 +292,18 @@ accessed. Python regular expressions are accepted.'} # XXX method / function continue except NotFoundError: - if isinstance(owner, astroid.Function) and owner.decorators: - continue - if isinstance(owner, Instance) and owner.has_dynamic_getattr(): - continue - # explicit skipping of module member access - if owner.root().name in self.config.ignored_modules: + # This can't be moved before the actual .getattr call, + # because there can be more values inferred and we are + # stopping after the first one which has the attribute in question. + # The problem is that if the first one has the attribute, + # but we continue to the next values which doesn't have the + # attribute, then we'll have a false positive. + # So call this only after the call has been made. + if not _emit_no_member(owner, name, node.attrname, + self.config.ignored_modules, + self.config.ignore_mixin_members, + self.config.ignored_classes): continue - if isinstance(owner, astroid.Class): - # Look up in the metaclass only if the owner is itself - # a class. - # TODO: getattr doesn't return by default members - # from the metaclass, because handling various cases - # of methods accessible from the metaclass itself - # and/or subclasses only is too complicated for little to - # no benefit. - metaclass = owner.metaclass() - try: - if metaclass and metaclass.getattr(node.attrname): - continue - except NotFoundError: - pass missingattr.add((owner, name)) continue # stop on the first found diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 1a40e25..d7896a2 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -385,13 +385,6 @@ def node_frame_class(node): return klass -def is_super_call(expr): - """return True if expression node is a function call and if function name - is super. Check before that you're in a method. - """ - return (isinstance(expr, astroid.CallFunc) and - isinstance(expr.func, astroid.Name) and - expr.func.name == 'super') def is_attr_private(attrname): """Check that attribute name is private (at least two leading underscores, diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 8e3789a..45fd76d 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -543,7 +543,7 @@ builtins. Remember that you should avoid to define new builtins when possible.' # don't check arguments of abstract methods or within an interface is_method = node.is_method() klass = node.parent.frame() - if is_method and (klass.type == 'interface' or node.is_abstract()): + if is_method and node.is_abstract(): return if is_method and isinstance(klass, astroid.Class): confidence = INFERENCE if has_known_bases(klass) else INFERENCE_FAILURE diff --git a/pylint/lint.py b/pylint/lint.py index ec03420..19aa838 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -204,13 +204,6 @@ MSGS = { } -def _deprecated_option(shortname, opt_type, help_msg): - def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument - sys.stderr.write('Warning: option %s is deprecated and ignored.\n' % (optname,)) - return {'short': shortname, 'help': help_msg, 'hide': True, - 'type': opt_type, 'action': 'callback', 'callback': _warn_deprecated} - - if multiprocessing is not None: class ChildLinter(multiprocessing.Process): # pylint: disable=no-member def run(self): @@ -389,9 +382,8 @@ class PyLinter(configuration.OptionsManagerMixIn, 'See doc for all details') }), - ('include-ids', _deprecated_option('i', 'yn', - INCLUDE_IDS_HELP)), - ('symbols', _deprecated_option('s', 'yn', SYMBOLS_HELP)), + ('include-ids', utils.deprecated_option('i', 'yn', INCLUDE_IDS_HELP)), + ('symbols', utils.deprecated_option('s', 'yn', SYMBOLS_HELP)), ('jobs', {'type' : 'int', 'metavar': '<n-processes>', @@ -604,8 +596,8 @@ class PyLinter(configuration.OptionsManagerMixIn, def disable_reporters(self): """disable all reporters""" - for reporters in six.itervalues(self._reports): - for report_id, _, _ in reporters: + for _reporters in six.itervalues(self._reports): + for report_id, _, _ in _reporters: self.disable_report(report_id) def error_mode(self): @@ -699,8 +691,8 @@ class PyLinter(configuration.OptionsManagerMixIn, def get_checkers(self): """return all available checkers as a list""" - return [self] + [c for checkers in six.itervalues(self._checkers) - for c in checkers if c is not self] + return [self] + [c for _checkers in six.itervalues(self._checkers) + for c in _checkers if c is not self] def prepare_checkers(self): """return checkers needed for activated messages and reports""" @@ -761,13 +753,14 @@ class PyLinter(configuration.OptionsManagerMixIn, # Prepare configuration for child linters. filter_options = {'symbols', 'include-ids', 'long-help'} filter_options.update([opt_name for opt_name, _ in self._external_opts]) - config = {} + child_config = {} for opt_providers in six.itervalues(self._all_options): for optname, optdict, val in opt_providers.options_and_values(): if optname not in filter_options: - config[optname] = configuration.format_option_value(optdict, val) - config['python3_porting_mode'] = self._python3_porting_mode - config['plugins'] = self._dynamic_plugins + child_config[optname] = configuration.format_option_value( + optdict, val) + child_config['python3_porting_mode'] = self._python3_porting_mode + child_config['plugins'] = self._dynamic_plugins childs = [] manager = multiprocessing.Manager() # pylint: disable=no-member @@ -775,7 +768,7 @@ class PyLinter(configuration.OptionsManagerMixIn, results_queue = manager.Queue() # pylint: disable=no-member for _ in range(self.config.jobs): - cl = ChildLinter(args=(tasks_queue, results_queue, config)) + cl = ChildLinter(args=(tasks_queue, results_queue, child_config)) cl.start() # pylint: disable=no-member childs.append(cl) @@ -816,7 +809,7 @@ class PyLinter(configuration.OptionsManagerMixIn, module = None for result in self._parallel_task(files_or_modules): ( - file_or_module, + _, self.file_state.base_name, module, messages, @@ -842,14 +835,14 @@ class PyLinter(configuration.OptionsManagerMixIn, def _do_check(self, files_or_modules): walker = utils.PyLintASTWalker(self) - checkers = self.prepare_checkers() - tokencheckers = [c for c in checkers + _checkers = self.prepare_checkers() + tokencheckers = [c for c in _checkers if interface.implements(c, interfaces.ITokenChecker) and c is not self] - rawcheckers = [c for c in checkers + rawcheckers = [c for c in _checkers if interface.implements(c, interfaces.IRawChecker)] # notify global begin - for checker in checkers: + for checker in _checkers: checker.open() if interface.implements(checker, interfaces.IAstroidChecker): walker.add_checker(checker) @@ -881,8 +874,7 @@ class PyLinter(configuration.OptionsManagerMixIn, self.add_message(msgid, line, None, args) # notify global end self.stats['statement'] = walker.nbstatements - checkers.reverse() - for checker in checkers: + for checker in reversed(_checkers): checker.close() def expand_files(self, modules): diff --git a/pylint/reporters/__init__.py b/pylint/reporters/__init__.py index db96cf6..7103ade 100644 --- a/pylint/reporters/__init__.py +++ b/pylint/reporters/__init__.py @@ -17,6 +17,8 @@ from __future__ import print_function import sys import locale import os +import warnings + import six @@ -68,7 +70,10 @@ class BaseReporter(object): def add_message(self, msg_id, location, msg): """Deprecated, do not use.""" - raise NotImplementedError + # pylint: disable=no-self-use,unused-argument + msg = ("This method is deprecated, use handle_message instead. " + "It will be removed in Pylint 1.6.") + warnings.warn(msg, DeprecationWarning, stacklevel=2) def set_output(self, output=None): """set output stream""" @@ -128,6 +133,8 @@ class CollectingReporter(BaseReporter): def handle_message(self, msg): self.messages.append(msg) + _display = None + def initialize(linter): """initialize linter with reporters in this package """ diff --git a/pylint/test/data/classes_No_Name.dot b/pylint/test/data/classes_No_Name.dot index 51b42e7..3a9df79 100644 --- a/pylint/test/data/classes_No_Name.dot +++ b/pylint/test/data/classes_No_Name.dot @@ -3,7 +3,7 @@ charset="utf-8" rankdir=BT "0" [label="{Ancestor|attr : str\lcls_member\l|get_value()\lset_value()\l}", shape="record"]; "1" [label="{DoNothing|\l|}", shape="record"]; -"2" [label="{«interface»\nInterface|\l|get_value()\lset_value()\l}", shape="record"]; +"2" [label="{Interface|\l|get_value()\lset_value()\l}", shape="record"]; "3" [label="{Specialization|TYPE : str\lrelation\ltop : str\l|}", shape="record"]; "3" -> "0" [arrowhead="empty", arrowtail="none"]; "0" -> "2" [arrowhead="empty", arrowtail="node", style="dashed"]; diff --git a/pylint/test/functional/class_members_py27.py b/pylint/test/functional/class_members_py27.py index 40423b1..415a890 100644 --- a/pylint/test/functional/class_members_py27.py +++ b/pylint/test/functional/class_members_py27.py @@ -1,6 +1,6 @@ """ Various tests for class members access. """
-# pylint: disable=R0903,print-statement,no-absolute-import, metaclass-assignment
-
+# pylint: disable=R0903,print-statement,no-absolute-import, metaclass-assignment,import-error,no-init,missing-docstring
+from missing import Missing
class MyClass(object):
"""class docstring"""
@@ -49,3 +49,17 @@ TestMetaclass.register(int) UsingMetaclass.test()
TestMetaclass().register(int) # [no-member]
UsingMetaclass().test() # [no-member]
+
+
+class NoKnownBases(Missing):
+ """Don't emit no-member if we don't know the bases of a class."""
+
+NoKnownBases().lalala()
+
+
+class MetaClass(object):
+ """Look some methods in the implicit metaclass."""
+
+ @classmethod
+ def whatever(cls):
+ return cls.mro() + cls.missing() # [no-member]
diff --git a/pylint/test/functional/class_members_py27.txt b/pylint/test/functional/class_members_py27.txt index e5e6005..3a3be0e 100644 --- a/pylint/test/functional/class_members_py27.txt +++ b/pylint/test/functional/class_members_py27.txt @@ -4,3 +4,4 @@ no-member:16:MyClass.test:Instance of 'MyClass' has no 'nonexistent1' member:INF 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 +no-member:65:MetaClass.whatever:Class 'MetaClass' has no 'missing' member:INFERENCE diff --git a/pylint/test/functional/class_members_py30.py b/pylint/test/functional/class_members_py30.py index cf0e7bd..8c27703 100644 --- a/pylint/test/functional/class_members_py30.py +++ b/pylint/test/functional/class_members_py30.py @@ -1,6 +1,6 @@ """ Various tests for class members access. """
-# pylint: disable=R0903
-
+# pylint: disable=R0903,import-error,no-init,missing-docstring
+from missing import Missing
class MyClass(object):
"""class docstring"""
@@ -47,3 +47,17 @@ TestMetaclass.register(int) UsingMetaclass.test()
TestMetaclass().register(int) # [no-member]
UsingMetaclass().test() # [no-member]
+
+
+class NoKnownBases(Missing):
+ """Don't emit no-member if we don't know the bases of a class."""
+
+NoKnownBases().lalala()
+
+
+class MetaClass(object):
+ """Look some methods in the implicit metaclass."""
+
+ @classmethod
+ def whatever(cls):
+ return cls.mro() + cls.missing() # [no-member]
diff --git a/pylint/test/functional/class_members_py30.txt b/pylint/test/functional/class_members_py30.txt index 4696579..d9163ec 100644 --- a/pylint/test/functional/class_members_py30.txt +++ b/pylint/test/functional/class_members_py30.txt @@ -4,3 +4,4 @@ no-member:16:MyClass.test:Instance of 'MyClass' has no 'nonexistent1' member:INF 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
+no-member:63:MetaClass.whatever:Class 'MetaClass' has no 'missing' member:INFERENCE
\ No newline at end of file diff --git a/pylint/test/functional/duplicate_bases.py b/pylint/test/functional/duplicate_bases.py new file mode 100644 index 0000000..974f975 --- /dev/null +++ b/pylint/test/functional/duplicate_bases.py @@ -0,0 +1,15 @@ +"""Test duplicate bases error."""
+# pylint: disable=missing-docstring,too-few-public-methods,no-init
+
+
+class Duplicates(str, str): # [duplicate-bases]
+ pass
+
+
+class Alpha(str):
+ pass
+
+
+class NotDuplicates(Alpha, str):
+ """The error should not be emitted for this case, since the
+ other same base comes from the ancestors."""
diff --git a/pylint/test/functional/duplicate_bases.txt b/pylint/test/functional/duplicate_bases.txt new file mode 100644 index 0000000..beb91b5 --- /dev/null +++ b/pylint/test/functional/duplicate_bases.txt @@ -0,0 +1 @@ +duplicate-bases:5:Duplicates:Duplicate bases for class 'Duplicates'
\ No newline at end of file diff --git a/pylint/test/functional/inconsistent_mro.py b/pylint/test/functional/inconsistent_mro.py new file mode 100644 index 0000000..0b65068 --- /dev/null +++ b/pylint/test/functional/inconsistent_mro.py @@ -0,0 +1,9 @@ +"""Tests for inconsistent-mro."""
+# pylint: disable=missing-docstring,too-few-public-methods,no-init
+
+class Str(str):
+ pass
+
+
+class Inconsistent(str, Str): # [inconsistent-mro]
+ pass
diff --git a/pylint/test/functional/inconsistent_mro.txt b/pylint/test/functional/inconsistent_mro.txt new file mode 100644 index 0000000..1ae9687 --- /dev/null +++ b/pylint/test/functional/inconsistent_mro.txt @@ -0,0 +1 @@ +inconsistent-mro:8:Inconsistent:Inconsistent method resolution order for class 'Inconsistent' diff --git a/pylint/test/functional/interface_not_implemented.py b/pylint/test/functional/interface_not_implemented.py deleted file mode 100644 index 6244ac4..0000000 --- a/pylint/test/functional/interface_not_implemented.py +++ /dev/null @@ -1,31 +0,0 @@ -# pylint: disable=missing-docstring -from __future__ import absolute_import - -from logilab.common.interface import Interface - -class IAaaa(Interface): # [interface-not-implemented] - """yo""" - - def meth1(self): - """hehehe""" - -class IBbbb(Interface): - """yo""" - - def meth1(self): - """hehehe""" - -class Concret(object): - """implements IBbbb""" - __implements__ = IBbbb - - def __init__(self): - pass - - def meth1(self): - """hehehe""" - return "et hop", self - - def meth2(self): - """hehehe""" - return "et hop", self diff --git a/pylint/test/functional/interface_not_implemented.txt b/pylint/test/functional/interface_not_implemented.txt deleted file mode 100644 index ded0625..0000000 --- a/pylint/test/functional/interface_not_implemented.txt +++ /dev/null @@ -1 +0,0 @@ -interface-not-implemented:6:IAaaa:Interface not implemented diff --git a/pylint/test/functional/member_checks.py b/pylint/test/functional/member_checks.py index c112425..38a3488 100644 --- a/pylint/test/functional/member_checks.py +++ b/pylint/test/functional/member_checks.py @@ -1,4 +1,4 @@ -# pylint: disable=print-statement +# pylint: disable=print-statement,missing-docstring,no-self-use,too-few-public-methods """check getattr if inference succeed""" from __future__ import print_function @@ -58,6 +58,32 @@ class Client(object): integer = 1 print(integer.whatever) # [no-member] + def test_no_false_positives(self): + none = None + print(none.whatever) + # This will be handled when we'll understand super + super(Client, self).misssing() + + +class Mixin(object): + """No no-member should be emitted for mixins.""" + +class Getattr(object): + """no-member shouldn't be emitted for classes with dunder getattr.""" + + def __getattr__(self, attr): + return self.__dict__[attr] + + +class Getattribute(object): + """no-member shouldn't be emitted for classes with dunder getattribute.""" + + def __getattribute__(self, attr): + return 42 + print(object.__init__) print(property.__init__) print(Client().set_later.lower()) # [no-member] +print(Mixin().nanana()) +print(Getattr().nananan()) +print(Getattribute().batman()) diff --git a/pylint/test/functional/member_checks.txt b/pylint/test/functional/member_checks.txt index 12fe6ee..9b4d258 100644 --- a/pylint/test/functional/member_checks.txt +++ b/pylint/test/functional/member_checks.txt @@ -6,4 +6,4 @@ no-member:53:Client.test_bt_types:Instance of 'dict' has no 'set' member:INFEREN 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 +no-member:86::Instance of 'int' has no 'lower' member:INFERENCE_FAILURE diff --git a/pylint/test/input/func_e0206.py b/pylint/test/input/func_e0206.py deleted file mode 100644 index a9f5790..0000000 --- a/pylint/test/input/func_e0206.py +++ /dev/null @@ -1,20 +0,0 @@ -# pylint: disable=R0903 -"""check for interface which are not classes""" - -__revision__ = None - -class Abcd(object): - """dummy""" - __implements__ = __revision__ - - def __init__(self): - self.attr = None - -class Cdef(object): - """dummy""" - __implements__ = (__revision__, Abcd) - - def __init__(self): - pass - - diff --git a/pylint/test/input/func_interfaces.py b/pylint/test/input/func_interfaces.py deleted file mode 100644 index 7a716c1..0000000 --- a/pylint/test/input/func_interfaces.py +++ /dev/null @@ -1,112 +0,0 @@ -# pylint:disable=R0201 -"""docstring""" -from __future__ import print_function - -class Interface(object): - """base class for interfaces""" - -class IMachin(Interface): - """docstring""" - def truc(self): - """docstring""" - - def troc(self, argument): - """docstring""" - -class Correct1(object): - """docstring""" - __implements__ = IMachin - - def __init__(self): - pass - - def truc(self): - """docstring""" - pass - - def troc(self, argument): - """docstring""" - pass - -class Correct2(object): - """docstring""" - __implements__ = (IMachin,) - - def __init__(self): - pass - - def truc(self): - """docstring""" - pass - - def troc(self, argument): - """docstring""" - print(argument) - -class MissingMethod(object): - """docstring""" - __implements__ = IMachin, - - def __init__(self): - pass - - def troc(self, argument): - """docstring""" - print(argument) - - def other(self): - """docstring""" - -class BadArgument(object): - """docstring""" - __implements__ = (IMachin,) - - def __init__(self): - pass - - def truc(self): - """docstring""" - pass - - def troc(self): - """docstring""" - pass - -class InterfaceCantBeFound(object): - """docstring""" - __implements__ = undefined - - def __init__(self): - """only to make pylint happier""" - - def please(self): - """public method 1/2""" - - def besilent(self): - """public method 2/2""" - -class InterfaceCanNowBeFound(object): - """docstring""" - __implements__ = BadArgument.__implements__ + Correct2.__implements__ - - def __init__(self): - """only to make pylint happier""" - - def please(self): - """public method 1/2""" - - def besilent(self): - """public method 2/2""" - - -class EmptyImplements(object): - """no pb""" - __implements__ = () - def __init__(self): - """only to make pylint happier""" - - def please(self): - """public method 1/2""" - - def besilent(self): - """public method 2/2""" diff --git a/pylint/test/input/func_noerror_w0232.py b/pylint/test/input/func_noerror_w0232.py index df93855..75b68df 100644 --- a/pylint/test/input/func_noerror_w0232.py +++ b/pylint/test/input/func_noerror_w0232.py @@ -1,10 +1,7 @@ -# pylint: disable=R0903,R0923 +# pylint: disable=R0903 """check interface and exception without __init__ doesn't print warnings """ __revision__ = '' -class Interface: - """interface without docstring""" - class MyError(Exception): """exception without docstring""" diff --git a/pylint/test/messages/func_e0206.txt b/pylint/test/messages/func_e0206.txt deleted file mode 100644 index c15f841..0000000 --- a/pylint/test/messages/func_e0206.txt +++ /dev/null @@ -1,3 +0,0 @@ -E: 6:Abcd: Interface resolved to None is not a class -E: 13:Cdef: Interface resolved to None is not a class - diff --git a/pylint/test/messages/func_interfaces.txt b/pylint/test/messages/func_interfaces.txt deleted file mode 100644 index a4cd21d..0000000 --- a/pylint/test/messages/func_interfaces.txt +++ /dev/null @@ -1,6 +0,0 @@ -E: 46:MissingMethod: Missing method 'truc' from IMachin interface -E: 77:InterfaceCantBeFound: Undefined variable 'undefined' -E: 88:InterfaceCanNowBeFound: Missing method 'troc' from IMachin interface -E: 88:InterfaceCanNowBeFound: Missing method 'truc' from IMachin interface -F: 77:InterfaceCantBeFound: failed to resolve interfaces implemented by InterfaceCantBeFound (undefined) -W: 71:BadArgument.troc: Arguments number differs from IMachin interface 'troc' method diff --git a/pylint/test/unittest_checker_imports.py b/pylint/test/unittest_checker_imports.py new file mode 100644 index 0000000..143d17c --- /dev/null +++ b/pylint/test/unittest_checker_imports.py @@ -0,0 +1,48 @@ +"""Unit tests for the imports checker.""" +import unittest + +from astroid import test_utils +from pylint.checkers import imports +from pylint.testutils import CheckerTestCase, set_config + +class ImportsCheckerTC(CheckerTestCase): + + CHECKER_CLASS = imports.ImportsChecker + + @set_config(ignored_modules=('external_module', 'fake_module.submodule')) + def test_import_error_skipped(self): + """Make sure that imports do not emit a 'import-error' when the + module is configured to be ignored.""" + + node = test_utils.extract_node(""" + from external_module import anything + """) + with self.assertNoMessages(): + self.checker.visit_from(node) + + node = test_utils.extract_node(""" + from external_module.another_module import anything + """) + with self.assertNoMessages(): + self.checker.visit_from(node) + + node = test_utils.extract_node(""" + import external_module + """) + with self.assertNoMessages(): + self.checker.visit_import(node) + + node = test_utils.extract_node(""" + from fake_module.submodule import anything + """) + with self.assertNoMessages(): + self.checker.visit_from(node) + + node = test_utils.extract_node(""" + from fake_module.submodule.deeper import anything + """) + with self.assertNoMessages(): + self.checker.visit_from(node) + +if __name__ == '__main__': + unittest.main() diff --git a/pylint/test/unittest_checker_python3.py b/pylint/test/unittest_checker_python3.py index bac3f4a..7d7b052 100644 --- a/pylint/test/unittest_checker_python3.py +++ b/pylint/test/unittest_checker_python3.py @@ -390,7 +390,7 @@ class Python3CheckerTest(testutils.CheckerTestCase): a = list(range(x)) a.sort(cmp=lambda x: x) #@ - sorted([], cmp=lambda x: x) #@ + sorted([], cmp=lambda x: x) #@ """) for node in nodes: message = testutils.Message('using-cmp-argument', node=node) diff --git a/pylint/test/unittest_reporting.py b/pylint/test/unittest_reporting.py index 03c108e..c98e86b 100644 --- a/pylint/test/unittest_reporting.py +++ b/pylint/test/unittest_reporting.py @@ -17,10 +17,12 @@ import unittest import warnings import six - from logilab.common.ureports import Section + +from pylint import __pkginfo__ from pylint.lint import PyLinter from pylint import checkers +from pylint.reporters import BaseReporter from pylint.reporters.text import TextReporter, ParseableTextReporter from pylint.reporters.html import HTMLReporter @@ -37,6 +39,21 @@ class PyLinterTC(unittest.TestCase): checkers.initialize(self.linter) os.environ.pop('PYLINTRC', None) + def test_add_message_is_deprecated(self): + if __pkginfo__.numversion > (1, 6, 0): + with self.assertRaises(AttributeError): + BaseReporter().add_message + + with warnings.catch_warnings(record=True) as cm: + warnings.simplefilter("always") + BaseReporter().add_message(None, None, None) + + self.assertEqual(len(cm), 1) + self.assertIsInstance(cm[0].message, DeprecationWarning) + msg = ('This method is deprecated, use handle_message ' + 'instead. It will be removed in Pylint 1.6.') + self.assertEqual(str(cm[0].message), msg) + def test_template_option(self): output = six.StringIO() self.linter.reporter.set_output(output) diff --git a/pylint/testutils.py b/pylint/testutils.py index f53d945..e4a0de1 100644 --- a/pylint/testutils.py +++ b/pylint/testutils.py @@ -129,6 +129,8 @@ class TestReporter(BaseReporter): def display_results(self, layout): """ignore layouts""" + _display = None + class Message(collections.namedtuple('Message', ['msg_id', 'line', 'node', 'args'])): @@ -316,7 +318,7 @@ class LintTestUsingFile(LintTestUsingModule): importable += '.py' tocheck = [importable] if self.depends: - tocheck += [join(self.INPUT_DIR, name) for name, _file in self.depends] + tocheck += [join(self.INPUT_DIR, name) for name, _ in self.depends] self._test(tocheck) class LintTestUpdate(LintTestUsingModule): diff --git a/pylint/utils.py b/pylint/utils.py index c91497a..d2c9ed8 100644 --- a/pylint/utils.py +++ b/pylint/utils.py @@ -925,3 +925,19 @@ def get_global_option(checker, option, default=None): if options[0] == option: return getattr(provider.config, option.replace("-", "_")) return default + + +def deprecated_option(shortname=None, opt_type=None, help_msg=None): + def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument + sys.stderr.write('Warning: option %s is deprecated and ignored.\n' % (optname,)) + + option = { + 'help': help_msg, + 'hide': True, + 'type': opt_type, + 'action': 'callback', + 'callback': _warn_deprecated + } + if shortname: + option['shortname'] = shortname + return option @@ -60,7 +60,16 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=C0103,C0111,C0302,C1001,E0239,E1101,R0201,R0401,R0801,R0901,R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0923,W0199,W0201,W0211,W0212,W0221,W0222,W0223,W0231,W0232,W0233,W0234,W0301,W0311,W0312,W0332,W0401,W0402,W0403,W0404,W0406,W0410,W0511,W0512,W0601,W0602,W0603,W0604,W0611,W0612,W0613,W0614,W0621,W0622,W0623,W0631,W0632,W0633,W0640,W0702,W0703,W0704,W0710,W0711,W1001,W1111,W1201,W1202,W1300,W1301,W1302,W1303,W1304,W1305,W1306,W1307,W1401,W1402,W1501,W1502,W1503,W1504,W1601,W1602,W1603,W1604,W1605W1606,W1607,W1608,W1609,W1610,W1611,W1612,W1613,W1614,W1615,W1616,W1617,W1618,W1619,W1620,W1621,W1622,W1623,W1624,W1625,W1626,W1627,W1628,W1629,W1630,W1632,W1633,W1634,W1635,W1636,W1637,W1638,W1639 + +disable=invalid-name,protected-access,fixme,too-many-branches, + attribute-defined-outside-init,too-many-locals, + too-many-arguments,too-many-statements, + too-many-return-statements,too-few-public-methods, + import-error,too-many-lines,too-many-instance-attributes, + too-many-public-methods,duplicate-code,broad-except, + unbalanced-tuple-unpacking,redefined-builtin,anomalous-backslash-in-string, + missing-docstring,no-member + [REPORTS] |
