Repository: pex-tool/pex

Issue:
Pex isolation observes concurrent bytecode compilation.
Seen in CI:
```
=================================== FAILURES ===================================
_____________________________ test_pex_root_build ______________________________
[gw0] linux -- Python 3.9.2 /home/runner/work/pex/pex/.tox/py39-integration/bin/python

    def test_pex_root_build():
        # type: () -> None
        with temporary_dir() as td, temporary_dir() as home:
            buildtime_pex_root = os.path.join(td, "buildtime_pex_root")
            output_dir = os.path.join(td, "output_dir")
    
            output_path = os.path.join(output_dir, "pex.pex")
            args = [
                "pex",
                "-o",
                output_path,
                "--not-zip-safe",
                "--pex-root={}".format(buildtime_pex_root),
            ]
            results = run_pex_command(args=args, env=make_env(HOME=home, PEX_INTERPRETER="1"))
>           results.assert_success()

tests/test_integration.py:139: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pex.testing.IntegResults object at 0x7f3b96235520>

    def assert_success(self):
        # type: () -> None
>       assert (
            self.return_code == 0
        ), "integration test failed: return_code={}, output={}, error={}".format(
            self.return_code, self.output, self.error
        )
E       AssertionError: integration test failed: return_code=1, output=, error=pex: Building pex
E       pex: Building pex :: Resolving distributions (['pex'])
E       pex: Building pex :: Resolving distributions (['pex']) :: Resolving requirements.
E       pex: Building pex :: Resolving distributions (['pex']) :: Resolving requirements. :: Resolving for:
E         DistributionTarget(interpreter=PythonInterpreter('/home/runner/work/pex/pex/.tox/py39-integration/bin/python', PythonIdentity('/home/runner/work/pex/pex/.tox/py39-integration/bin/python', 'cp39', 'cp39', 'manylinux_2_31_x86_64', (3, 9, 2))))
E       pex: Hashing pex
E       pex: Hashing pex: 39.9ms
E       pex: Isolating pex
E       pex: Isolating pex :: Extracting pex to /tmp/tmp0cf1npes/buildtime_pex_root/isolated/823f13c2a0ee2d652a83c35660353522d49e5aac
E       Failed to spawn a job for DistributionTarget(interpreter=PythonInterpreter('/home/runner/work/pex/pex/.tox/py39-integration/bin/python', PythonIdentity('/home/runner/work/pex/pex/.tox/py39-integration/bin/python', 'cp39', 'cp39', 'manylinux_2_31_x86_64', (3, 9, 2)))): [Errno 2] No such file or directory: '/home/runner/work/pex/pex/pex/vendor/_vendored/setuptools/pkg_resources/__pycache__/__init__.cpython-36.pyc.140691570564272'

pex/testing.py:339: AssertionError
========= 1 failed, 107 passed, 4 skipped, 1 xfailed in 494.81 seconds =========
ERROR: InvocationError for command /home/runner/work/pex/pex/.tox/py39-integration/bin/pytest -n auto tests/test_integration.py -vvs (exited with code 1)
___________________________________ summary ____________________________________
ERROR:   py39-integration: commands failed
Error: Process completed with exit code 1.
```


Discussion:

Resolving patch:
diff --git a/pex/third_party/__init__.py b/pex/third_party/__init__.py
--- a/pex/third_party/__init__.py
+++ b/pex/third_party/__init__.py
@@ -346,7 +346,7 @@ def isolated():
     global _ISOLATED
     if _ISOLATED is None:
         from pex import vendor
-        from pex.common import atomic_directory
+        from pex.common import atomic_directory, is_pyc_temporary_file
         from pex.util import CacheHelper
         from pex.variables import ENV
         from pex.third_party.pkg_resources import resource_isdir, resource_listdir, resource_stream
@@ -367,8 +367,10 @@ def recursive_copy(srcdir, dstdir):
                 src_entry = "{}/{}".format(srcdir, entry_name) if srcdir else entry_name
                 dst_entry = os.path.join(dstdir, entry_name)
                 if resource_isdir(module, src_entry):
+                    if os.path.basename(src_entry) == "__pycache__":
+                        continue
                     recursive_copy(src_entry, dst_entry)
-                elif not entry_name.endswith(".pyc"):
+                elif not entry_name.endswith(".pyc") and not is_pyc_temporary_file(entry_name):
                     with open(dst_entry, "wb") as fp:
                         with closing(resource_stream(module, src_entry)) as resource:
                             shutil.copyfileobj(resource, fp)


Test patch: