mitmproxy / mitmproxy/pdoc

pdoc treats overridden methods as inherited when mixing Generic[...] base class with non-generic ABC in multiple inheritance

Open
#862 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
2.5k
Forks
228
PR merge metrics
No merged PRs in 30d

Description

Problem Description

When a subclass uses multiple inheritance and one of its base classes is a generic ABC (abc.ABC + typing.Generic[...]), pdoc incorrectly marks an overridden method from a non-generic base class as inherited, and does not show the overriding method’s docstring.

This does not happen for:

  • multiple inheritance from two non-generic ABCs, or
  • multiple inheritance from two generic ABCs.

So the misclassification appears specific to mixing a Generic[...] base with a non-generic ABC base.

Steps to reproduce the behavior:
# foo.py
from abc import ABC
from typing import Generic, TypeVar

T = TypeVar("T")
U = TypeVar("U")


class GenericABC1(ABC, Generic[T]):
    """"""

    def method_generic1(self):
        pass


class GenericABC2(ABC, Generic[U]):
    """"""

    def method_generic2(self):
        pass


class ABC1(ABC):
    """"""

    def method1(self):
        pass


class ABC2(ABC):
    """"""

    def method2(self):
        pass


class NonGenericSubclass(ABC1, ABC2):
    """
    Both methods' docstring are displayed, and are not considered inherited.
    """

    def method1(self):
        """method1 implementation docstring"""
        pass

    def method2(self):
        """method2 implementation docstring"""
        pass


class GenericSubclass1(GenericABC1[T], GenericABC2[U]):
    """
    Both methods' docstring are displayed, and are not considered inherited.
    """

    def method_generic1(self):
        """method_generic1 implementation docstring"""
        pass

    def method_generic2(self):
        """method_generic2 implementation docstring"""
        pass


class GenericSubclass2(GenericABC1[T], ABC1):
    """
    method1's docstring should be displayed, but it is considered inherited. Behavior is different than inheriting either from both non-generic ABC, or from both generic ABC, as above.
    """

    def method_generic1(self):
        """method_generic1 implementation docstring"""
        pass

    def method1(self):
        """method1 implementation docstring"""
        pass


if __name__ == "__main__":
    assert GenericSubclass2.method1 is not ABC1.method1
    assert "method1 implementation" in (GenericSubclass2.method1.__doc__ or "")

pdoc ./foo

Open the generated HTML for module foo and scroll to GenericSubclass2.

Observed Behavior

For GenericSubclass2(GenericABC1[T], ABC1):

  • method_generic1 is shown normally with its docstring.
  • method1 is not shown as a concrete method on GenericSubclass2.
  • Instead, method1 appears under Inherited Members as ABC1.method1().
  • The docstring "method1 implementation docstring" is not displayed as the subclass implementation.
Image
Expected Behavior

Because GenericSubclass2 explicitly defines def method1(self): ..., pdoc should:

  • treat method1 as belonging to GenericSubclass2 (not inherited), and
  • display the overriding method’s docstring.

This is the same behavior pdoc already has for NonGenericSubclass(ABC1, ABC2) and GenericSubclass1(GenericABC1[T], GenericABC2[U]).

System Information
pdoc --version
pdoc: 16.0.0
Python: 3.12.11
Platform: macOS-15.6.1-arm64-arm-64bit

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by saving the provided reproducer as foo.py and running pdoc ./foo, then inspect the generated HTML for GenericSubclass2. Trace how pdoc distinguishes concrete and inherited methods when GenericABC1[T] and ABC1 are mixed. Done means method1 is listed on GenericSubclass2 with its implementation docstring, not only under inherited members.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
documentation
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.