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

pylint crashed with a AstroidError (astroid.exceptions.ParentMissingError) #7680

Open
sharky98 opened this issue Oct 26, 2022 · 2 comments
Open
Labels
Crash 💥 A bug that makes pylint crash Needs astroid update Needs an astroid update (probably a release too) before being mergable Needs investigation 🔬 A bug or crash where it's not immediately obvious what is happenning

Comments

@sharky98
Copy link

Bug description

When trying to lint a file that contain the DeclarativeBase of the recent SQLAlchemy 2.0 (beta), it fails with an astroid.exceptions.ParentMissingError: Parent not found on <Const.str l.None at 0x189844d3730>. error. I was able to pin point the issue with the new sqlalchemy.orm.DeclarativeBase.

I understand this is most probably due to the new SQLAlchemy 2.0, but since the traceback point to pylint, I created to issue here. Maybe some guys from SQLAlchemy could help out here too? @CaselIT @zzzeek (hopefully that will ping them!)

Code that makes pylint crash

The code below is based on the migration guide and the declarative mixins.
The only difference I made is to put the mixin directly in the base.

# pylint: disable=missing-docstring
from sqlalchemy.orm import DeclarativeBase, declared_attr


class Base(DeclarativeBase):
    @classmethod
    @declared_attr.directive
    def __tablename__(cls):
        return cls.__name__.lower()

Stacktrace

Traceback (most recent call last):
  File "[project_root].venv\lib\site-packages\pylint\lint\pylinter.py", line 790, in _lint_file
    check_astroid_module(module)
  File "[project_root].venv\lib\site-packages\pylint\lint\pylinter.py", line 1060, in check_astroid_module
    retval = self._check_astroid_module(
  File "[project_root].venv\lib\site-packages\pylint\lint\pylinter.py", line 1110, in _check_astroid_module
    walker.walk(node)
  File "[project_root].venv\lib\site-packages\pylint\utils\ast_walker.py", line 93, in walk
    self.walk(child)
  File "[project_root].venv\lib\site-packages\pylint\utils\ast_walker.py", line 93, in walk
    self.walk(child)
  File "[project_root].venv\lib\site-packages\pylint\utils\ast_walker.py", line 90, in walk
    callback(astroid)
  File "[project_root].venv\lib\site-packages\pylint\checkers\classes\special_methods_checker.py", line 183, in visit_functiondef
    inferred = _safe_infer_call_result(node, node)
  File "[project_root].venv\lib\site-packages\pylint\checkers\classes\special_methods_checker.py", line 42, in _safe_infer_call_result
    value = next(inferit)
  File "[project_root].venv\lib\site-packages\astroid\nodes\scoped_nodes\scoped_nodes.py", line 1749, in infer_call_result
    yield from returnnode.value.infer(context)
  File "[project_root].venv\lib\site-packages\astroid\nodes\node_ng.py", line 169, in infer
    yield from self._infer(context=context, **kwargs)
  File "[project_root].venv\lib\site-packages\astroid\decorators.py", line 140, in raise_if_nothing_inferred
    yield next(generator)
  File "[project_root].venv\lib\site-packages\astroid\decorators.py", line 109, in wrapped
    for res in _func(node, context, **kwargs):
  File "[project_root].venv\lib\site-packages\astroid\inference.py", line 255, in infer_call
    for callee in self.func.infer(context):
  File "[project_root].venv\lib\site-packages\astroid\nodes\node_ng.py", line 182, in infer
    for i, result in enumerate(self._infer(context=context, **kwargs)):
  File "[project_root].venv\lib\site-packages\astroid\decorators.py", line 140, in raise_if_nothing_inferred
    yield next(generator)
  File "[project_root].venv\lib\site-packages\astroid\decorators.py", line 109, in wrapped
    for res in _func(node, context, **kwargs):
  File "[project_root].venv\lib\site-packages\astroid\inference.py", line 343, in infer_attribute
    for owner in self.expr.infer(context):
  File "[project_root].venv\lib\site-packages\astroid\nodes\node_ng.py", line 182, in infer
    for i, result in enumerate(self._infer(context=context, **kwargs)):
  File "[project_root].venv\lib\site-packages\astroid\decorators.py", line 140, in raise_if_nothing_inferred
    yield next(generator)
  File "[project_root].venv\lib\site-packages\astroid\decorators.py", line 109, in wrapped
    for res in _func(node, context, **kwargs):
  File "[project_root].venv\lib\site-packages\astroid\inference.py", line 352, in infer_attribute
    yield from owner.igetattr(self.attrname, context)
  File "[project_root].venv\lib\site-packages\astroid\nodes\scoped_nodes\scoped_nodes.py", line 2646, in igetattr
    first_scope = first_attr.scope()
  File "[project_root].venv\lib\site-packages\astroid\nodes\node_ng.py", line 359, in scope
    raise ParentMissingError(target=self)
astroid.exceptions.ParentMissingError: Parent not found on <Const.str l.None at 0x189844d3730>.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "[project_root].venv\lib\site-packages\pylint\lint\pylinter.py", line 755, in _lint_files
    self._lint_file(fileitem, module, check_astroid_module)
  File "[project_root].venv\lib\site-packages\pylint\lint\pylinter.py", line 792, in _lint_file
    raise astroid.AstroidError from e
astroid.exceptions.AstroidError

Code that works

Having the mixin code not in the Base seems to resolve the issue.

# pylint: disable=missing-docstring
from sqlalchemy.orm import DeclarativeBase, Mapped, declared_attr, mapped_column


class Base(DeclarativeBase):
    pass


class CommonMixin:
    @classmethod
    @declared_attr.directive
    def __tablename__(cls) -> str:
        return cls.__name__.lower()

    id: Mapped[int] = mapped_column(primary_key=True)

So does getting rid of DeclarativeBase

# pylint: disable=missing-docstring
class Base:
    @classmethod
    def __tablename__(cls):
        """My Docstring"""
        return cls.__name__.lower()

Configuration

No response

Command used

pylint test.py

Pylint output

************* Module test
test.py:1:0: F0002: test.py: Fatal error while checking 'test.py'. Please open an issue in our bug tracker so we address this. There is a pre-filled template that you can use in '[...]'. (astroid-error)

Expected behavior

No issues found.

Pylint version

pylint 2.15.5
astroid 2.12.12
Python 3.10.5 (tags/v3.10.5:f377153, Jun  6 2022, 16:14:13) [MSC v.1929 64 bit (AMD64)]

OS / Environment

Windows 10
Poetry

Additional dependencies

sqlalchemy = {version = "^2.0.0b2", allow-prereleases = true}

@sharky98 sharky98 added the Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling label Oct 26, 2022
@zzzeek
Copy link

zzzeek commented Oct 26, 2022

definitely on pylint side, we run many code quality tools on sqlalchemy including flake8 with lots of extensions, mypy, pyright, pylance, black, and others.

pylint 2.15.5 reproduces the issue, while an older version I had installed, 2.11.1, does not:

$ pylint test3.py 
************* Module test3
test3.py:9:0: C0304: Final newline missing (missing-final-newline)
test3.py:5:0: R0903: Too few public methods (1/2) (too-few-public-methods)

------------------------------------------------------------------
Your code has been rated at 5.00/10 (previous run: 5.00/10, +0.00)

@Pierre-Sassoulas Pierre-Sassoulas added Crash 💥 A bug that makes pylint crash Needs investigation 🔬 A bug or crash where it's not immediately obvious what is happenning Needs astroid update Needs an astroid update (probably a release too) before being mergable and removed Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling labels Oct 26, 2022
@widal001
Copy link

I had a very similar issue in #9446 and simply removing the @classmethod worked for me:

class UUIDAuditBase(DeclarativeBase):
    """Base db model that includes id, created_at, and update_at."""

    id: Mapped[UUID] = mapped_column(primary_key=True)
    created_at: Mapped[DateTime] = mapped_column(
        DateTime(timezone=True),
        default=functions.now(),
    )
    updated_at: Mapped[DateTime] = mapped_column(
        DateTime(timezone=True),
        default=functions.now(),
        onupdate=functions.now(),
    )

    @declared_attr.directive
    def __tablename__(cls) -> str:  # noqa: N805
        """Set default table name as the lowercase version of the class name."""
        return cls.__name__.lower()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Crash 💥 A bug that makes pylint crash Needs astroid update Needs an astroid update (probably a release too) before being mergable Needs investigation 🔬 A bug or crash where it's not immediately obvious what is happenning
Projects
None yet
Development

No branches or pull requests

4 participants