Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnboundLocalError: local variable 'key_cls' referenced before assignment #232

Closed
federicobond opened this issue Apr 26, 2019 · 19 comments
Closed

Comments

@federicobond
Copy link
Contributor

I found this failure in my CI logs, but could not reproduce it locally. The corresponding code looks wrong but I don't know exactly how to correct it. If I can be of any help fixing it let me know.

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/astroid/__init__.py", line 91, in _inference_tip_cached
    return iter(_cache[func, node])
KeyError: (<function infer_key_classes at 0x7fe149cfe048>, <Call l.306 at 0x7fe142e66320>)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/pylint", line 11, in <module>
    sys.exit(run_pylint())
  File "/usr/local/lib/python3.6/site-packages/pylint/__init__.py", line 20, in run_pylint
    Run(sys.argv[1:])
  File "/usr/local/lib/python3.6/site-packages/pylint/lint.py", line 1628, in __init__
    linter.check(args)
  File "/usr/local/lib/python3.6/site-packages/pylint/lint.py", line 943, in check
    self._do_check(files_or_modules)
  File "/usr/local/lib/python3.6/site-packages/pylint/lint.py", line 1075, in _do_check
    self.check_astroid_module(ast_node, walker, rawcheckers, tokencheckers)
  File "/usr/local/lib/python3.6/site-packages/pylint/lint.py", line 1158, in check_astroid_module
    walker.walk(ast_node)
  File "/usr/local/lib/python3.6/site-packages/pylint/utils.py", line 1303, in walk
    self.walk(child)
  File "/usr/local/lib/python3.6/site-packages/pylint/utils.py", line 1300, in walk
    cb(astroid)
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 57, in augment_func
    augmentation(chain, node)
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 148, in do_suppress
    chain()
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 56, in chain
    old_method(node)
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 57, in augment_func
    augmentation(chain, node)
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 148, in do_suppress
    chain()
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 56, in chain
    old_method(node)
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 57, in augment_func
    augmentation(chain, node)
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 148, in do_suppress
    chain()
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 56, in chain
    old_method(node)
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 57, in augment_func
    augmentation(chain, node)
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 148, in do_suppress
    chain()
  File "/usr/local/lib/python3.6/site-packages/pylint_plugin_utils/__init__.py", line 56, in chain
    old_method(node)
  File "/usr/local/lib/python3.6/site-packages/pylint/checkers/classes.py", line 729, in visit_classdef
    self._check_bases_classes(node)
  File "/usr/local/lib/python3.6/site-packages/pylint/checkers/classes.py", line 1444, in _check_bases_classes
    unimplemented_abstract_methods(node, is_abstract).items(),
  File "/usr/local/lib/python3.6/site-packages/pylint/checkers/utils.py", line 782, in unimplemented_abstract_methods
    infered = safe_infer(obj)
  File "/usr/local/lib/python3.6/site-packages/pylint/checkers/utils.py", line 1066, in safe_infer
    value = next(inferit)
  File "/usr/local/lib/python3.6/site-packages/astroid/decorators.py", line 131, in raise_if_nothing_inferred
    yield next(generator)
  File "/usr/local/lib/python3.6/site-packages/astroid/decorators.py", line 95, in wrapped
    res = next(generator)
  File "/usr/local/lib/python3.6/site-packages/astroid/bases.py", line 137, in _infer_stmts
    for inferred in stmt.infer(context=context):
  File "/usr/local/lib/python3.6/site-packages/astroid/node_classes.py", line 348, in infer
    return self._explicit_inference(self, context, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/astroid/__init__.py", line 93, in _inference_tip_cached
    result = func(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/pylint_django/transforms/foreignkey.py", line 91, in infer_key_classes
    return iter([key_cls.instantiate_class()])
UnboundLocalError: local variable 'key_cls' referenced before assignment

Version: pylint-django==2.0.8

@atodorov
Copy link
Contributor

Can you update to 2.0.9 and see if it reproduces.

Also we'll need a code snippet which is triggering the error otherwise there's nothing we can do.

@atodorov
Copy link
Contributor

@prokher from #229 (comment)

Just received the same error in 2.0.9.

Please don't comment on closed issues, we are bound to miss them. Also post a reproducer.

CC @imomaliev

@imomaliev
Copy link
Contributor

imomaliev commented Apr 27, 2019

@federicobond Hi, could you provide a code snippet that fails?

@carlio @prokher does it also occur on 2.0.8?

@federicobond
Copy link
Contributor Author

I could not reproduce this again, however, now I have another error with 2.0.9. I will open an issue soon.

@zout
Copy link

zout commented May 1, 2019

I can confirm that we still have the issue with 2.0.9, and already had it with 2.0.8.

$ docker run --rm --name=test_${CI_COMMIT_SHA}_linter -t test:${CI_COMMIT_SHA} pylint --load-plugins pylint_django /usr/src/app/app/
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/astroid/__init__.py", line 91, in _inference_tip_cached
    return iter(_cache[func, node])
KeyError: (<function infer_key_classes at 0x7f4ab4bd1268>, <Call l.44 at 0x7f4ab2b78358>)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/pylint", line 10, in <module>
    sys.exit(run_pylint())
  File "/usr/local/lib/python3.5/dist-packages/pylint/__init__.py", line 20, in run_pylint
    Run(sys.argv[1:])
  File "/usr/local/lib/python3.5/dist-packages/pylint/lint.py", line 1628, in __init__
    linter.check(args)
  File "/usr/local/lib/python3.5/dist-packages/pylint/lint.py", line 943, in check
    self._do_check(files_or_modules)
  File "/usr/local/lib/python3.5/dist-packages/pylint/lint.py", line 1075, in _do_check
    self.check_astroid_module(ast_node, walker, rawcheckers, tokencheckers)
  File "/usr/local/lib/python3.5/dist-packages/pylint/lint.py", line 1158, in check_astroid_module
    walker.walk(ast_node)
  File "/usr/local/lib/python3.5/dist-packages/pylint/utils.py", line 1303, in walk
    self.walk(child)
  File "/usr/local/lib/python3.5/dist-packages/pylint/utils.py", line 1303, in walk
    self.walk(child)
  File "/usr/local/lib/python3.5/dist-packages/pylint/utils.py", line 1303, in walk
    self.walk(child)
  File "/usr/local/lib/python3.5/dist-packages/pylint/utils.py", line 1303, in walk
    self.walk(child)
  File "/usr/local/lib/python3.5/dist-packages/pylint/utils.py", line 1300, in walk
    cb(astroid)
  File "/usr/local/lib/python3.5/dist-packages/pylint/checkers/base.py", line 766, in visit_call
    self._check_inferred_class_is_abstract(inferred, node)
  File "/usr/local/lib/python3.5/dist-packages/pylint/checkers/base.py", line 783, in _check_inferred_class_is_abstract
    abstract_methods = _has_abstract_methods(infered)
  File "/usr/local/lib/python3.5/dist-packages/pylint/checkers/base.py", line 353, in _has_abstract_methods
    return len(utils.unimplemented_abstract_methods(node)) > 0
  File "/usr/local/lib/python3.5/dist-packages/pylint/checkers/utils.py", line 782, in unimplemented_abstract_methods
    infered = safe_infer(obj)
  File "/usr/local/lib/python3.5/dist-packages/pylint/checkers/utils.py", line 1066, in safe_infer
    value = next(inferit)
  File "/usr/local/lib/python3.5/dist-packages/astroid/decorators.py", line 131, in raise_if_nothing_inferred
    yield next(generator)
  File "/usr/local/lib/python3.5/dist-packages/astroid/decorators.py", line 95, in wrapped
    res = next(generator)
  File "/usr/local/lib/python3.5/dist-packages/astroid/bases.py", line 137, in _infer_stmts
    for inferred in stmt.infer(context=context):
  File "/usr/local/lib/python3.5/dist-packages/astroid/node_classes.py", line 348, in infer
    return self._explicit_inference(self, context, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/astroid/__init__.py", line 93, in _inference_tip_cached
    result = func(*args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/pylint_django/transforms/foreignkey.py", line 112, in infer_key_classes
    return iter([key_cls.instantiate_class()])
UnboundLocalError: local variable 'key_cls' referenced before assignment

@atodorov
Copy link
Contributor

atodorov commented May 1, 2019

@zout this traceback is useless without a code snippet to reproduce the problem. As you can see from our test suite we've added some code samples for the use-cases we could think of. When you see a failure for something that apparently works in CI you have to provide us with a reproducer.

So far the entire discussion and "me too" comments here are useless because we don't have a reproducer and we can't do anything else to debug. If you want this fixed provide us with a code snippet that causes the crash.

@zout
Copy link

zout commented May 1, 2019

@zout this traceback is useless without a code snippet to reproduce the problem. As you can see from our test suite we've added some code samples for the use-cases we could think of. When you see a failure for something that apparently works in CI you have to provide us with a reproducer.

So far the entire discussion and "me too" comments here are useless because we don't have a reproducer and we can't do anything else to debug. If you want this fixed provide us with a code snippet that causes the crash.

I've did some checking, and it seems that I have to import the model in the scope to make it work.
So:
generated_app = models.ForeignKey('App', default=None, null=True, blank=True, on_delete=models.DO_NOTHING) this code works without importing actual App into the scope because I have a __init__.py where everything gets imported (i think). When i add a from .app import App it does not give this issue anymore.

When i run the exact same project with

pylint==1.9.3
pylint-django==2.0.6

there are no issues at all.

So i've fixed it by importing all models into the scope of the file.

UPDATE
As it turns out the project does not run anymore because of circulair imports. Anyhow, it has something to do with foreign keys and the scope of the file and pylint not able to resolve it the way Django / python can.

@zout
Copy link

zout commented May 3, 2019

@atodorov I've narrowed it down to OneToOne relations. On my github there is a project that will trigger the error. Hope it helps in finding out the root cause: https://github.com/zout/django-pylint-issue

@snstanton
Copy link

snstanton commented May 10, 2019

The following simple file triggers the error for me:

from django.db import models

class BadModel(models.Model):
    bad = models.ForeignKey('Other', null=True, on_delete=models.CASCADE)

    def foo(self):
        return self.bad.name

@atodorov
Copy link
Contributor

@snstanton where is 'Other' model defined ?

@snstanton
Copy link

snstanton commented May 10, 2019

It isn't defined anywhere. In my test case it's an unbound reference. This is the minimal test case I could come up with to cause the pylint crash.

In my production code, it's just defined in another file in the same directory. I have a directory structure like:

app/models/
  __init__.py
  model1.py
  model2.py
  ...

All of the model files are included by the package __init__.py. To deal with circular references, most of the ForeignKey fields are defined using the string syntax rather than actually imported references.

@snstanton
Copy link

I can confirm that rolling back to

pylint==1.9.3
pylint-django==2.0.6

does work around the issue.

@atodorov
Copy link
Contributor

ok, I think I see where the problem is. When FK is specified as string we execute this branch:

        elif isinstance(arg, nodes.Const):

and inside of it key_cls is indeed never assigned to so when execution jumps to the return statement at the end it breaks.

@imomaliev ^^^. Not sure how to handle this ATM.

@imomaliev
Copy link
Contributor

I will look into this in on next weekend. Currently on vacation. I think it could be solved by filtering out incoming args

@snstanton
Copy link

Any progress on this issue?

@imomaliev
Copy link
Contributor

@snstanton not yet. Been busy with work. If somebody is up for fixing this I could help reviewing PR afterwards. If not, I will hopefully have time this week

@imomaliev
Copy link
Contributor

Created PR with fix for UnboundLocalError #234 but don't know how to properly make @snstanton 's case work. infer_key_classes should be able to introspect not only module with Model but all modules in models package to achieve this

@chocoelho
Copy link

@imomaliev looks like the issue is that other ForeignKey arguments are being evaluated as well. This line here: https://github.com/PyCQA/pylint-django/blob/c3edd260d94ca607d2c05a5cc33324921fae55e4/pylint_django/transforms/foreignkey.py#L78 is the one causing the whole problem, as in @snstanton case, the null=True is being evaluated and it throws the AttributeError exception as True will be the value of arg.value in the moment. Because it has a break clause for this exception, the return is called (thus it fail because it's unbound at the moment).

atodorov added a commit that referenced this issue Jul 7, 2019
b/c this test file is only to validate we don't get the traceback
seen in #232
atodorov added a commit that referenced this issue Jul 7, 2019
b/c this test file is only to validate we don't get the traceback
seen in #232
atodorov added a commit that referenced this issue Jul 7, 2019
b/c this test file is only to validate we don't get the traceback
seen in #232
@atodorov
Copy link
Contributor

atodorov commented Aug 6, 2019

The original traceback reported at the top has been fixed by @imomaliev in version 2.0.10 so closing this. For further discussion open new issues.

@atodorov atodorov closed this as completed Aug 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants