1
0
forked from VimPlug/jedi

Fix an issue with displaying attribute errors.

This commit is contained in:
Dave Halter
2016-12-18 21:37:15 +01:00
parent 7fdcdbbd5c
commit 4e62e98539
3 changed files with 18 additions and 17 deletions

View File

@@ -77,15 +77,15 @@ class Warning(Error):
pass pass
def add(context, error_name, node, message=None, typ=Error, payload=None): def add(node_context, error_name, node, message=None, typ=Error, payload=None):
exception = CODES[error_name][1] exception = CODES[error_name][1]
if _check_for_exception_catch(context, node, exception, payload): if _check_for_exception_catch(node_context, node, exception, payload):
return return
module_path = node.get_root_node().path module_path = node.get_root_node().path
instance = typ(error_name, module_path, node.start_pos, message) instance = typ(error_name, module_path, node.start_pos, message)
debug.warning(str(instance), format=False) debug.warning(str(instance), format=False)
context.evaluator.analysis.append(instance) node_context.evaluator.analysis.append(instance)
def _check_for_setattr(instance): def _check_for_setattr(instance):
@@ -107,29 +107,29 @@ def _check_for_setattr(instance):
for stmt in stmts) for stmt in stmts)
def add_attribute_error(context, name): def add_attribute_error(name_context, lookup_context, name):
message = ('AttributeError: %s has no attribute %s.' % (context, name)) message = ('AttributeError: %s has no attribute %s.' % (lookup_context, name))
from jedi.evaluate.instance import AbstractInstanceContext, CompiledInstanceName from jedi.evaluate.instance import AbstractInstanceContext, CompiledInstanceName
# Check for __getattr__/__getattribute__ existance and issue a warning # Check for __getattr__/__getattribute__ existance and issue a warning
# instead of an error, if that happens. # instead of an error, if that happens.
typ = Error typ = Error
if isinstance(context, AbstractInstanceContext): if isinstance(lookup_context, AbstractInstanceContext):
slot_names = context.get_function_slot_names('__getattr__') + \ slot_names = lookup_context.get_function_slot_names('__getattr__') + \
context.get_function_slot_names('__getattribute__') lookup_context.get_function_slot_names('__getattribute__')
for n in slot_names: for n in slot_names:
if isinstance(name, CompiledInstanceName) and \ if isinstance(name, CompiledInstanceName) and \
n.parent_context.obj == object: n.parent_context.obj == object:
typ = Warning typ = Warning
break break
if _check_for_setattr(context): if _check_for_setattr(lookup_context):
typ = Warning typ = Warning
payload = context, name payload = lookup_context, name
add(context, 'attribute-error', name, message, typ, payload) add(name_context, 'attribute-error', name, message, typ, payload)
def _check_for_exception_catch(context, jedi_name, exception, payload=None): def _check_for_exception_catch(node_context, jedi_name, exception, payload=None):
""" """
Checks if a jedi object (e.g. `Statement`) sits inside a try/catch and Checks if a jedi object (e.g. `Statement`) sits inside a try/catch and
doesn't count as an error (if equal to `exception`). doesn't count as an error (if equal to `exception`).
@@ -157,7 +157,7 @@ def _check_for_exception_catch(context, jedi_name, exception, payload=None):
if node is None: if node is None:
return True # An exception block that catches everything. return True # An exception block that catches everything.
else: else:
except_classes = context.eval_node(node) except_classes = node_context.eval_node(node)
for cls in except_classes: for cls in except_classes:
from jedi.evaluate import iterable from jedi.evaluate import iterable
if isinstance(cls, iterable.AbstractSequence) and \ if isinstance(cls, iterable.AbstractSequence) and \
@@ -182,7 +182,7 @@ def _check_for_exception_catch(context, jedi_name, exception, payload=None):
arglist = trailer.children[1] arglist = trailer.children[1]
assert arglist.type == 'arglist' assert arglist.type == 'arglist'
from jedi.evaluate.param import TreeArguments from jedi.evaluate.param import TreeArguments
args = list(TreeArguments(context.evaluator, context, arglist).unpack()) args = list(TreeArguments(node_context.evaluator, node_context, arglist).unpack())
# Arguments should be very simple # Arguments should be very simple
assert len(args) == 2 assert len(args) == 2

View File

@@ -67,11 +67,12 @@ class NameFinder(object):
isinstance(self._name.parent.parent, tree.Param)): isinstance(self._name.parent.parent, tree.Param)):
if isinstance(self._name, tree.Name): if isinstance(self._name, tree.Name):
if attribute_lookup: if attribute_lookup:
analysis.add_attribute_error(self._context, self._name) analysis.add_attribute_error(
self._name_context, self._context, self._name)
else: else:
message = ("NameError: name '%s' is not defined." message = ("NameError: name '%s' is not defined."
% self._string_name) % self._string_name)
analysis.add(self._context, 'name-error', self._name, message) analysis.add(self._name_context, 'name-error', self._name, message)
return types return types

View File

@@ -112,7 +112,7 @@ def test_goto_assignments_on_non_name():
assert api.Script('True').goto_assignments() == [] assert api.Script('True').goto_assignments() == []
else: else:
# In Python 2.7 True is still a name. # In Python 2.7 True is still a name.
assert api.Script('True').goto_assignments()[0].description == 'class bool' assert api.Script('True').goto_assignments()[0].description == 'instance True'
def test_goto_definitions_on_non_name(): def test_goto_definitions_on_non_name():