-
-
Notifications
You must be signed in to change notification settings - Fork 275
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
Add qname
attribute to AssignAttr
to prevent crashes
#1373
Conversation
This change prevents a crash that occurs when reassigning module names and retrieving their `qname`. In some constructions an `AssignAttr` is inferred and treated as a `ClassDef`.
for more information, see https://pre-commit.ci
a0cac85
to
dd7c47f
Compare
I have brought the crashing code down to. Class names resemble those found in the original offending code: import datetime
# Change the modules
datetime.date = FakeDate
from django.db.models.sql.query import Query
class Lookup:
def __init__(self, param):
if isinstance(param, Query):
raise NotImplementedError
Lookup() Where class Query:
... This crashes with The crash doesn't necessarily seem to be in pylint crashed with a ``AttributeError`` and with the following stacktrace:
Traceback (most recent call last):
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/pylint/lint/pylinter.py", line 1034, in _check_files
self._check_file(get_ast, check_astroid_module, file)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/pylint/lint/pylinter.py", line 1069, in _check_file
check_astroid_module(ast_node)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/pylint/lint/pylinter.py", line 1203, in check_astroid_module
retval = self._check_astroid_module(
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/pylint/lint/pylinter.py", line 1250, in _check_astroid_module
walker.walk(node)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/pylint/utils/ast_walker.py", line 75, in walk
self.walk(child)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/pylint/utils/ast_walker.py", line 72, in walk
callback(astroid)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/pylint/checkers/variables.py", line 1339, in visit_importfrom
module = self._check_module_attrs(node, module, name_parts[1:])
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/pylint/checkers/variables.py", line 2168, in _check_module_attrs
module = next(module.getattr(name)[0].infer())
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py", line 622, in getattr
result = [self.import_module(name, relative_only=True)]
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py", line 739, in import_module
return AstroidManager().ast_from_module_name(absmodname)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/manager.py", line 211, in ast_from_module_name
return self.ast_from_file(found_spec.location, modname, fallback=False)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/manager.py", line 124, in ast_from_file
return AstroidBuilder(self).file_build(filepath, modname)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/builder.py", line 151, in file_build
return self._post_build(module, encoding)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/builder.py", line 168, in _post_build
self.add_from_names_to_locals(from_node)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/builder.py", line 224, in add_from_names_to_locals
imported = node.do_import_module()
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/mixins.py", line 108, in do_import_module
return mymodule.import_module(
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py", line 739, in import_module
return AstroidManager().ast_from_module_name(absmodname)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/manager.py", line 211, in ast_from_module_name
return self.ast_from_file(found_spec.location, modname, fallback=False)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/manager.py", line 124, in ast_from_file
return AstroidBuilder(self).file_build(filepath, modname)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/builder.py", line 151, in file_build
return self._post_build(module, encoding)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/builder.py", line 175, in _post_build
module = self._manager.visit_transforms(module)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/manager.py", line 101, in visit_transforms
return self._transform.visit(node)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 96, in visit
return self._visit(module)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 61, in _visit
visited = self._visit_generic(value)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 68, in _visit_generic
return [self._visit_generic(child) for child in node]
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 68, in <listcomp>
return [self._visit_generic(child) for child in node]
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 74, in _visit_generic
return self._visit(node)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 61, in _visit
visited = self._visit_generic(value)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 68, in _visit_generic
return [self._visit_generic(child) for child in node]
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 68, in <listcomp>
return [self._visit_generic(child) for child in node]
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 74, in _visit_generic
return self._visit(node)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 61, in _visit
visited = self._visit_generic(value)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 74, in _visit_generic
return self._visit(node)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 64, in _visit
return self._transform(node)
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/astroid/transforms.py", line 45, in _transform
if predicate is None or predicate(node):
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/pylint_django/transforms/foreignkey.py", line 17, in is_foreignkey_in_class
is_in_django_model_class = node_is_subclass(node.parent.parent, "django.db.models.base.Model", ".Model")
File "/Users/daniel/.pyenv/versions/3.10.0/envs/astroid-3.10.0/lib/python3.10/site-packages/pylint_django/utils.py", line 24, in node_is_subclass
if inf.qname() in subclass_names:
AttributeError: 'AssignAttr' object has no attribute 'qname' The offending line seems to be in |
Thanks! This is really great. I find that the So our approach seems to be:
Let me know what you think. |
Oh wow, have I just found another crash while trying to reproduce the crash of this issue? 😅
I think this is the best approach, although I would like a small investigation as to why
I don't think that is the right way forward. That seems to add a method just so we don't crash. |
Okay, great. Agree on I can look again into how That investigation can be discussed in the |
This change prevents a crash that occurs when reassigning module names and retrieving their
qname
. In some constructions anAssignAttr
is inferred and treated as aClassDef
.Steps
Description
The testing package freezegun includes this construction, which overrides a module name.
This can cause an
AssignAttr
to become part of an inference for a class definition and appear where we normally expect onlyClassDef
.This causes the crash in the linked issue.
The below file also creates a related crash in
pylint_django
.The most straightforward fix is to give
AssignAttr
aqname
method likeClassDef
have. For theAssignAttr
datetime
, the second one, this would bedatetime.datetime
.This fixes the crashes described above and passes the Astroid tests and Pylint tests on Python 3.9.
I have not been able to create a concise reproduction example, so I am not sure how to test it. The examples we have seem to involve the intersection of Django and Freezegun. I have already spent a decent effort trying to figure out exactly what causes the crash without much headway, but the fix is so straightforward and seems safe enough that I wanted to just submit it and see what we think about merging it anyways.
Type of Changes
Related Issue
Closes pylint-dev/pylint#5717