Skip to content

Empty transform plugin leads to false-positive missing-function-docstring issues #10352

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

Open
efroemling opened this issue Apr 24, 2025 · 0 comments
Labels
False Positive 🦟 A message is emitted but nothing is wrong with the code Needs reproduction 🔍 Need a way to reproduce it locally on a maintainer's machine

Comments

@efroemling
Copy link
Contributor

efroemling commented Apr 24, 2025

Bug description

I have recently started replacing typing.TypeVar/typing.Generic usage with Python 3.12 style type-params in my codebase, and it has led to some odd new false positive issues being detected by pylint.

I have distilled things down to as simple a repro case as I can. It is worth noting that I am using a custom pylint transform plugin in my codebase and I found the issues only manifest with it enabled. Normally I would assume my plugin is at fault; however I also found that the issues persist even if I strip the plugin down to do nothing at all, so I figured it would still be worth submitting.

To repro the issue, place the following code in pylintbug.py:

"""Example of slight wonkiness with type params"""
# pylint: disable=too-few-public-methods

from typing import TypeVar, Generic


# ------------------------------------------------------------------------------
# Using new-style (Python 3.12) template parameters results in a
# complaint about missing docstrings in a method we override.
#
# This complaint seems very dependent on these specific conditions. If
# we disable the empty pylint plugin, take the middle class out of the
# equation, remove the __init__() from the child class, or even switch
# the order of the do_something_* overrides in the child class, the
# complaint goes away.

class BaseClass[T]:
    """A generic class"""

    def do_something(self) -> None:
        """Method meant to be overridden by child classes"""

class MiddleClass[T](BaseClass[T]):
    """Inherits from base generic class"""

    def do_something_2(self) -> None:
        """Method meant to be overridden by child classes"""


class ChildClass(MiddleClass[int]):
    """Concrete subclass of the middle one above"""

    def __init__(self) -> None:
        pass

    # Trying to override base class method results in pylint complaining
    # about a missing docstring.
    def do_something(self) -> None:
        pass

    # Trying to override middle class method does not complain about
    # missing docstring (expected behavior).
    def do_something_2(self) -> None:
        pass


# ------------------------------------------------------------------------------
# If we define the equivalent setup using old style TypeVar/Generic, we
# get no missing-docstring complaints.

T = TypeVar('T')

class BaseClassOld(Generic[T]):
    """A generic class using old-style type-vars"""

    def do_something(self) -> None:
        """Method meant to be overridden by child classes"""

class MiddleClassOld(BaseClassOld[T]):
    """Inherits from base generic class"""

    def do_something_2(self) -> None:
        """Method meant to be overridden by child classes"""


class ChildClassOld(MiddleClassOld[int]):
    """Concrete subclass of the middle one above"""

    def __init__(self) -> None:
        pass

    # In this case, neither method override results in missing-docstring
    # complaints.

    def do_something(self) -> None:
        pass

    def do_something_2(self) -> None:
        pass

Also place the following code in pylintbugplug.py:

"""A transform plugin that does nothing.

Strangely the issue only shows up when this plugin is active.
"""

from typing import Any

import astroid
import astroid.nodes
import astroid.manager


def func_annotations_filter(node: astroid.nodes.NodeNG) -> astroid.nodes.NodeNG:
    """Filter FunctionDefs"""

    # Do nothing.
    return node


def register(linter: Any) -> None:  # pylint: disable=unused-argument
    """Unused here; we're only registering plugins."""


def register_plugins(manager: astroid.manager.AstroidManager) -> None:
    """Register a transform for FunctionDef that does nothing."""
    manager.register_transform(astroid.FunctionDef, func_annotations_filter)


register_plugins(astroid.MANAGER)

Now run the following command from directory containing pylintbugplug.py and pylintbug.py:

PYTHONPATH=. pylint --load-plugins=pylintbugplug pylintbug.py

Pylint output

************* Module pylintbug
pylintbug.py:38:4: C0116: Missing function or method docstring (missing-function-docstring)

Expected behavior

I would expect the test case to not complain about any missing docstrings since all methods without docstrings are overrides.
You can see in the example code that the old style setup using typing.Generic/typing.TypeVar behaves as expected, but the new style type-param equivalent does not.

Pylint version

pylint 3.3.6
astroid 3.3.9
Python 3.12.10 (main, Apr  8 2025, 11:35:47) [Clang 16.0.0 (clang-1600.0.26.6)]

OS / Environment

Sequoia 15.4.1 on Apple Silicon

Additional dependencies

@efroemling efroemling added the Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling label Apr 24, 2025
@Pierre-Sassoulas Pierre-Sassoulas added Needs reproduction 🔍 Need a way to reproduce it locally on a maintainer's machine False Positive 🦟 A message is emitted but nothing is wrong with the code and removed Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling labels Apr 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
False Positive 🦟 A message is emitted but nothing is wrong with the code Needs reproduction 🔍 Need a way to reproduce it locally on a maintainer's machine
Projects
None yet
Development

No branches or pull requests

2 participants