python: Silence yapf import triggered log messages

Importing yapf in any Python file was triggering these log messages
during bootstrap and activate on Linux and Mac:

Generating grammar tables from
 ./.environment/cipd/packages/python/lib/python3.9/lib2to3/Grammar.txt
Writing grammar tables to
 ./.environment/cipd/packages/python/lib/python3.9/lib2to3/Grammar3.9.5.final.0.pickle
Writing failed: [Errno 13] Permission denied:
 './.environment/cipd/packages/python/lib/python3.9/lib2to3/Grammar3.9.5.final.0.pickle'
Generating grammar tables from
 ./.environment/cipd/packages/python/lib/python3.9/lib2to3/PatternGrammar.txt
Writing grammar tables to
 ./.environment/cipd/packages/python/lib/python3.9/lib2to3/PatternGrammar3.9.5.final.0.pickle
Writing failed: [Errno 13] Permission denied:
 './.environment/cipd/packages/python/lib/python3.9/lib2to3/PatternGrammar3.9.5.final.0.pickle'

This CL modifies pw_env_setup to chmoding those pickle files to be
writeable. All CIPD packages are put in place as read only.

Additionally those log messages are suppressed when importing yapf
in python_protos.py.

Bug: b/236166970
Change-Id: I41ca5ca6dd435a9fb534983d9aa967a31314ff62
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/98520
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Pigweed-Auto-Submit: Anthony DiGirolamo <tonymd@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
diff --git a/pw_env_setup/py/pw_env_setup/virtualenv_setup/install.py b/pw_env_setup/py/pw_env_setup/virtualenv_setup/install.py
index b8841b9..73c751f 100644
--- a/pw_env_setup/py/pw_env_setup/virtualenv_setup/install.py
+++ b/pw_env_setup/py/pw_env_setup/virtualenv_setup/install.py
@@ -24,6 +24,7 @@
 import shutil
 import subprocess
 import sys
+import stat
 import tempfile
 
 # Grabbing datetime string once so it will always be the same for all GnTarget
@@ -130,6 +131,24 @@
             shutil.rmtree(venv_path)
 
 
+def _check_python_install_permissions(python):
+    # These pickle files are not included on windows.
+    # The path on windows is environment/cipd/packages/python/bin/Lib/lib2to3/
+    if platform.system().lower() == 'windows':
+        return
+
+    # Make any existing lib2to3 pickle files read+write. This is needed for
+    # importing yapf.
+    lib2to3_path = os.path.join(os.path.dirname(os.path.dirname(python)),
+                                'lib', 'python3.9', 'lib2to3')
+    pickle_file_paths = list(file_path
+                             for file_path in os.listdir(lib2to3_path)
+                             if '.pickle' in file_path)
+    for pickle_file in pickle_file_paths:
+        pickle_full_path = os.path.join(lib2to3_path, pickle_file)
+        os.chmod(pickle_full_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
+
+
 def install(  # pylint: disable=too-many-arguments
     project_root,
     venv_path,
@@ -175,6 +194,7 @@
 
     pyvenv_cfg = os.path.join(venv_path, 'pyvenv.cfg')
 
+    _check_python_install_permissions(python)
     _check_venv(python, version, venv_path, pyvenv_cfg)
 
     if full_envsetup or not os.path.exists(pyvenv_cfg):
diff --git a/pw_protobuf_compiler/py/pw_protobuf_compiler/python_protos.py b/pw_protobuf_compiler/py/pw_protobuf_compiler/python_protos.py
index 5030051..c8f080d 100644
--- a/pw_protobuf_compiler/py/pw_protobuf_compiler/python_protos.py
+++ b/pw_protobuf_compiler/py/pw_protobuf_compiler/python_protos.py
@@ -25,7 +25,26 @@
 from typing import (Dict, Generic, Iterable, Iterator, List, NamedTuple, Set,
                     Tuple, TypeVar, Union)
 
-from yapf.yapflib import yapf_api  # type: ignore[import]
+# Temporarily set the root logger level to critical while importing yapf.
+# This silences INFO level messages from
+# .environment/cipd/packages/python/lib/python3.9/lib2to3/driver.py
+# when it writes Grammar3.*.pickle and PatternGrammar3.*.pickle files.
+_original_level = 0
+for handler in logging.getLogger().handlers:
+    if type(handler) == logging.StreamHandler:  # pylint: disable=unidiomatic-typecheck
+        if handler.level > _original_level:
+            _original_level = handler.level
+        handler.level = logging.CRITICAL
+
+from yapf.yapflib import yapf_api  # type: ignore[import] # pylint: disable=wrong-import-position
+
+# Restore the original stderr/out log handler level.
+for handler in logging.getLogger().handlers:
+    # Must use type() check here since isinstance returns True for FileHandlers
+    # and StreamHandler: isinstance(logging.FileHandler, logging.StreamHandler)
+    if type(handler) == logging.StreamHandler:  # pylint: disable=unidiomatic-typecheck
+        handler.level = _original_level
+del _original_level
 
 _LOG = logging.getLogger(__name__)