From 418d046a5cabcbf0e572e6236d018ced7a531e19 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:26:28 +0200 Subject: [PATCH] PEP 813: Add custom lexer --- pep_sphinx_extensions/lexers/pep813_lexer.py | 50 ++++++++++++++++++++ peps/pep-0813.rst | 30 ++++-------- 2 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 pep_sphinx_extensions/lexers/pep813_lexer.py diff --git a/pep_sphinx_extensions/lexers/pep813_lexer.py b/pep_sphinx_extensions/lexers/pep813_lexer.py new file mode 100644 index 00000000000..0a4bf2859ed --- /dev/null +++ b/pep_sphinx_extensions/lexers/pep813_lexer.py @@ -0,0 +1,50 @@ +# This file is placed in the public domain or under the +# CC0-1.0-Universal license, whichever is more permissive. + +"""Custom lexer for PEP 813.""" + +# This lexer is most useful while PEP 813 is being discussed. +# If it breaks in the future, it can be safely replaced by plain +# Python or text lexers. + +from pygments.lexer import DelegatingLexer, inherit +from pygments.lexers.python import ( + PythonLexer, + PythonTracebackLexer, + _PythonConsoleLexerBase, +) +from pygments.token import Other, String + + +class Py813Lexer(PythonLexer): + name = "py813" + + tokens = { + "expr-inside-fstring": [ + # The ``!p`` conversion, without a format specifier + (r"(=\s*)?(\!p)\}", String.Interpol, "#pop"), + # The ``!p`` conversion, with a format specifier + (r"(=\s*)?(\!p):", String.Interpol, "#pop"), + inherit, + ], + } + + +class Py813ConsoleLexer(DelegatingLexer): + name = "py813-console" + + def __init__(self, **options): + pylexer = Py813Lexer + tblexer = PythonTracebackLexer + + class _ReplaceInnerCode(DelegatingLexer): + def __init__(self, **options): + super().__init__( + pylexer, _PythonConsoleLexerBase, Other.Code, **options + ) + + super().__init__(tblexer, _ReplaceInnerCode, Other.Traceback, **options) + + +def register(): + return [Py813Lexer, Py813ConsoleLexer] diff --git a/peps/pep-0813.rst b/peps/pep-0813.rst index 74eee19bc12..5d115de6d92 100644 --- a/peps/pep-0813.rst +++ b/peps/pep-0813.rst @@ -476,8 +476,7 @@ processes the template decides what to do with it: For example: -.. code-block:: pycon - :force: +.. code-block:: py813-console >>> from string.templatelib import convert >>> basses = ['precision', 'jazz', 'stingray'] @@ -646,8 +645,7 @@ class: Now let's create a couple of instances and pretty print them: -.. code-block:: pycon - :force: +.. code-block:: py813-console >>> precision = Bass(4, 'split coil P', active=False) >>> stingray = Bass(5, 'humbucker', active=True) @@ -662,8 +660,7 @@ Now let's create a couple of instances and pretty print them: For more complex objects, ``!p`` can help make debugging output more readable: -.. code-block:: pycon - :force: +.. code-block:: py813-console >>> import os >>> print(os.pathconf_names) @@ -696,8 +693,7 @@ For more complex objects, ``!p`` can help make debugging output more readable: In f-strings, the ``!p`` conversion specifier also accepts a printer, such as a ``pprint.Printer`` with its own options: -.. code-block:: pycon - :force: +.. code-block:: py813-console >>> narrow = pprint.Printer(width=30) >>> print(f'{stingray!p:{narrow}}') @@ -715,8 +711,7 @@ own options: A printer can also be a plain function that takes the object and returns its lines: -.. code-block:: pycon - :force: +.. code-block:: py813-console >>> def slappa(da: Bass) -> Iterator[str]: ... yield 'All about that bass' @@ -726,8 +721,7 @@ A printer can also be a plain function that takes the object and returns its lin Since tuples are read according to the current section, a positional tuple value needs nothing special: -.. code-block:: pycon - :force: +.. code-block:: py813-console >>> class Things: ... def __pprint__(self): @@ -753,8 +747,7 @@ Objects with a ``__pprint__()`` method wrap, however deeply they are nested: yield pprint.KWARGS yield 'children', self.children, [] -.. code-block:: pycon - :force: +.. code-block:: py813-console >>> tree = Node('root', [Node('left', [Node('leaf')]), Node('right')]) >>> print(f'{tree!p:{pprint.Printer(width=60)}}') @@ -781,8 +774,7 @@ A ``Layout`` changes the brackets. This class prints in the angular form that R yield 'pitch', self.pitch yield 'beats', self.beats, 1 -.. code-block:: pycon - :force: +.. code-block:: py813-console >>> print(f"{Note('E', beats=2)!p}") @@ -808,8 +800,7 @@ for each row: The default printer ignores the hint, so its output still agrees with ``repr()``: -.. code-block:: pycon - :force: +.. code-block:: py813-console >>> print(f"{Table(['Bass', 'Strings'], [['Precision', 4], ['Stingray', 5]])!p}") Table(headers=['Bass', 'Strings'], rows=[['Precision', 4], ['Stingray', 5]]) @@ -840,8 +831,7 @@ value it wraps degrades gracefully when it reaches a printer that doesn't know a yield pprint.DELEGATE yield self.value -.. code-block:: pycon - :force: +.. code-block:: py813-console >>> notes = [Note('E'), Emphasis(Note('A')), Note('D')] >>> print(f'{notes!p}')