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

Support using beartype and MyST-Parser for creating a custom role which requires an Inliner #1017

Open
adamtheturtle opened this issue Jan 21, 2025 · 0 comments · May be fixed by #1018
Open
Labels
enhancement New feature or request

Comments

@adamtheturtle
Copy link

Describe the feature you'd like to request

Goals

  • Create a custom role which extends docutils.parsers.rst.roles.code_role.
  • Pass static type checking (mypy)
  • Pass runtime type checking (beartype)

Below is a minimized example of my work towards supporting MyST for substitution-code within https://github.com/adamtheturtle/sphinx-substitution-extensions.

Requirements

  • beartype==0.19.0
  • myst-parser==4.0.0
  • types-docutils==0.21.0.20241128

Files

Layout as described in
https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-extensions.

<project directory>/
├── conf.py
└── sphinxext/
    └── extname.py
# conf.py

import sys
from pathlib import Path

# Path configured as per
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-extensions
sys.path.append(str(Path('sphinxext').resolve()))

extensions = [
    "myst_parser",
    "extname",
]
# sphinxext/extname.py

from typing import Any

from beartype import beartype
from docutils.nodes import Node, system_message
from docutils.parsers.rst.roles import code_role
from docutils.parsers.rst.states import Inliner


# Typed version of the example from https://docutils.sourceforge.io/docs/howto/rst-roles.html#define-the-role-function
@beartype
def role_fn(
    name: str,
    rawtext: str,
    text: str,
    lineno: int,
    # This is an `Inliner`, so it can be passed through directly to `code_role`.
    inliner: Inliner,
    options: dict[Any, Any] = {},
    content: list[str] = [],
) -> tuple[list[Node], list[system_message]]:
    print("Custom logic")
    # Type stubs for `code_role` are at
    # https://github.com/python/typeshed/blob/57d7c4334b64856fda6f6e8f992b101ddafe2f57/stubs/docutils/docutils/parsers/rst/roles.pyi#L103
    #
    # Importantly, they require that `inliner` is an `Inliner`.
    return code_role(   
        role=name,
        rawtext=rawtext,
        text=text,
        lineno=lineno,
        inliner=inliner,
        options=options,
        content=content,
    )

@beartype
def setup(app):
    app.add_role(name="my-role", role=role_fn)

Command

sphinx-build -M html . build/

Error

beartype.roar.BeartypeCallHintParamViolation: Function extname.role_fn() parameter inliner=<myst_parser.mocking.MockInliner object at 0x10826e120> violates type hint <class 'docutils.parsers.rst.states.Inliner'>, as <class "myst_parser.mocking.MockInliner"> <myst_parser.mocking.MockInliner object at 0x10826e120> not instance of <class "docutils.parsers.rst.states.Inliner">.

Describe the solution you'd like

Modify MockInliner as per https://beartype.readthedocs.io/en/latest/faq/#mock-types to define a new __class__ property which returns Inliner.

Describe alternatives you've considered

My workaround is to have role_fn hint inliner as inliner: Inliner | MockInliner, and then to create a new Inliner object as needed for code_role:

if isinstance(inliner, MockInliner):
    new_inliner = Inliner()
    new_inliner.document = inliner_document
    inliner = new_inliner

Another alternative is to contribute to types-docutils to have code_role take a Protocol of just what is needed from Inliner, so it supports MockInliner, too.

@adamtheturtle adamtheturtle added the enhancement New feature or request label Jan 21, 2025
adamtheturtle added a commit to adamtheturtle/MyST-Parser that referenced this issue Jan 21, 2025
adamtheturtle added a commit to adamtheturtle/sphinx-substitution-extensions that referenced this issue Jan 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant