From 750b08db11ea097776ba4794dce24db2a7aea792 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 15 Sep 2026 18:50:15 +0200 Subject: [PATCH 1/3] Add custom lexers for PEP 823 + 824 --- pep_sphinx_extensions/__init__.py | 5 +++ pep_sphinx_extensions/lexers/__init__.py | 19 ++++++++++ pep_sphinx_extensions/lexers/pep823_lexer.py | 40 ++++++++++++++++++++ pep_sphinx_extensions/lexers/pep824_lexer.py | 21 ++++++++++ 4 files changed, 85 insertions(+) create mode 100644 pep_sphinx_extensions/lexers/__init__.py create mode 100644 pep_sphinx_extensions/lexers/pep823_lexer.py create mode 100644 pep_sphinx_extensions/lexers/pep824_lexer.py diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index 109c09d7890..04ec927a592 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -14,6 +14,7 @@ get_from_doctree, pep_abstract, ) +from pep_sphinx_extensions.lexers import pep_lexers from pep_sphinx_extensions.pep_processor.html import ( pep_html_builder, pep_html_translator, @@ -105,6 +106,10 @@ def setup(app: Sphinx) -> dict[str, bool]: app.add_directive("superseded", pep_banner_directive.SupersededBanner) app.add_directive("withdrawn", pep_banner_directive.WithdrawnBanner) + # Register custom lexers + for lexer in pep_lexers: + app.add_lexer(lexer.name, lexer) + # Register event callbacks app.connect("builder-inited", _update_config_for_builder) # Update configuration values for builder used app.connect("env-before-read-docs", create_pep_zero) # PEP 0 hook diff --git a/pep_sphinx_extensions/lexers/__init__.py b/pep_sphinx_extensions/lexers/__init__.py new file mode 100644 index 00000000000..f5f9eb83ed6 --- /dev/null +++ b/pep_sphinx_extensions/lexers/__init__.py @@ -0,0 +1,19 @@ +# This file is placed in the public domain or under the +# CC0-1.0-Universal license, whichever is more permissive. + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .pep823_lexer import Py823ConsoleLexer, Py823Lexer +from .pep824_lexer import Py824Lexer + +if TYPE_CHECKING: + from pygments.lexer import Lexer + + +pep_lexers: list[type[Lexer]] = [ + Py823ConsoleLexer, + Py823Lexer, + Py824Lexer, +] diff --git a/pep_sphinx_extensions/lexers/pep823_lexer.py b/pep_sphinx_extensions/lexers/pep823_lexer.py new file mode 100644 index 00000000000..f8efb9dd192 --- /dev/null +++ b/pep_sphinx_extensions/lexers/pep823_lexer.py @@ -0,0 +1,40 @@ +# This file is placed in the public domain or under the +# CC0-1.0-Universal license, whichever is more permissive. + +"""Custom lexer for PEP 823.""" + +from pygments.lexer import DelegatingLexer, inherit +from pygments.lexers.python import ( + _PythonConsoleLexerBase, + PythonLexer, + PythonTracebackLexer, +) +from pygments.token import Operator, Other + + +class Py823Lexer(PythonLexer): + name = "py823" + + tokens = { + "expr": [ + (r"maybe\b", Operator.Word), + (r"\?", Operator), + inherit, + ], + } + + +class Py823ConsoleLexer(DelegatingLexer): + name = "py823-console" + + def __init__(self, **options): + pylexer = Py823Lexer + tblexer = PythonTracebackLexer + + class _ReplaceInnerCode(DelegatingLexer): + def __init__(self, **options): + super().__init__( + pylexer, _PythonConsoleLexerBase, Other.Code, **options + ) + + super().__init__(tblexer, _ReplaceInnerCode, Other.Traceback, **options) diff --git a/pep_sphinx_extensions/lexers/pep824_lexer.py b/pep_sphinx_extensions/lexers/pep824_lexer.py new file mode 100644 index 00000000000..2038f44b104 --- /dev/null +++ b/pep_sphinx_extensions/lexers/pep824_lexer.py @@ -0,0 +1,21 @@ +# This file is placed in the public domain or under the +# CC0-1.0-Universal license, whichever is more permissive. + +"""Custom lexer for PEP 824.""" + +from pygments.lexer import inherit +from pygments.lexers.python import PythonLexer +from pygments.token import Operator + + +class Py824Lexer(PythonLexer): + name = "py824" + + tokens = { + "expr": [ + (r"\?\?(?=\s)", Operator.Word), + (r"otherwise\b", Operator.Word), + (r"\?", Operator), + inherit, + ], + } From 2c1bf24e60026ff816bf8610076811ad9bd94416 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 16 Sep 2026 19:14:33 +0200 Subject: [PATCH 2/3] Register lexers automatically --- pep_sphinx_extensions/__init__.py | 4 ++-- pep_sphinx_extensions/lexers/__init__.py | 18 ++++++++++-------- pep_sphinx_extensions/lexers/pep823_lexer.py | 4 ++++ pep_sphinx_extensions/lexers/pep824_lexer.py | 4 ++++ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index 04ec927a592..3fc9c2d56ce 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -14,7 +14,7 @@ get_from_doctree, pep_abstract, ) -from pep_sphinx_extensions.lexers import pep_lexers +from pep_sphinx_extensions.lexers import get_custom_lexers from pep_sphinx_extensions.pep_processor.html import ( pep_html_builder, pep_html_translator, @@ -107,7 +107,7 @@ def setup(app: Sphinx) -> dict[str, bool]: app.add_directive("withdrawn", pep_banner_directive.WithdrawnBanner) # Register custom lexers - for lexer in pep_lexers: + for lexer in get_custom_lexers(): app.add_lexer(lexer.name, lexer) # Register event callbacks diff --git a/pep_sphinx_extensions/lexers/__init__.py b/pep_sphinx_extensions/lexers/__init__.py index f5f9eb83ed6..e76f0d26339 100644 --- a/pep_sphinx_extensions/lexers/__init__.py +++ b/pep_sphinx_extensions/lexers/__init__.py @@ -3,17 +3,19 @@ from __future__ import annotations +import importlib +import pkgutil from typing import TYPE_CHECKING -from .pep823_lexer import Py823ConsoleLexer, Py823Lexer -from .pep824_lexer import Py824Lexer - if TYPE_CHECKING: from pygments.lexer import Lexer -pep_lexers: list[type[Lexer]] = [ - Py823ConsoleLexer, - Py823Lexer, - Py824Lexer, -] +def get_custom_lexers() -> list[type[Lexer]]: + lexers: list[type[Lexer]] = [] + for module_info in pkgutil.walk_packages(__path__, prefix=f"{__name__}."): + module = importlib.import_module(module_info.name) + if (register_func := getattr(module, "register", None)) is None: + continue + lexers.extend(register_func()) + return lexers diff --git a/pep_sphinx_extensions/lexers/pep823_lexer.py b/pep_sphinx_extensions/lexers/pep823_lexer.py index f8efb9dd192..3ed2f1990a0 100644 --- a/pep_sphinx_extensions/lexers/pep823_lexer.py +++ b/pep_sphinx_extensions/lexers/pep823_lexer.py @@ -38,3 +38,7 @@ def __init__(self, **options): ) super().__init__(tblexer, _ReplaceInnerCode, Other.Traceback, **options) + + +def register(): + return [Py823Lexer, Py823ConsoleLexer] diff --git a/pep_sphinx_extensions/lexers/pep824_lexer.py b/pep_sphinx_extensions/lexers/pep824_lexer.py index 2038f44b104..afef3caaecf 100644 --- a/pep_sphinx_extensions/lexers/pep824_lexer.py +++ b/pep_sphinx_extensions/lexers/pep824_lexer.py @@ -19,3 +19,7 @@ class Py824Lexer(PythonLexer): inherit, ], } + + +def register(): + return [Py824Lexer] From 03a8225f2c9e7df89333232bb164146195f5c4de Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 16 Sep 2026 19:31:26 +0200 Subject: [PATCH 3/3] Fix ruff --- pep_sphinx_extensions/lexers/pep823_lexer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep_sphinx_extensions/lexers/pep823_lexer.py b/pep_sphinx_extensions/lexers/pep823_lexer.py index 3ed2f1990a0..3be76b8ede7 100644 --- a/pep_sphinx_extensions/lexers/pep823_lexer.py +++ b/pep_sphinx_extensions/lexers/pep823_lexer.py @@ -5,9 +5,9 @@ from pygments.lexer import DelegatingLexer, inherit from pygments.lexers.python import ( - _PythonConsoleLexerBase, PythonLexer, PythonTracebackLexer, + _PythonConsoleLexerBase, ) from pygments.token import Operator, Other