diff --git a/changelog/7780.doc.rst b/changelog/7780.doc.rst new file mode 100644 index 00000000000..631873b156e --- /dev/null +++ b/changelog/7780.doc.rst @@ -0,0 +1 @@ +Classes which should not be inherited from are now marked ``final class`` in the API reference. diff --git a/doc/en/conf.py b/doc/en/conf.py index c631484aa34..12e90afef7d 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -15,8 +15,10 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. +import ast import os import sys +from typing import List from _pytest import __version__ as version from _pytest.compat import TYPE_CHECKING @@ -398,3 +400,22 @@ def setup(app: "sphinx.application.Sphinx") -> None: ) configure_logging(app) + + # Make Sphinx mark classes with "final" when decorated with @final. + # We need this because we import final from pytest._compat, not from + # typing (for Python < 3.8 compat), so Sphinx doesn't detect it. + # To keep things simple we accept any `@final` decorator. + # Ref: https://github.com/pytest-dev/pytest/pull/7780 + import sphinx.pycode.ast + import sphinx.pycode.parser + + original_is_final = sphinx.pycode.parser.VariableCommentPicker.is_final + + def patched_is_final(self, decorators: List[ast.expr]) -> bool: + if original_is_final(self, decorators): + return True + return any( + sphinx.pycode.ast.unparse(decorator) == "final" for decorator in decorators + ) + + sphinx.pycode.parser.VariableCommentPicker.is_final = patched_is_final