diff --git a/jedi/evaluate/analysis.py b/jedi/evaluate/analysis.py index 322780e7..b750bc77 100644 --- a/jedi/evaluate/analysis.py +++ b/jedi/evaluate/analysis.py @@ -82,6 +82,20 @@ def add(evaluator, name, jedi_obj, message=None, typ=Error, payload=None): evaluator.analysis.append(instance) +def _check_for_setattr(instance): + """ + Check if there's any setattr method inside an instance. If so, return True. + """ + module = instance.get_parent_until() + try: + stmts = module.used_names['setattr'] + except KeyError: + return False + + return any(instance.start_pos < stmt.start_pos < instance.end_pos + for stmt in stmts) + + def add_attribute_error(evaluator, scope, name_part): message = ('AttributeError: %s has no attribute %s.' % (scope, name_part)) from jedi.evaluate.representation import Instance @@ -95,7 +109,8 @@ def add_attribute_error(evaluator, scope, name_part): try: scope.get_subscope_by_name('__getattribute__') except KeyError: - typ = Error + if not _check_for_setattr(scope): + typ = Error else: typ = Error diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 15ee8499..bd03c618 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -115,7 +115,6 @@ class NameFinder(object): if result: break - self._last_filter_name_scope = name_list_scope scope_txt = (self.scope if self.scope == name_list_scope else '%s-%s' % (self.scope, name_list_scope)) debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self.name_str, diff --git a/test/static_analysis/attribute_warnings.py b/test/static_analysis/attribute_warnings.py index 73a9f2b3..d31058cc 100644 --- a/test/static_analysis/attribute_warnings.py +++ b/test/static_analysis/attribute_warnings.py @@ -3,6 +3,10 @@ Jedi issues warnings for possible errors if ``__getattr__``, ``__getattribute__`` or ``setattr`` are used. """ +# ----------------- +# __getattr*__ +# ----------------- + class Cls(): def __getattr__(self, name): @@ -22,3 +26,21 @@ Inherited().upper #! 12 warning attribute-error Inherited().undefined + +# ----------------- +# setattr +# ----------------- + + +class SetattrCls(): + def __init__(self, dct): + # Jedi doesn't even try to understand such code + for k, v in dct: + setattr(self, k, v) + + self.defined = 3 + +c = SetattrCls({'a': 'b'}) +c.defined +#! 2 warning attribute-error +c.undefined