From 68f41af8b945d0250276f5b6e6d9b544dca87c81 Mon Sep 17 00:00:00 2001
From: serge-sans-paille <serge.guelton@telecom-bretagne.eu>
Date: Mon, 2 Nov 2020 14:08:03 +0100
Subject: [PATCH] Disable peg generator test under PGO

---
 Lib/test/test_peg_generator/test_c_parser.py |  4 ++++
 Tools/peg_generator/pegen/testutil.py        | 21 ++++++++++++++++++++-
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/Lib/test/test_peg_generator/test_c_parser.py b/Lib/test/test_peg_generator/test_c_parser.py
index f993525..5ca92f7 100644
--- a/Lib/test/test_peg_generator/test_c_parser.py
+++ b/Lib/test/test_peg_generator/test_c_parser.py
@@ -1,446 +1,450 @@
+import sysconfig
 import textwrap
 import unittest
 from distutils.tests.support import TempdirManager
 from pathlib import Path
 
 from test import test_tools
 from test import support
 from test.support.script_helper import assert_python_ok
 
+if sysconfig.get_config_var('PGO_PROF_USE_FLAG') in sysconfig.get_config_var('PY_CFLAGS_NODIST'):
+    raise unittest.SkipTest("peg_generator test disabled when building with PGO")
+
 test_tools.skip_if_missing("peg_generator")
 with test_tools.imports_under_tool("peg_generator"):
     from pegen.grammar_parser import GeneratedParser as GrammarParser
     from pegen.testutil import (
         parse_string,
         generate_parser_c_extension,
         generate_c_parser_source,
     )
     from pegen.ast_dump import ast_dump
 
 
 TEST_TEMPLATE = """
 tmp_dir = {extension_path!r}
 
 import ast
 import traceback
 import sys
 import unittest
 
 from test import test_tools
 with test_tools.imports_under_tool("peg_generator"):
     from pegen.ast_dump import ast_dump
 
 sys.path.insert(0, tmp_dir)
 import parse
 
 class Tests(unittest.TestCase):
 
     def check_input_strings_for_grammar(
         self,
         valid_cases = (),
         invalid_cases = (),
     ):
         if valid_cases:
             for case in valid_cases:
                 parse.parse_string(case, mode=0)
 
         if invalid_cases:
             for case in invalid_cases:
                 with self.assertRaises(SyntaxError):
                     parse.parse_string(case, mode=0)
 
     def verify_ast_generation(self, stmt):
         expected_ast = ast.parse(stmt)
         actual_ast = parse.parse_string(stmt, mode=1)
         self.assertEqual(ast_dump(expected_ast), ast_dump(actual_ast))
 
     def test_parse(self):
         {test_source}
 
 unittest.main()
 """
 
 
 class TestCParser(TempdirManager, unittest.TestCase):
     def setUp(self):
         cmd = support.missing_compiler_executable()
         if cmd is not None:
             self.skipTest("The %r command is not found" % cmd)
         super(TestCParser, self).setUp()
         self.tmp_path = self.mkdtemp()
         change_cwd = support.change_cwd(self.tmp_path)
         change_cwd.__enter__()
         self.addCleanup(change_cwd.__exit__, None, None, None)
 
     def tearDown(self):
         super(TestCParser, self).tearDown()
 
     def build_extension(self, grammar_source):
         grammar = parse_string(grammar_source, GrammarParser)
         generate_parser_c_extension(grammar, Path(self.tmp_path))
 
     def run_test(self, grammar_source, test_source):
         self.build_extension(grammar_source)
         test_source = textwrap.indent(textwrap.dedent(test_source), 8 * " ")
         assert_python_ok(
             "-c",
             TEST_TEMPLATE.format(extension_path=self.tmp_path, test_source=test_source),
         )
 
     def test_c_parser(self) -> None:
         grammar_source = """
         start[mod_ty]: a=stmt* $ { Module(a, NULL, p->arena) }
         stmt[stmt_ty]: a=expr_stmt { a }
         expr_stmt[stmt_ty]: a=expression NEWLINE { _Py_Expr(a, EXTRA) }
         expression[expr_ty]: ( l=expression '+' r=term { _Py_BinOp(l, Add, r, EXTRA) }
                             | l=expression '-' r=term { _Py_BinOp(l, Sub, r, EXTRA) }
                             | t=term { t }
                             )
         term[expr_ty]: ( l=term '*' r=factor { _Py_BinOp(l, Mult, r, EXTRA) }
                     | l=term '/' r=factor { _Py_BinOp(l, Div, r, EXTRA) }
                     | f=factor { f }
                     )
         factor[expr_ty]: ('(' e=expression ')' { e }
                         | a=atom { a }
                         )
         atom[expr_ty]: ( n=NAME { n }
                     | n=NUMBER { n }
                     | s=STRING { s }
                     )
         """
         test_source = """
         expressions = [
             "4+5",
             "4-5",
             "4*5",
             "1+4*5",
             "1+4/5",
             "(1+1) + (1+1)",
             "(1+1) - (1+1)",
             "(1+1) * (1+1)",
             "(1+1) / (1+1)",
         ]
 
         for expr in expressions:
             the_ast = parse.parse_string(expr, mode=1)
             expected_ast = ast.parse(expr)
             self.assertEqual(ast_dump(the_ast), ast_dump(expected_ast))
         """
         self.run_test(grammar_source, test_source)
 
     def test_lookahead(self) -> None:
         grammar_source = """
         start: NAME &NAME expr NEWLINE? ENDMARKER
         expr: NAME | NUMBER
         """
         test_source = """
         valid_cases = ["foo bar"]
         invalid_cases = ["foo 34"]
         self.check_input_strings_for_grammar(valid_cases, invalid_cases)
         """
         self.run_test(grammar_source, test_source)
 
     def test_negative_lookahead(self) -> None:
         grammar_source = """
         start: NAME !NAME expr NEWLINE? ENDMARKER
         expr: NAME | NUMBER
         """
         test_source = """
         valid_cases = ["foo 34"]
         invalid_cases = ["foo bar"]
         self.check_input_strings_for_grammar(valid_cases, invalid_cases)
         """
         self.run_test(grammar_source, test_source)
 
     def test_cut(self) -> None:
         grammar_source = """
         start: X ~ Y Z | X Q S
         X: 'x'
         Y: 'y'
         Z: 'z'
         Q: 'q'
         S: 's'
         """
         test_source = """
         valid_cases = ["x y z"]
         invalid_cases = ["x q s"]
         self.check_input_strings_for_grammar(valid_cases, invalid_cases)
         """
         self.run_test(grammar_source, test_source)
 
     def test_gather(self) -> None:
         grammar_source = """
         start: ';'.pass_stmt+ NEWLINE
         pass_stmt: 'pass'
         """
         test_source = """
         valid_cases = ["pass", "pass; pass"]
         invalid_cases = ["pass;", "pass; pass;"]
         self.check_input_strings_for_grammar(valid_cases, invalid_cases)
         """
         self.run_test(grammar_source, test_source)
 
     def test_left_recursion(self) -> None:
         grammar_source = """
         start: expr NEWLINE
         expr: ('-' term | expr '+' term | term)
         term: NUMBER
         """
         test_source = """
         valid_cases = ["-34", "34", "34 + 12", "1 + 1 + 2 + 3"]
         self.check_input_strings_for_grammar(valid_cases)
         """
         self.run_test(grammar_source, test_source)
 
     def test_advanced_left_recursive(self) -> None:
         grammar_source = """
         start: NUMBER | sign start
         sign: ['-']
         """
         test_source = """
         valid_cases = ["23", "-34"]
         self.check_input_strings_for_grammar(valid_cases)
         """
         self.run_test(grammar_source, test_source)
 
     def test_mutually_left_recursive(self) -> None:
         grammar_source = """
         start: foo 'E'
         foo: bar 'A' | 'B'
         bar: foo 'C' | 'D'
         """
         test_source = """
         valid_cases = ["B E", "D A C A E"]
         self.check_input_strings_for_grammar(valid_cases)
         """
         self.run_test(grammar_source, test_source)
 
     def test_nasty_mutually_left_recursive(self) -> None:
         grammar_source = """
         start: target '='
         target: maybe '+' | NAME
         maybe: maybe '-' | target
         """
         test_source = """
         valid_cases = ["x ="]
         invalid_cases = ["x - + ="]
         self.check_input_strings_for_grammar(valid_cases, invalid_cases)
         """
         self.run_test(grammar_source, test_source)
 
     def test_return_stmt_noexpr_action(self) -> None:
         grammar_source = """
         start[mod_ty]: a=[statements] ENDMARKER { Module(a, NULL, p->arena) }
         statements[asdl_seq*]: a=statement+ { a }
         statement[stmt_ty]: simple_stmt
         simple_stmt[stmt_ty]: small_stmt
         small_stmt[stmt_ty]: return_stmt
         return_stmt[stmt_ty]: a='return' NEWLINE { _Py_Return(NULL, EXTRA) }
         """
         test_source = """
         stmt = "return"
         self.verify_ast_generation(stmt)
         """
         self.run_test(grammar_source, test_source)
 
     def test_gather_action_ast(self) -> None:
         grammar_source = """
         start[mod_ty]: a=';'.pass_stmt+ NEWLINE ENDMARKER { Module(a, NULL, p->arena) }
         pass_stmt[stmt_ty]: a='pass' { _Py_Pass(EXTRA)}
         """
         test_source = """
         stmt = "pass; pass"
         self.verify_ast_generation(stmt)
         """
         self.run_test(grammar_source, test_source)
 
     def test_pass_stmt_action(self) -> None:
         grammar_source = """
         start[mod_ty]: a=[statements] ENDMARKER { Module(a, NULL, p->arena) }
         statements[asdl_seq*]: a=statement+ { a }
         statement[stmt_ty]: simple_stmt
         simple_stmt[stmt_ty]: small_stmt
         small_stmt[stmt_ty]: pass_stmt
         pass_stmt[stmt_ty]: a='pass' NEWLINE { _Py_Pass(EXTRA) }
         """
         test_source = """
         stmt = "pass"
         self.verify_ast_generation(stmt)
         """
         self.run_test(grammar_source, test_source)
 
     def test_if_stmt_action(self) -> None:
         grammar_source = """
         start[mod_ty]: a=[statements] ENDMARKER { Module(a, NULL, p->arena) }
         statements[asdl_seq*]: a=statement+ { _PyPegen_seq_flatten(p, a) }
         statement[asdl_seq*]:  a=compound_stmt { _PyPegen_singleton_seq(p, a) } | simple_stmt
 
         simple_stmt[asdl_seq*]: a=small_stmt b=further_small_stmt* [';'] NEWLINE { _PyPegen_seq_insert_in_front(p, a, b) }
         further_small_stmt[stmt_ty]: ';' a=small_stmt { a }
 
         block: simple_stmt | NEWLINE INDENT a=statements DEDENT { a }
 
         compound_stmt: if_stmt
 
         if_stmt: 'if' a=full_expression ':' b=block { _Py_If(a, b, NULL, EXTRA) }
 
         small_stmt[stmt_ty]: pass_stmt
 
         pass_stmt[stmt_ty]: a='pass' { _Py_Pass(EXTRA) }
 
         full_expression: NAME
         """
         test_source = """
         stmt = "pass"
         self.verify_ast_generation(stmt)
         """
         self.run_test(grammar_source, test_source)
 
     def test_same_name_different_types(self) -> None:
         grammar_source = """
         start[mod_ty]: a=import_from+ NEWLINE ENDMARKER { Module(a, NULL, p->arena)}
         import_from[stmt_ty]: ( a='from' !'import' c=simple_name 'import' d=import_as_names_from {
                                 _Py_ImportFrom(c->v.Name.id, d, 0, EXTRA) }
                             | a='from' '.' 'import' c=import_as_names_from {
                                 _Py_ImportFrom(NULL, c, 1, EXTRA) }
                             )
         simple_name[expr_ty]: NAME
         import_as_names_from[asdl_seq*]: a=','.import_as_name_from+ { a }
         import_as_name_from[alias_ty]: a=NAME 'as' b=NAME { _Py_alias(((expr_ty) a)->v.Name.id, ((expr_ty) b)->v.Name.id, p->arena) }
         """
         test_source = """
         for stmt in ("from a import b as c", "from . import a as b"):
             expected_ast = ast.parse(stmt)
             actual_ast = parse.parse_string(stmt, mode=1)
             self.assertEqual(ast_dump(expected_ast), ast_dump(actual_ast))
         """
         self.run_test(grammar_source, test_source)
 
     def test_with_stmt_with_paren(self) -> None:
         grammar_source = """
         start[mod_ty]: a=[statements] ENDMARKER { Module(a, NULL, p->arena) }
         statements[asdl_seq*]: a=statement+ { _PyPegen_seq_flatten(p, a) }
         statement[asdl_seq*]: a=compound_stmt { _PyPegen_singleton_seq(p, a) }
         compound_stmt[stmt_ty]: with_stmt
         with_stmt[stmt_ty]: (
             a='with' '(' b=','.with_item+ ')' ':' c=block {
                 _Py_With(b, _PyPegen_singleton_seq(p, c), NULL, EXTRA) }
         )
         with_item[withitem_ty]: (
             e=NAME o=['as' t=NAME { t }] { _Py_withitem(e, _PyPegen_set_expr_context(p, o, Store), p->arena) }
         )
         block[stmt_ty]: a=pass_stmt NEWLINE { a } | NEWLINE INDENT a=pass_stmt DEDENT { a }
         pass_stmt[stmt_ty]: a='pass' { _Py_Pass(EXTRA) }
         """
         test_source = """
         stmt = "with (\\n    a as b,\\n    c as d\\n): pass"
         the_ast = parse.parse_string(stmt, mode=1)
         self.assertTrue(ast_dump(the_ast).startswith(
             "Module(body=[With(items=[withitem(context_expr=Name(id='a', ctx=Load()), optional_vars=Name(id='b', ctx=Store())), "
             "withitem(context_expr=Name(id='c', ctx=Load()), optional_vars=Name(id='d', ctx=Store()))]"
         ))
         """
         self.run_test(grammar_source, test_source)
 
     def test_ternary_operator(self) -> None:
         grammar_source = """
         start[mod_ty]: a=expr ENDMARKER { Module(a, NULL, p->arena) }
         expr[asdl_seq*]: a=listcomp NEWLINE { _PyPegen_singleton_seq(p, _Py_Expr(a, EXTRA)) }
         listcomp[expr_ty]: (
             a='[' b=NAME c=for_if_clauses d=']' { _Py_ListComp(b, c, EXTRA) }
         )
         for_if_clauses[asdl_seq*]: (
             a=(y=[ASYNC] 'for' a=NAME 'in' b=NAME c=('if' z=NAME { z })*
                 { _Py_comprehension(_Py_Name(((expr_ty) a)->v.Name.id, Store, EXTRA), b, c, (y == NULL) ? 0 : 1, p->arena) })+ { a }
         )
         """
         test_source = """
         stmt = "[i for i in a if b]"
         self.verify_ast_generation(stmt)
         """
         self.run_test(grammar_source, test_source)
 
     def test_syntax_error_for_string(self) -> None:
         grammar_source = """
         start: expr+ NEWLINE? ENDMARKER
         expr: NAME
         """
         test_source = r"""
         for text in ("a b 42 b a", "\u540d \u540d 42 \u540d \u540d"):
             try:
                 parse.parse_string(text, mode=0)
             except SyntaxError as e:
                 tb = traceback.format_exc()
             self.assertTrue('File "<string>", line 1' in tb)
             self.assertTrue(f"SyntaxError: invalid syntax" in tb)
         """
         self.run_test(grammar_source, test_source)
 
     def test_headers_and_trailer(self) -> None:
         grammar_source = """
         @header 'SOME HEADER'
         @subheader 'SOME SUBHEADER'
         @trailer 'SOME TRAILER'
         start: expr+ NEWLINE? ENDMARKER
         expr: x=NAME
         """
         grammar = parse_string(grammar_source, GrammarParser)
         parser_source = generate_c_parser_source(grammar)
 
         self.assertTrue("SOME HEADER" in parser_source)
         self.assertTrue("SOME SUBHEADER" in parser_source)
         self.assertTrue("SOME TRAILER" in parser_source)
 
     def test_error_in_rules(self) -> None:
         grammar_source = """
         start: expr+ NEWLINE? ENDMARKER
         expr: NAME {PyTuple_New(-1)}
         """
         # PyTuple_New raises SystemError if an invalid argument was passed.
         test_source = """
         with self.assertRaises(SystemError):
             parse.parse_string("a", mode=0)
         """
         self.run_test(grammar_source, test_source)
 
     def test_no_soft_keywords(self) -> None:
         grammar_source = """
         start: expr+ NEWLINE? ENDMARKER
         expr: 'foo'
         """
         grammar = parse_string(grammar_source, GrammarParser)
         parser_source = generate_c_parser_source(grammar)
         assert "expect_soft_keyword" not in parser_source
 
     def test_soft_keywords(self) -> None:
         grammar_source = """
         start: expr+ NEWLINE? ENDMARKER
         expr: "foo"
         """
         grammar = parse_string(grammar_source, GrammarParser)
         parser_source = generate_c_parser_source(grammar)
         assert "expect_soft_keyword" in parser_source
 
     def test_soft_keywords_parse(self) -> None:
         grammar_source = """
         start: "if" expr '+' expr NEWLINE
         expr: NAME
         """
         test_source = """
         valid_cases = ["if if + if"]
         invalid_cases = ["if if"]
         self.check_input_strings_for_grammar(valid_cases, invalid_cases)
         """
         self.run_test(grammar_source, test_source)
 
     def test_soft_keywords_lookahead(self) -> None:
         grammar_source = """
         start: &"if" "if" expr '+' expr NEWLINE
         expr: NAME
         """
         test_source = """
         valid_cases = ["if if + if"]
         invalid_cases = ["if if"]
         self.check_input_strings_for_grammar(valid_cases, invalid_cases)
         """
         self.run_test(grammar_source, test_source)
diff --git a/Tools/peg_generator/pegen/testutil.py b/Tools/peg_generator/pegen/testutil.py
index 920d246..e898a9b 100644
--- a/Tools/peg_generator/pegen/testutil.py
+++ b/Tools/peg_generator/pegen/testutil.py
@@ -1,133 +1,152 @@
 import importlib.util
 import io
 import os
 import pathlib
 import sys
 import textwrap
 import tokenize
 import token
 
 from typing import Any, cast, Dict, IO, Type, Final
 
 from pegen.build import compile_c_extension
 from pegen.c_generator import CParserGenerator
 from pegen.grammar import Grammar
 from pegen.grammar_parser import GeneratedParser as GrammarParser
 from pegen.parser import Parser
 from pegen.python_generator import PythonParserGenerator
 from pegen.tokenizer import Tokenizer
 
 ALL_TOKENS = token.tok_name
 EXACT_TOKENS = token.EXACT_TOKEN_TYPES  # type: ignore
 NON_EXACT_TOKENS = {
     name for index, name in token.tok_name.items() if index not in EXACT_TOKENS.values()
 }
 
 
 def generate_parser(grammar: Grammar) -> Type[Parser]:
     # Generate a parser.
     out = io.StringIO()
     genr = PythonParserGenerator(grammar, out)
     genr.generate("<string>")
 
     # Load the generated parser class.
     ns: Dict[str, Any] = {}
     exec(out.getvalue(), ns)
     return ns["GeneratedParser"]
 
 
 def run_parser(file: IO[bytes], parser_class: Type[Parser], *, verbose: bool = False) -> Any:
     # Run a parser on a file (stream).
     tokenizer = Tokenizer(tokenize.generate_tokens(file.readline))  # type: ignore # typeshed issue #3515
     parser = parser_class(tokenizer, verbose=verbose)
     result = parser.start()
     if result is None:
         raise parser.make_syntax_error()
     return result
 
 
 def parse_string(
     source: str, parser_class: Type[Parser], *, dedent: bool = True, verbose: bool = False
 ) -> Any:
     # Run the parser on a string.
     if dedent:
         source = textwrap.dedent(source)
     file = io.StringIO(source)
     return run_parser(file, parser_class, verbose=verbose)  # type: ignore # typeshed issue #3515
 
 
 def make_parser(source: str) -> Type[Parser]:
     # Combine parse_string() and generate_parser().
     grammar = parse_string(source, GrammarParser)
     return generate_parser(grammar)
 
 
 def import_file(full_name: str, path: str) -> Any:
     """Import a python module from a path"""
 
     spec = importlib.util.spec_from_file_location(full_name, path)
     mod = importlib.util.module_from_spec(spec)
 
     # We assume this is not None and has an exec_module() method.
     # See https://docs.python.org/3/reference/import.html?highlight=exec_module#loading
     loader = cast(Any, spec.loader)
     loader.exec_module(mod)
     return mod
 
 
 def generate_c_parser_source(grammar: Grammar) -> str:
     out = io.StringIO()
     genr = CParserGenerator(grammar, ALL_TOKENS, EXACT_TOKENS, NON_EXACT_TOKENS, out)
     genr.generate("<string>")
     return out.getvalue()
 
 
 def generate_parser_c_extension(
     grammar: Grammar, path: pathlib.PurePath, debug: bool = False
 ) -> Any:
     """Generate a parser c extension for the given grammar in the given path
 
     Returns a module object with a parse_string() method.
     TODO: express that using a Protocol.
     """
     # Make sure that the working directory is empty: reusing non-empty temporary
     # directories when generating extensions can lead to segmentation faults.
     # Check issue #95 (https://github.com/gvanrossum/pegen/issues/95) for more
     # context.
     assert not os.listdir(path)
     source = path / "parse.c"
     with open(source, "w", encoding="utf-8") as file:
         genr = CParserGenerator(
             grammar, ALL_TOKENS, EXACT_TOKENS, NON_EXACT_TOKENS, file, debug=debug
         )
         genr.generate("parse.c")
-    compile_c_extension(str(source), build_dir=str(path))
+
+    # Monkey patch CFLAGS to remove profiling flags; These files are not
+    # especially relevant for testing, 
+    import pegen.build
+    get_extra_flags = pegen.build.get_extra_flags
+    def custom_get_extra_flags(compiler_flags, compiler_py_flags_nodist):
+        extra_flags = get_extra_flags(compiler_flags, compiler_py_flags_nodist)
+        if compiler_py_flags_nodist == 'PY_CFLAGS_NODIST':
+            import sysconfig
+            try:
+                extra_flags.remove(sysconfig.get_config_var('PGO_PROF_USE_FLAG'))
+            except ValueError:
+                pass
+        return extra_flags
+
+    pegen.build.get_extra_flags = custom_get_extra_flags
+    try:
+        compile_c_extension(str(source), build_dir=str(path))
+    finally:
+        pegen.build.get_extra_flags = get_extra_flags
 
 
 def print_memstats() -> bool:
     MiB: Final = 2 ** 20
     try:
         import psutil  # type: ignore
     except ImportError:
         return False
     print("Memory stats:")
     process = psutil.Process()
     meminfo = process.memory_info()
     res = {}
     res["rss"] = meminfo.rss / MiB
     res["vms"] = meminfo.vms / MiB
     if sys.platform == "win32":
         res["maxrss"] = meminfo.peak_wset / MiB
     else:
         # See https://stackoverflow.com/questions/938733/total-memory-used-by-python-process
         import resource  # Since it doesn't exist on Windows.
 
         rusage = resource.getrusage(resource.RUSAGE_SELF)
         if sys.platform == "darwin":
             factor = 1
         else:
             factor = 1024  # Linux
         res["maxrss"] = rusage.ru_maxrss * factor / MiB
     for key, value in res.items():
         print(f"  {key:12.12s}: {value:10.0f} MiB")
     return True
-- 
1.8.3.1

