Path: blob/main/Tools/peg_generator/pegen/testutil.py
12 views
import importlib.util1import io2import os3import pathlib4import sys5import textwrap6import token7import tokenize8from typing import IO, Any, Dict, Final, Optional, Type, cast910from pegen.build import compile_c_extension11from pegen.c_generator import CParserGenerator12from pegen.grammar import Grammar13from pegen.grammar_parser import GeneratedParser as GrammarParser14from pegen.parser import Parser15from pegen.python_generator import PythonParserGenerator16from pegen.tokenizer import Tokenizer1718ALL_TOKENS = token.tok_name19EXACT_TOKENS = token.EXACT_TOKEN_TYPES20NON_EXACT_TOKENS = {21name for index, name in token.tok_name.items() if index not in EXACT_TOKENS.values()22}232425def generate_parser(grammar: Grammar) -> Type[Parser]:26# Generate a parser.27out = io.StringIO()28genr = PythonParserGenerator(grammar, out)29genr.generate("<string>")3031# Load the generated parser class.32ns: Dict[str, Any] = {}33exec(out.getvalue(), ns)34return ns["GeneratedParser"]353637def run_parser(file: IO[bytes], parser_class: Type[Parser], *, verbose: bool = False) -> Any:38# Run a parser on a file (stream).39tokenizer = Tokenizer(tokenize.generate_tokens(file.readline)) # type: ignore # typeshed issue #351540parser = parser_class(tokenizer, verbose=verbose)41result = parser.start()42if result is None:43raise parser.make_syntax_error("invalid syntax")44return result454647def parse_string(48source: str, parser_class: Type[Parser], *, dedent: bool = True, verbose: bool = False49) -> Any:50# Run the parser on a string.51if dedent:52source = textwrap.dedent(source)53file = io.StringIO(source)54return run_parser(file, parser_class, verbose=verbose) # type: ignore # typeshed issue #3515555657def make_parser(source: str) -> Type[Parser]:58# Combine parse_string() and generate_parser().59grammar = parse_string(source, GrammarParser)60return generate_parser(grammar)616263def import_file(full_name: str, path: str) -> Any:64"""Import a python module from a path"""6566spec = importlib.util.spec_from_file_location(full_name, path)67assert spec is not None68mod = importlib.util.module_from_spec(spec)6970# We assume this is not None and has an exec_module() method.71# See https://docs.python.org/3/reference/import.html?highlight=exec_module#loading72loader = cast(Any, spec.loader)73loader.exec_module(mod)74return mod757677def generate_c_parser_source(grammar: Grammar) -> str:78out = io.StringIO()79genr = CParserGenerator(grammar, ALL_TOKENS, EXACT_TOKENS, NON_EXACT_TOKENS, out)80genr.generate("<string>")81return out.getvalue()828384def generate_parser_c_extension(85grammar: Grammar,86path: pathlib.PurePath,87debug: bool = False,88library_dir: Optional[str] = None,89) -> Any:90"""Generate a parser c extension for the given grammar in the given path9192Returns a module object with a parse_string() method.93TODO: express that using a Protocol.94"""95# Make sure that the working directory is empty: reusing non-empty temporary96# directories when generating extensions can lead to segmentation faults.97# Check issue #95 (https://github.com/gvanrossum/pegen/issues/95) for more98# context.99assert not os.listdir(path)100source = path / "parse.c"101with open(source, "w", encoding="utf-8") as file:102genr = CParserGenerator(103grammar, ALL_TOKENS, EXACT_TOKENS, NON_EXACT_TOKENS, file, debug=debug104)105genr.generate("parse.c")106compile_c_extension(107str(source),108build_dir=str(path),109# Significant test_peg_generator speedups110disable_optimization=True,111library_dir=library_dir,112)113114115def print_memstats() -> bool:116MiB: Final = 2**20117try:118import psutil # type: ignore119except ImportError:120return False121print("Memory stats:")122process = psutil.Process()123meminfo = process.memory_info()124res = {}125res["rss"] = meminfo.rss / MiB126res["vms"] = meminfo.vms / MiB127if sys.platform == "win32":128res["maxrss"] = meminfo.peak_wset / MiB129else:130# See https://stackoverflow.com/questions/938733/total-memory-used-by-python-process131import resource # Since it doesn't exist on Windows.132133rusage = resource.getrusage(resource.RUSAGE_SELF)134if sys.platform == "darwin":135factor = 1136else:137factor = 1024 # Linux138res["maxrss"] = rusage.ru_maxrss * factor / MiB139for key, value in res.items():140print(f" {key:12.12s}: {value:10.0f} MiB")141return True142143144