1from __future__ import annotations 2import contextlib 3import functools 4import io 5from types import NoneType 6from typing import Any, Protocol, TYPE_CHECKING 7 8from libclinic import unspecified 9from libclinic.block_parser import Block 10from libclinic.converter import CConverter, converters 11from libclinic.converters import buffer, robuffer, rwbuffer 12from libclinic.return_converters import CReturnConverter, return_converters 13if TYPE_CHECKING: 14 from libclinic.app import Clinic 15 16 17class Parser(Protocol): 18 def __init__(self, clinic: Clinic) -> None: ... 19 def parse(self, block: Block) -> None: ... 20 21 22@functools.cache 23def _create_parser_base_namespace() -> dict[str, Any]: 24 ns = dict( 25 CConverter=CConverter, 26 CReturnConverter=CReturnConverter, 27 buffer=buffer, 28 robuffer=robuffer, 29 rwbuffer=rwbuffer, 30 unspecified=unspecified, 31 NoneType=NoneType, 32 ) 33 for name, converter in converters.items(): 34 ns[f'{name}_converter'] = converter 35 for name, return_converter in return_converters.items(): 36 ns[f'{name}_return_converter'] = return_converter 37 return ns 38 39 40def create_parser_namespace() -> dict[str, Any]: 41 base_namespace = _create_parser_base_namespace() 42 return base_namespace.copy() 43 44 45class PythonParser: 46 def __init__(self, clinic: Clinic) -> None: 47 pass 48 49 def parse(self, block: Block) -> None: 50 namespace = create_parser_namespace() 51 with contextlib.redirect_stdout(io.StringIO()) as s: 52 exec(block.input, namespace) 53 block.output = s.getvalue() 54