Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(67)

Unified Diff: third_party/pylint/checkers/typecheck.py

Issue 719313003: Revert "pylint: upgrade to 1.3.1" (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/pylint/checkers/strings.py ('k') | third_party/pylint/checkers/utils.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/pylint/checkers/typecheck.py
diff --git a/third_party/pylint/checkers/typecheck.py b/third_party/pylint/checkers/typecheck.py
index fb8f3c6e769c5217992bb7d82e4e5fb2d0dd1368..b0a591f485c617f96a69ac0ffab43c1c74a25b33 100644
--- a/third_party/pylint/checkers/typecheck.py
+++ b/third_party/pylint/checkers/typecheck.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2006-2013 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2006-2010 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program is free software; you can redistribute it and/or modify it under
@@ -12,128 +12,59 @@
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-"""try to find more bugs in the code using astroid inference capabilities
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"""try to find more bugs in the code using astng inference capabilities
"""
import re
import shlex
-import astroid
-from astroid import InferenceError, NotFoundError, YES, Instance
-from astroid.bases import BUILTINS
+from logilab import astng
+from logilab.astng import InferenceError, NotFoundError, YES, Instance
-from pylint.interfaces import IAstroidChecker
+from pylint.interfaces import IASTNGChecker
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.'),
'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 \
+ astng 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 \
inferred function doesn\'t return anything.'),
'W1111': ('Assigning to function call which only returns None',
- 'assignment-from-none',
'Used when an assignment is done on a function call but the \
inferred function returns nothing but None.'),
- 'E1120': ('No value for argument %s in %s call',
- 'no-value-for-parameter',
+ 'E1120': ('No value passed for parameter %s in function call',
'Used when a function call passes too few arguments.'),
- 'E1121': ('Too many positional arguments for %s call',
- 'too-many-function-args',
+ 'E1121': ('Too many positional arguments for function call',
'Used when a function call passes too many positional \
arguments.'),
- 'E1122': ('Duplicate keyword argument %r in %s call',
- 'duplicate-keyword-arg',
+ 'E1122': ('Duplicate keyword argument %r in function call',
'Used when a function call passes the same keyword argument \
- multiple times.',
- {'maxversion': (2, 6)}),
- 'E1123': ('Unexpected keyword argument %r in %s call',
- 'unexpected-keyword-arg',
+ multiple times.'),
+ 'E1123': ('Passing unexpected keyword argument %r in function call',
'Used when a function call passes a keyword argument that \
doesn\'t correspond to one of the function\'s parameter names.'),
- 'E1124': ('Argument %r passed by position and keyword in %s call',
- 'redundant-keyword-arg',
+ 'E1124': ('Multiple values passed for parameter %r in function call',
'Used when a function call would result in assigning multiple \
values to a function parameter, one value from a positional \
argument and one from a keyword argument.'),
- 'E1125': ('Missing mandatory keyword argument %r in %s call',
- 'missing-kwoa',
- ('Used when a function call does not pass a mandatory'
- ' keyword-only argument.'),
- {'minversion': (3, 0)}),
- 'E1126': ('Sequence index is not an int, slice, or instance with __index__',
- 'invalid-sequence-index',
- 'Used when a sequence type is indexed with an invalid type. '
- 'Valid types are ints, slices, and objects with an __index__ '
- 'method.'),
- 'E1127': ('Slice index is not an int, None, or instance with __index__',
- 'invalid-slice-index',
- 'Used when a slice index is not an integer, None, or an object \
- with an __index__ method.'),
}
-# builtin sequence types in Python 2 and 3.
-SEQUENCE_TYPES = set(['str', 'unicode', 'list', 'tuple', 'bytearray',
- 'xrange', 'range', 'bytes', 'memoryview'])
-
-def _determine_callable(callable_obj):
- # Ordering is important, since BoundMethod is a subclass of UnboundMethod,
- # and Function inherits Lambda.
- if isinstance(callable_obj, astroid.BoundMethod):
- # Bound methods have an extra implicit 'self' argument.
- return callable_obj, 1, callable_obj.type
- elif isinstance(callable_obj, astroid.UnboundMethod):
- return callable_obj, 0, 'unbound method'
- elif isinstance(callable_obj, astroid.Function):
- return callable_obj, 0, callable_obj.type
- elif isinstance(callable_obj, astroid.Lambda):
- return callable_obj, 0, 'lambda'
- elif isinstance(callable_obj, astroid.Class):
- # Class instantiation, lookup __new__ instead.
- # If we only find object.__new__, we can safely check __init__
- # instead.
- try:
- # Use the last definition of __new__.
- new = callable_obj.local_attr('__new__')[-1]
- except astroid.NotFoundError:
- new = None
-
- if not new or new.parent.scope().name == 'object':
- try:
- # Use the last definition of __init__.
- callable_obj = callable_obj.local_attr('__init__')[-1]
- except astroid.NotFoundError:
- # do nothing, covered by no-init.
- raise ValueError
- else:
- callable_obj = new
-
- if not isinstance(callable_obj, astroid.Function):
- raise ValueError
- # both have an extra implicit 'cls'/'self' argument.
- return callable_obj, 1, 'constructor'
- else:
- raise ValueError
-
class TypeChecker(BaseChecker):
"""try to find bugs in the code using type inference
"""
- __implements__ = (IAstroidChecker,)
+ __implements__ = (IASTNGChecker,)
# configuration section name
name = 'typecheck'
@@ -146,38 +77,31 @@ class TypeChecker(BaseChecker):
'help' : 'Tells whether missing members accessed in mixin \
class should be ignored. A mixin class is detected if its name ends with \
"mixin" (case insensitive).'}
- ),
- ('ignored-modules',
- {'default': (),
- 'type': 'csv',
- 'metavar': '<module names>',
- 'help': 'List of module names for which member attributes \
-should not be checked (useful for modules/projects where namespaces are \
-manipulated during runtime and thus existing member attributes cannot be \
-deduced by static analysis'},
- ),
+ ),
+
('ignored-classes',
{'default' : ('SQLObject',),
'type' : 'csv',
'metavar' : '<members names>',
'help' : 'List of classes names for which member attributes \
should not be checked (useful for classes with attributes dynamically set).'}
- ),
+ ),
('zope',
{'default' : False, 'type' : 'yn', 'metavar': '<y_or_n>',
'help' : 'When zope mode is activated, add a predefined set \
of Zope acquired attributes to generated-members.'}
- ),
+ ),
('generated-members',
- {'default' : ('REQUEST', 'acl_users', 'aq_parent'),
+ {'default' : (
+ 'REQUEST', 'acl_users', 'aq_parent'),
'type' : 'string',
'metavar' : '<members names>',
'help' : 'List of members which are set dynamically and \
missed by pylint inference system, and so shouldn\'t trigger E0201 when \
accessed. Python regular expressions are accepted.'}
- ),
- )
+ ),
+ )
def open(self):
# do this in open since config not fully initialized in __init__
@@ -186,13 +110,13 @@ accessed. Python regular expressions are accepted.'}
self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent'))
def visit_assattr(self, node):
- if isinstance(node.ass_type(), astroid.AugAssign):
+ if isinstance(node.ass_type(), astng.AugAssign):
self.visit_getattr(node)
def visit_delattr(self, node):
self.visit_getattr(node)
- @check_messages('no-member', 'maybe-no-member')
+ @check_messages('E1101', 'E1103')
def visit_getattr(self, node):
"""check that the accessed attribute exists
@@ -208,7 +132,6 @@ accessed. Python regular expressions are accepted.'}
if isinstance(self.config.generated_members, str):
gen = shlex.shlex(self.config.generated_members)
gen.whitespace += ','
- gen.wordchars += '[]-+'
self.config.generated_members = tuple(tok.strip('"') for tok in gen)
for pattern in self.config.generated_members:
# attribute is marked as generated, stop here
@@ -228,7 +151,7 @@ accessed. Python regular expressions are accepted.'}
inference_failure = True
continue
# skip None anyway
- if isinstance(owner, astroid.Const) and owner.value is None:
+ if isinstance(owner, astng.Const) and owner.value is None:
continue
# XXX "super" / metaclass call
if is_super(owner) or getattr(owner, 'type', None) == 'metaclass':
@@ -240,19 +163,19 @@ accessed. Python regular expressions are accepted.'}
continue
try:
if not [n for n in owner.getattr(node.attrname)
- if not isinstance(n.statement(), astroid.AugAssign)]:
+ if not isinstance(n.statement(), astng.AugAssign)]:
missingattr.add((owner, name))
continue
except AttributeError:
# XXX method / function
continue
except NotFoundError:
- if isinstance(owner, astroid.Function) and owner.decorators:
- continue
+ if isinstance(owner, astng.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:
+ # explicit skipping of optparse'Values class
+ if owner.name == 'Values' and owner.root().name == 'optparse':
continue
missingattr.add((owner, name))
continue
@@ -271,122 +194,55 @@ accessed. Python regular expressions are accepted.'}
continue
done.add(actual)
if inference_failure:
- msgid = 'maybe-no-member'
+ msgid = 'E1103'
else:
- msgid = 'no-member'
+ msgid = 'E1101'
self.add_message(msgid, node=node,
args=(owner.display_type(), name,
node.attrname))
- @check_messages('assignment-from-no-return', 'assignment-from-none')
+
def visit_assign(self, node):
"""check that if assigning to a function call, the function is
possibly returning something valuable
"""
- if not isinstance(node.value, astroid.CallFunc):
+ if not isinstance(node.value, astng.CallFunc):
return
function_node = safe_infer(node.value.func)
# skip class, generator and incomplete function definition
- if not (isinstance(function_node, astroid.Function) and
+ if not (isinstance(function_node, astng.Function) and
function_node.root().fully_defined()):
return
if function_node.is_generator() \
or function_node.is_abstract(pass_is_abstract=False):
return
- returns = list(function_node.nodes_of_class(astroid.Return,
- skip_klass=astroid.Function))
+ returns = list(function_node.nodes_of_class(astng.Return,
+ skip_klass=astng.Function))
if len(returns) == 0:
- self.add_message('assignment-from-no-return', node=node)
+ self.add_message('E1111', node=node)
else:
for rnode in returns:
- if not (isinstance(rnode.value, astroid.Const)
- and rnode.value.value is None
- or rnode.value is None):
+ if not (isinstance(rnode.value, astng.Const)
+ and rnode.value.value is None):
break
else:
- self.add_message('assignment-from-none', node=node)
+ self.add_message('W1111', node=node)
- def _check_uninferable_callfunc(self, node):
- """
- Check that the given uninferable CallFunc node does not
- call an actual function.
- """
- if not isinstance(node.func, astroid.Getattr):
- return
-
- # Look for properties. First, obtain
- # the lhs of the Getattr node and search the attribute
- # there. If that attribute is a property or a subclass of properties,
- # then most likely it's not callable.
-
- # TODO: since astroid doesn't understand descriptors very well
- # we will not handle them here, right now.
-
- expr = node.func.expr
- klass = safe_infer(expr)
- if (klass is None or klass is astroid.YES or
- not isinstance(klass, astroid.Instance)):
- return
-
- try:
- attrs = klass._proxied.getattr(node.func.attrname)
- except astroid.NotFoundError:
- return
-
- stop_checking = False
- for attr in attrs:
- if attr is astroid.YES:
- continue
- if stop_checking:
- break
- if not isinstance(attr, astroid.Function):
- continue
-
- # Decorated, see if it is decorated with a property
- if not attr.decorators:
- continue
- for decorator in attr.decorators.nodes:
- if not isinstance(decorator, astroid.Name):
- continue
- try:
- for infered in decorator.infer():
- property_like = False
- if isinstance(infered, astroid.Class):
- if (infered.root().name == BUILTINS and
- infered.name == 'property'):
- property_like = True
- else:
- for ancestor in infered.ancestors():
- if (ancestor.name == 'property' and
- ancestor.root().name == BUILTINS):
- property_like = True
- break
- if property_like:
- self.add_message('not-callable', node=node,
- args=node.func.as_string())
- stop_checking = True
- break
- except InferenceError:
- pass
- if stop_checking:
- break
-
- @check_messages(*(list(MSGS.keys())))
def visit_callfunc(self, node):
"""check that called functions/methods are inferred to callable objects,
and that the arguments passed to the function match the parameters in
the inferred function's definition
"""
+
# Build the set of keyword arguments, checking for duplicate keywords,
# and count the positional arguments.
keyword_args = set()
num_positional_args = 0
for arg in node.args:
- if isinstance(arg, astroid.Keyword):
+ if isinstance(arg, astng.Keyword):
keyword = arg.arg
if keyword in keyword_args:
- self.add_message('duplicate-keyword-arg', node=node,
- args=(keyword, 'function'))
+ self.add_message('E1122', node=node, args=keyword)
keyword_args.add(keyword)
else:
num_positional_args += 1
@@ -394,23 +250,31 @@ accessed. Python regular expressions are accepted.'}
called = safe_infer(node.func)
# only function, generator and object defining __call__ are allowed
if called is not None and not called.callable():
- self.add_message('not-callable', node=node,
- args=node.func.as_string())
-
- self._check_uninferable_callfunc(node)
-
- try:
- called, implicit_args, callable_name = _determine_callable(called)
- except ValueError:
- # Any error occurred during determining the function type, most of
- # those errors are handled by different warnings.
+ self.add_message('E1102', node=node, args=node.func.as_string())
+
+ # Note that BoundMethod is a subclass of UnboundMethod (huh?), so must
+ # come first in this 'if..else'.
+ if isinstance(called, astng.BoundMethod):
+ # Bound methods have an extra implicit 'self' argument.
+ num_positional_args += 1
+ elif isinstance(called, astng.UnboundMethod):
+ if called.decorators is not None:
+ for d in called.decorators.nodes:
+ if isinstance(d, astng.Name) and (d.name == 'classmethod'):
+ # Class methods have an extra implicit 'cls' argument.
+ num_positional_args += 1
+ break
+ elif (isinstance(called, astng.Function) or
+ isinstance(called, astng.Lambda)):
+ pass
+ else:
return
- num_positional_args += implicit_args
+
if called.args.args is None:
# Built-in functions have no argument information.
return
- if len(called.argnames()) != len(set(called.argnames())):
+ if len( called.argnames() ) != len( set( called.argnames() ) ):
# Duplicate parameter name (see E9801). We can't really make sense
# of the function call in this case, so just return.
return
@@ -420,15 +284,15 @@ accessed. Python regular expressions are accepted.'}
parameters = []
parameter_name_to_index = {}
for i, arg in enumerate(called.args.args):
- if isinstance(arg, astroid.Tuple):
+ if isinstance(arg, astng.Tuple):
name = None
# Don't store any parameter names within the tuple, since those
# are not assignable from keyword arguments.
else:
- if isinstance(arg, astroid.Keyword):
+ if isinstance(arg, astng.Keyword):
name = arg.arg
else:
- assert isinstance(arg, astroid.AssName)
+ assert isinstance(arg, astng.AssName)
# This occurs with:
# def f( (a), (b) ): pass
name = arg.name
@@ -439,15 +303,6 @@ accessed. Python regular expressions are accepted.'}
defval = None
parameters.append([(name, defval), False])
- kwparams = {}
- for i, arg in enumerate(called.args.kwonlyargs):
- if isinstance(arg, astroid.Keyword):
- name = arg.arg
- else:
- assert isinstance(arg, astroid.AssName)
- name = arg.name
- kwparams[name] = [called.args.kw_defaults[i], False]
-
# Match the supplied arguments against the function parameters.
# 1. Match the positional arguments.
@@ -460,8 +315,7 @@ accessed. Python regular expressions are accepted.'}
break
else:
# Too many positional arguments.
- self.add_message('too-many-function-args',
- node=node, args=(callable_name,))
+ self.add_message('E1121', node=node)
break
# 2. Match the keyword arguments.
@@ -470,24 +324,15 @@ accessed. Python regular expressions are accepted.'}
i = parameter_name_to_index[keyword]
if parameters[i][1]:
# Duplicate definition of function parameter.
- self.add_message('redundant-keyword-arg',
- node=node, args=(keyword, callable_name))
+ self.add_message('E1124', node=node, args=keyword)
else:
parameters[i][1] = True
- elif keyword in kwparams:
- if kwparams[keyword][1]: # XXX is that even possible?
- # Duplicate definition of function parameter.
- self.add_message('redundant-keyword-arg', node=node,
- args=(keyword, callable_name))
- else:
- kwparams[keyword][1] = True
elif called.args.kwarg is not None:
# The keyword argument gets assigned to the **kwargs parameter.
pass
else:
# Unexpected keyword argument.
- self.add_message('unexpected-keyword-arg', node=node,
- args=(keyword, callable_name))
+ self.add_message('E1123', node=node, args=keyword)
# 3. Match the *args, if any. Note that Python actually processes
# *args _before_ any keyword arguments, but we wait until after
@@ -521,133 +366,10 @@ accessed. Python regular expressions are accepted.'}
for [(name, defval), assigned] in parameters:
if (defval is None) and not assigned:
if name is None:
- display_name = '<tuple>'
+ display = '<tuple>'
else:
display_name = repr(name)
- self.add_message('no-value-for-parameter', node=node,
- args=(display_name, callable_name))
-
- for name in kwparams:
- defval, assigned = kwparams[name]
- if defval is None and not assigned:
- self.add_message('missing-kwoa', node=node,
- args=(name, callable_name))
-
- @check_messages('invalid-sequence-index')
- def visit_extslice(self, node):
- # Check extended slice objects as if they were used as a sequence
- # index to check if the object being sliced can support them
- return self.visit_index(node)
-
- @check_messages('invalid-sequence-index')
- def visit_index(self, node):
- if not node.parent or not hasattr(node.parent, "value"):
- return
-
- # Look for index operations where the parent is a sequence type.
- # If the types can be determined, only allow indices to be int,
- # slice or instances with __index__.
-
- parent_type = safe_infer(node.parent.value)
-
- if not isinstance(parent_type, (astroid.Class, astroid.Instance)):
- return
-
- # Determine what method on the parent this index will use
- # The parent of this node will be a Subscript, and the parent of that
- # node determines if the Subscript is a get, set, or delete operation.
- operation = node.parent.parent
- if isinstance(operation, astroid.Assign):
- methodname = '__setitem__'
- elif isinstance(operation, astroid.Delete):
- methodname = '__delitem__'
- else:
- methodname = '__getitem__'
-
- # Check if this instance's __getitem__, __setitem__, or __delitem__, as
- # appropriate to the statement, is implemented in a builtin sequence
- # type. This way we catch subclasses of sequence types but skip classes
- # that override __getitem__ and which may allow non-integer indices.
- try:
- methods = parent_type.getattr(methodname)
- if methods is astroid.YES:
- return
- itemmethod = methods[0]
- except (astroid.NotFoundError, IndexError):
- return
-
- if not isinstance(itemmethod, astroid.Function):
- return
-
- if itemmethod.root().name != BUILTINS:
- return
-
- if not itemmethod.parent:
- return
-
- if itemmethod.parent.name not in SEQUENCE_TYPES:
- return
-
- # For ExtSlice objects coming from visit_extslice, no further
- # inference is necessary, since if we got this far the ExtSlice
- # is an error.
- if isinstance(node, astroid.ExtSlice):
- index_type = node
- else:
- index_type = safe_infer(node)
-
- if index_type is None or index_type is astroid.YES:
- return
-
- # Constants must be of type int
- if isinstance(index_type, astroid.Const):
- if isinstance(index_type.value, int):
- return
- # Instance values must be int, slice, or have an __index__ method
- elif isinstance(index_type, astroid.Instance):
- if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'):
- return
-
- try:
- index_type.getattr('__index__')
- return
- except astroid.NotFoundError:
- pass
-
- # Anything else is an error
- self.add_message('invalid-sequence-index', node=node)
-
- @check_messages('invalid-slice-index')
- def visit_slice(self, node):
- # Check the type of each part of the slice
- for index in (node.lower, node.upper, node.step):
- if index is None:
- continue
-
- index_type = safe_infer(index)
-
- if index_type is None or index_type is astroid.YES:
- continue
-
- # Constants must of type int or None
- if isinstance(index_type, astroid.Const):
- if isinstance(index_type.value, (int, type(None))):
- continue
- # Instance values must be of type int, None or an object
- # with __index__
- elif isinstance(index_type, astroid.Instance):
- if index_type.pytype() in (BUILTINS + '.int',
- BUILTINS + '.NoneType'):
- continue
-
- try:
- index_type.getattr('__index__')
- return
- except astroid.NotFoundError:
- pass
-
- # Anything else is an error
- self.add_message('invalid-slice-index', node=node)
+ self.add_message('E1120', node=node, args=display_name)
def register(linter):
"""required method to auto register this checker """
« no previous file with comments | « third_party/pylint/checkers/strings.py ('k') | third_party/pylint/checkers/utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698