We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add support for PEX_INTERPRETER `-c`, `-m` and `-` special modes to PEX venvs.
#1128 lands a Venv tool that creates a venv from a PEX file with an entrypoint that fully emulates PEX behavior except for handling `-c`, `-m` and `-` special modes when `PEX_INTERPRETER` is activated. Implement these modes so that a PEX venv can be counted on to work exactly like a PEX file.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pex/tools/commands/venv.py`
Content:
```
1 # Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
2 # Licensed under the Apache License, Version 2.0 (see LICENSE).
3 
4 from __future__ import absolute_import
5 
6 import errno
7 import os
8 import shutil
9 import zipfile
10 from argparse import ArgumentParser, Namespace
11 from textwrap import dedent
12 
13 from pex import pex_builder, pex_warnings
14 from pex.common import chmod_plus_x, safe_mkdir
15 from pex.environment import PEXEnvironment
16 from pex.pex import PEX
17 from pex.tools.command import Command, Error, Ok, Result
18 from pex.tools.commands.virtualenv import PipUnavailableError, Virtualenv
19 from pex.tracer import TRACER
20 from pex.typing import TYPE_CHECKING
21 
22 if TYPE_CHECKING:
23     from typing import Tuple
24 
25 
26 # N.B.: We can't use shutil.copytree since we copy from multiple source locations to the same site
27 # packages directory destination. Since we're forced to stray from the stdlib here, support for
28 # hardlinks is added to provide a measurable speed up and disk space savings when possible.
29 def _copytree(
30     src,  # type: str
31     dst,  # type: str
32     exclude=(),  # type: Tuple[str, ...]
33     collisions_ok=False,  # type: bool
34 ):
35     # type: (...) -> None
36     safe_mkdir(dst)
37     link = True
38     for root, dirs, files in os.walk(src, topdown=True, followlinks=False):
39         if src == root:
40             dirs[:] = [d for d in dirs if d not in exclude]
41             files[:] = [f for f in files if f not in exclude]
42 
43         for d in dirs:
44             try:
45                 os.mkdir(os.path.join(dst, os.path.relpath(os.path.join(root, d), src)))
46             except OSError as e:
47                 if e.errno != errno.EEXIST:
48                     raise e
49 
50         for f in files:
51             src_entry = os.path.join(root, f)
52             dst_entry = os.path.join(dst, os.path.relpath(src_entry, src))
53             try:
54                 if link:
55                     try:
56                         os.link(src_entry, dst_entry)
57                         continue
58                     except OSError as e:
59                         if e.errno != errno.EXDEV:
60                             raise e
61                         link = False
62                 shutil.copy(src_entry, dst_entry)
63             except OSError as e:
64                 if e.errno == errno.EEXIST:
65                     pex_warnings.warn(
66                         "Failed to overwrite {} with {}: {}".format(dst_entry, src_entry, e)
67                     )
68                     if not collisions_ok:
69                         raise e
70 
71 
72 class Venv(Command):
73     """Creates a venv from the PEX file."""
74 
75     def add_arguments(self, parser):
76         # type: (ArgumentParser) -> None
77         parser.add_argument(
78             "venv",
79             nargs=1,
80             metavar="PATH",
81             help="The directory to create the virtual environment in.",
82         )
83         parser.add_argument(
84             "-b",
85             "--bin-path",
86             choices=("prepend", "append"),
87             default=None,
88             help="Add the venv bin dir to the PATH in the __main__.py script.",
89         )
90         parser.add_argument(
91             "-f",
92             "--force",
93             action="store_true",
94             default=False,
95             help="If the venv directory already exists, overwrite it.",
96         )
97         parser.add_argument(
98             "--collisions-ok",
99             action="store_true",
100             default=False,
101             help=(
102                 "Don't error if population of the venv encounters distributions in the PEX file "
103                 "with colliding files."
104             ),
105         )
106         parser.add_argument(
107             "-p",
108             "--pip",
109             action="store_true",
110             default=False,
111             help="Add pip to the venv.",
112         )
113 
114     def run(
115         self,
116         pex,  # type: PEX
117         options,  # type: Namespace
118     ):
119         # type: (...) -> Result
120 
121         # 0. Create an empty virtual environment to populate with the PEX code and dependencies.
122         venv = Virtualenv.create(options.venv[0], interpreter=pex.interpreter, force=options.force)
123 
124         # 1. Populate the venv with the PEX contents.
125         pex_info = pex.pex_info()
126         if zipfile.is_zipfile(pex.path()):
127             PEXEnvironment.explode_code(
128                 pex.path(), pex_info, venv.site_packages_dir, exclude=("__main__.py",)
129             )
130         else:
131             _copytree(
132                 src=pex.path(),
133                 dst=venv.site_packages_dir,
134                 exclude=(pex_info.internal_cache, pex_builder.BOOTSTRAP_DIR, "__main__.py"),
135             )
136 
137         for dist in pex.activate():
138             _copytree(
139                 src=dist.location,
140                 dst=venv.site_packages_dir,
141                 exclude=("bin",),
142                 collisions_ok=options.collisions_ok,
143             )
144             dist_bin_dir = os.path.join(dist.location, "bin")
145             if os.path.isdir(dist_bin_dir):
146                 _copytree(dist_bin_dir, venv.bin_dir, collisions_ok=options.collisions_ok)
147 
148         # 2. Add a __main__ to the root of the venv for running the venv dir like a loose PEX dir
149         # and a main.py for running as a script.
150         main_contents = dedent(
151             """\
152             #!{venv_python} -sE
153 
154             import os
155             import sys
156 
157             python = {venv_python!r}
158             if sys.executable != python:
159                 os.execv(python, [python, "-sE"] + sys.argv)
160 
161             os.environ["VIRTUAL_ENV"] = {venv_dir!r}
162             sys.path.extend(os.environ.get("PEX_EXTRA_SYS_PATH", "").split(os.pathsep))
163 
164             bin_dir = {venv_bin_dir!r}
165             bin_path = {bin_path!r}
166             if bin_path:
167                 PATH = os.environ.get("PATH", "").split(os.pathsep)
168                 if bin_path == "prepend":
169                     PATH = [bin_dir] + PATH
170                 else:
171                     PATH.append(bin_dir)
172                 os.environ["PATH"] = os.pathsep.join(PATH)
173 
174             PEX_OVERRIDE_KEYS = ("PEX_INTERPRETER", "PEX_SCRIPT", "PEX_MODULE")
175             pex_overrides = dict(
176                 (key, os.environ.pop(key)) for key in PEX_OVERRIDE_KEYS if key in os.environ
177             )
178             if len(pex_overrides) > 1:
179                 sys.stderr.write(
180                     "Can only specify one of {{overrides}}; found: {{found}}\\n".format(
181                         overrides=", ".join(PEX_OVERRIDE_KEYS),
182                         found=" ".join("{{}}={{}}".format(k, v) for k, v in pex_overrides.items())
183                     )
184                 )
185                 sys.exit(1)
186 
187             pex_script = pex_overrides.get("PEX_SCRIPT")
188             if pex_script:
189                 script_path = os.path.join(bin_dir, pex_script)
190                 os.execv(script_path, [script_path] + sys.argv[1:])
191 
192             # TODO(John Sirois): Support `-c`, `-m` and `-` special modes when PEX_INTERPRETER is
193             # activated like PEX files do: https://github.com/pantsbuild/pex/issues/1136
194             pex_interpreter = pex_overrides.get("PEX_INTERPRETER", "").lower()
195             entry_point = (
196                 "code:interact"
197                 if pex_interpreter in ("1", "true")
198                 else pex_overrides.get("PEX_MODULE", {entry_point!r} or "code:interact")
199             )
200             module_name, _, function = entry_point.partition(":")
201             if not function:
202                 import runpy
203                 runpy.run_module(module_name, run_name="__main__")
204             else:
205                 import importlib
206                 module = importlib.import_module(module_name)
207                 # N.B.: Functions may be hung off top-level objects in the module namespace,
208                 # e.g.: Class.method; so we drill down through any attributes to the final function
209                 # object.
210                 namespace, func = module, None
211                 for attr in function.split("."):
212                     func = namespace = getattr(namespace, attr)
213                 func()
214             """.format(
215                 venv_python=venv.interpreter.binary,
216                 bin_path=options.bin_path,
217                 venv_dir=venv.venv_dir,
218                 venv_bin_dir=venv.bin_dir,
219                 entry_point=pex_info.entry_point,
220             )
221         )
222         with open(venv.join_path("__main__.py"), "w") as fp:
223             fp.write(main_contents)
224         chmod_plus_x(fp.name)
225         os.symlink(os.path.basename(fp.name), venv.join_path("pex"))
226 
227         # 3. Re-write any (console) scripts to use the venv Python.
228         for script in venv.rewrite_scripts(python_args="-sE"):
229             TRACER.log("Re-writing {}".format(script))
230 
231         if options.pip:
232             try:
233                 venv.install_pip()
234             except PipUnavailableError as e:
235                 return Error(
236                     "The virtual environment was successfully created, but Pip was not "
237                     "installed:\n{}".format(e)
238                 )
239 
240         return Ok()
241
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pex/tools/commands/venv.py b/pex/tools/commands/venv.py
--- a/pex/tools/commands/venv.py
+++ b/pex/tools/commands/venv.py
@@ -189,14 +189,47 @@
                 script_path = os.path.join(bin_dir, pex_script)
                 os.execv(script_path, [script_path] + sys.argv[1:])
 
-            # TODO(John Sirois): Support `-c`, `-m` and `-` special modes when PEX_INTERPRETER is
-            # activated like PEX files do: https://github.com/pantsbuild/pex/issues/1136
-            pex_interpreter = pex_overrides.get("PEX_INTERPRETER", "").lower()
+            pex_interpreter = pex_overrides.get("PEX_INTERPRETER", "").lower() in ("1", "true")
+            PEX_INTERPRETER_ENTRYPOINT = "code:interact"
             entry_point = (
-                "code:interact"
-                if pex_interpreter in ("1", "true")
-                else pex_overrides.get("PEX_MODULE", {entry_point!r} or "code:interact")
+                PEX_INTERPRETER_ENTRYPOINT
+                if pex_interpreter
+                else pex_overrides.get("PEX_MODULE", {entry_point!r} or PEX_INTERPRETER_ENTRYPOINT)
             )
+            if entry_point == PEX_INTERPRETER_ENTRYPOINT and len(sys.argv) > 1:
+                args = sys.argv[1:]
+                arg = args[0]
+                if arg == "-m":
+                    if len(args) < 2:
+                        sys.stderr.write("Argument expected for the -m option\\n")
+                        sys.exit(2)
+                    entry_point = module = args[1]
+                    sys.argv = args[1:]
+                    # Fall through to entry_point handling below.
+                else:
+                    filename = arg
+                    sys.argv = args
+                    if arg == "-c":
+                        if len(args) < 2:
+                            sys.stderr.write("Argument expected for the -c option\\n")
+                            sys.exit(2)
+                        filename = "-c <cmd>"
+                        content = args[1]
+                        sys.argv = ["-c"] + args[2:]
+                    elif arg == "-":
+                        content = sys.stdin.read()
+                    else:
+                        with open(arg) as fp:
+                            content = fp.read()
+                    
+                    ast = compile(content, filename, "exec", flags=0, dont_inherit=1)
+                    globals_map = globals().copy()
+                    globals_map["__name__"] = "__main__"
+                    globals_map["__file__"] = filename
+                    locals_map = globals_map
+                    {exec_ast}
+                    sys.exit(0)
+
             module_name, _, function = entry_point.partition(":")
             if not function:
                 import runpy
@@ -217,6 +250,11 @@
                 venv_dir=venv.venv_dir,
                 venv_bin_dir=venv.bin_dir,
                 entry_point=pex_info.entry_point,
+                exec_ast=(
+                    "exec ast in globals_map, locals_map"
+                    if venv.interpreter.version[0] == 2
+                    else "exec(ast, globals_map, locals_map)"
+                ),
             )
         )
         with open(venv.join_path("__main__.py"), "w") as fp:

{"golden_diff": "diff --git a/pex/tools/commands/venv.py b/pex/tools/commands/venv.py\n--- a/pex/tools/commands/venv.py\n+++ b/pex/tools/commands/venv.py\n@@ -189,14 +189,47 @@\n                 script_path = os.path.join(bin_dir, pex_script)\n                 os.execv(script_path, [script_path] + sys.argv[1:])\n \n-            # TODO(John Sirois): Support `-c`, `-m` and `-` special modes when PEX_INTERPRETER is\n-            # activated like PEX files do: https://github.com/pantsbuild/pex/issues/1136\n-            pex_interpreter = pex_overrides.get(\"PEX_INTERPRETER\", \"\").lower()\n+            pex_interpreter = pex_overrides.get(\"PEX_INTERPRETER\", \"\").lower() in (\"1\", \"true\")\n+            PEX_INTERPRETER_ENTRYPOINT = \"code:interact\"\n             entry_point = (\n-                \"code:interact\"\n-                if pex_interpreter in (\"1\", \"true\")\n-                else pex_overrides.get(\"PEX_MODULE\", {entry_point!r} or \"code:interact\")\n+                PEX_INTERPRETER_ENTRYPOINT\n+                if pex_interpreter\n+                else pex_overrides.get(\"PEX_MODULE\", {entry_point!r} or PEX_INTERPRETER_ENTRYPOINT)\n             )\n+            if entry_point == PEX_INTERPRETER_ENTRYPOINT and len(sys.argv) > 1:\n+                args = sys.argv[1:]\n+                arg = args[0]\n+                if arg == \"-m\":\n+                    if len(args) < 2:\n+                        sys.stderr.write(\"Argument expected for the -m option\\\\n\")\n+                        sys.exit(2)\n+                    entry_point = module = args[1]\n+                    sys.argv = args[1:]\n+                    # Fall through to entry_point handling below.\n+                else:\n+                    filename = arg\n+                    sys.argv = args\n+                    if arg == \"-c\":\n+                        if len(args) < 2:\n+                            sys.stderr.write(\"Argument expected for the -c option\\\\n\")\n+                            sys.exit(2)\n+                        filename = \"-c <cmd>\"\n+                        content = args[1]\n+                        sys.argv = [\"-c\"] + args[2:]\n+                    elif arg == \"-\":\n+                        content = sys.stdin.read()\n+                    else:\n+                        with open(arg) as fp:\n+                            content = fp.read()\n+                    \n+                    ast = compile(content, filename, \"exec\", flags=0, dont_inherit=1)\n+                    globals_map = globals().copy()\n+                    globals_map[\"__name__\"] = \"__main__\"\n+                    globals_map[\"__file__\"] = filename\n+                    locals_map = globals_map\n+                    {exec_ast}\n+                    sys.exit(0)\n+\n             module_name, _, function = entry_point.partition(\":\")\n             if not function:\n                 import runpy\n@@ -217,6 +250,11 @@\n                 venv_dir=venv.venv_dir,\n                 venv_bin_dir=venv.bin_dir,\n                 entry_point=pex_info.entry_point,\n+                exec_ast=(\n+                    \"exec ast in globals_map, locals_map\"\n+                    if venv.interpreter.version[0] == 2\n+                    else \"exec(ast, globals_map, locals_map)\"\n+                ),\n             )\n         )\n         with open(venv.join_path(\"__main__.py\"), \"w\") as fp:\n", "issue": "Add support for PEX_INTERPRETER `-c`, `-m` and `-` special modes to PEX venvs.\n#1128 lands a Venv tool that creates a venv from a PEX file with an entrypoint that fully emulates PEX behavior except for handling `-c`, `-m` and `-` special modes when `PEX_INTERPRETER` is activated. Implement these modes so that a PEX venv can be counted on to work exactly like a PEX file.\n", "before_files": [{"content": "# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import absolute_import\n\nimport errno\nimport os\nimport shutil\nimport zipfile\nfrom argparse import ArgumentParser, Namespace\nfrom textwrap import dedent\n\nfrom pex import pex_builder, pex_warnings\nfrom pex.common import chmod_plus_x, safe_mkdir\nfrom pex.environment import PEXEnvironment\nfrom pex.pex import PEX\nfrom pex.tools.command import Command, Error, Ok, Result\nfrom pex.tools.commands.virtualenv import PipUnavailableError, Virtualenv\nfrom pex.tracer import TRACER\nfrom pex.typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from typing import Tuple\n\n\n# N.B.: We can't use shutil.copytree since we copy from multiple source locations to the same site\n# packages directory destination. Since we're forced to stray from the stdlib here, support for\n# hardlinks is added to provide a measurable speed up and disk space savings when possible.\ndef _copytree(\n    src,  # type: str\n    dst,  # type: str\n    exclude=(),  # type: Tuple[str, ...]\n    collisions_ok=False,  # type: bool\n):\n    # type: (...) -> None\n    safe_mkdir(dst)\n    link = True\n    for root, dirs, files in os.walk(src, topdown=True, followlinks=False):\n        if src == root:\n            dirs[:] = [d for d in dirs if d not in exclude]\n            files[:] = [f for f in files if f not in exclude]\n\n        for d in dirs:\n            try:\n                os.mkdir(os.path.join(dst, os.path.relpath(os.path.join(root, d), src)))\n            except OSError as e:\n                if e.errno != errno.EEXIST:\n                    raise e\n\n        for f in files:\n            src_entry = os.path.join(root, f)\n            dst_entry = os.path.join(dst, os.path.relpath(src_entry, src))\n            try:\n                if link:\n                    try:\n                        os.link(src_entry, dst_entry)\n                        continue\n                    except OSError as e:\n                        if e.errno != errno.EXDEV:\n                            raise e\n                        link = False\n                shutil.copy(src_entry, dst_entry)\n            except OSError as e:\n                if e.errno == errno.EEXIST:\n                    pex_warnings.warn(\n                        \"Failed to overwrite {} with {}: {}\".format(dst_entry, src_entry, e)\n                    )\n                    if not collisions_ok:\n                        raise e\n\n\nclass Venv(Command):\n    \"\"\"Creates a venv from the PEX file.\"\"\"\n\n    def add_arguments(self, parser):\n        # type: (ArgumentParser) -> None\n        parser.add_argument(\n            \"venv\",\n            nargs=1,\n            metavar=\"PATH\",\n            help=\"The directory to create the virtual environment in.\",\n        )\n        parser.add_argument(\n            \"-b\",\n            \"--bin-path\",\n            choices=(\"prepend\", \"append\"),\n            default=None,\n            help=\"Add the venv bin dir to the PATH in the __main__.py script.\",\n        )\n        parser.add_argument(\n            \"-f\",\n            \"--force\",\n            action=\"store_true\",\n            default=False,\n            help=\"If the venv directory already exists, overwrite it.\",\n        )\n        parser.add_argument(\n            \"--collisions-ok\",\n            action=\"store_true\",\n            default=False,\n            help=(\n                \"Don't error if population of the venv encounters distributions in the PEX file \"\n                \"with colliding files.\"\n            ),\n        )\n        parser.add_argument(\n            \"-p\",\n            \"--pip\",\n            action=\"store_true\",\n            default=False,\n            help=\"Add pip to the venv.\",\n        )\n\n    def run(\n        self,\n        pex,  # type: PEX\n        options,  # type: Namespace\n    ):\n        # type: (...) -> Result\n\n        # 0. Create an empty virtual environment to populate with the PEX code and dependencies.\n        venv = Virtualenv.create(options.venv[0], interpreter=pex.interpreter, force=options.force)\n\n        # 1. Populate the venv with the PEX contents.\n        pex_info = pex.pex_info()\n        if zipfile.is_zipfile(pex.path()):\n            PEXEnvironment.explode_code(\n                pex.path(), pex_info, venv.site_packages_dir, exclude=(\"__main__.py\",)\n            )\n        else:\n            _copytree(\n                src=pex.path(),\n                dst=venv.site_packages_dir,\n                exclude=(pex_info.internal_cache, pex_builder.BOOTSTRAP_DIR, \"__main__.py\"),\n            )\n\n        for dist in pex.activate():\n            _copytree(\n                src=dist.location,\n                dst=venv.site_packages_dir,\n                exclude=(\"bin\",),\n                collisions_ok=options.collisions_ok,\n            )\n            dist_bin_dir = os.path.join(dist.location, \"bin\")\n            if os.path.isdir(dist_bin_dir):\n                _copytree(dist_bin_dir, venv.bin_dir, collisions_ok=options.collisions_ok)\n\n        # 2. Add a __main__ to the root of the venv for running the venv dir like a loose PEX dir\n        # and a main.py for running as a script.\n        main_contents = dedent(\n            \"\"\"\\\n            #!{venv_python} -sE\n\n            import os\n            import sys\n\n            python = {venv_python!r}\n            if sys.executable != python:\n                os.execv(python, [python, \"-sE\"] + sys.argv)\n\n            os.environ[\"VIRTUAL_ENV\"] = {venv_dir!r}\n            sys.path.extend(os.environ.get(\"PEX_EXTRA_SYS_PATH\", \"\").split(os.pathsep))\n\n            bin_dir = {venv_bin_dir!r}\n            bin_path = {bin_path!r}\n            if bin_path:\n                PATH = os.environ.get(\"PATH\", \"\").split(os.pathsep)\n                if bin_path == \"prepend\":\n                    PATH = [bin_dir] + PATH\n                else:\n                    PATH.append(bin_dir)\n                os.environ[\"PATH\"] = os.pathsep.join(PATH)\n\n            PEX_OVERRIDE_KEYS = (\"PEX_INTERPRETER\", \"PEX_SCRIPT\", \"PEX_MODULE\")\n            pex_overrides = dict(\n                (key, os.environ.pop(key)) for key in PEX_OVERRIDE_KEYS if key in os.environ\n            )\n            if len(pex_overrides) > 1:\n                sys.stderr.write(\n                    \"Can only specify one of {{overrides}}; found: {{found}}\\\\n\".format(\n                        overrides=\", \".join(PEX_OVERRIDE_KEYS),\n                        found=\" \".join(\"{{}}={{}}\".format(k, v) for k, v in pex_overrides.items())\n                    )\n                )\n                sys.exit(1)\n\n            pex_script = pex_overrides.get(\"PEX_SCRIPT\")\n            if pex_script:\n                script_path = os.path.join(bin_dir, pex_script)\n                os.execv(script_path, [script_path] + sys.argv[1:])\n\n            # TODO(John Sirois): Support `-c`, `-m` and `-` special modes when PEX_INTERPRETER is\n            # activated like PEX files do: https://github.com/pantsbuild/pex/issues/1136\n            pex_interpreter = pex_overrides.get(\"PEX_INTERPRETER\", \"\").lower()\n            entry_point = (\n                \"code:interact\"\n                if pex_interpreter in (\"1\", \"true\")\n                else pex_overrides.get(\"PEX_MODULE\", {entry_point!r} or \"code:interact\")\n            )\n            module_name, _, function = entry_point.partition(\":\")\n            if not function:\n                import runpy\n                runpy.run_module(module_name, run_name=\"__main__\")\n            else:\n                import importlib\n                module = importlib.import_module(module_name)\n                # N.B.: Functions may be hung off top-level objects in the module namespace,\n                # e.g.: Class.method; so we drill down through any attributes to the final function\n                # object.\n                namespace, func = module, None\n                for attr in function.split(\".\"):\n                    func = namespace = getattr(namespace, attr)\n                func()\n            \"\"\".format(\n                venv_python=venv.interpreter.binary,\n                bin_path=options.bin_path,\n                venv_dir=venv.venv_dir,\n                venv_bin_dir=venv.bin_dir,\n                entry_point=pex_info.entry_point,\n            )\n        )\n        with open(venv.join_path(\"__main__.py\"), \"w\") as fp:\n            fp.write(main_contents)\n        chmod_plus_x(fp.name)\n        os.symlink(os.path.basename(fp.name), venv.join_path(\"pex\"))\n\n        # 3. Re-write any (console) scripts to use the venv Python.\n        for script in venv.rewrite_scripts(python_args=\"-sE\"):\n            TRACER.log(\"Re-writing {}\".format(script))\n\n        if options.pip:\n            try:\n                venv.install_pip()\n            except PipUnavailableError as e:\n                return Error(\n                    \"The virtual environment was successfully created, but Pip was not \"\n                    \"installed:\\n{}\".format(e)\n                )\n\n        return Ok()\n", "path": "pex/tools/commands/venv.py"}], "after_files": [{"content": "# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import absolute_import\n\nimport errno\nimport os\nimport shutil\nimport zipfile\nfrom argparse import ArgumentParser, Namespace\nfrom textwrap import dedent\n\nfrom pex import pex_builder, pex_warnings\nfrom pex.common import chmod_plus_x, safe_mkdir\nfrom pex.environment import PEXEnvironment\nfrom pex.pex import PEX\nfrom pex.tools.command import Command, Error, Ok, Result\nfrom pex.tools.commands.virtualenv import PipUnavailableError, Virtualenv\nfrom pex.tracer import TRACER\nfrom pex.typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from typing import Tuple\n\n\n# N.B.: We can't use shutil.copytree since we copy from multiple source locations to the same site\n# packages directory destination. Since we're forced to stray from the stdlib here, support for\n# hardlinks is added to provide a measurable speed up and disk space savings when possible.\ndef _copytree(\n    src,  # type: str\n    dst,  # type: str\n    exclude=(),  # type: Tuple[str, ...]\n    collisions_ok=False,  # type: bool\n):\n    # type: (...) -> None\n    safe_mkdir(dst)\n    link = True\n    for root, dirs, files in os.walk(src, topdown=True, followlinks=False):\n        if src == root:\n            dirs[:] = [d for d in dirs if d not in exclude]\n            files[:] = [f for f in files if f not in exclude]\n\n        for d in dirs:\n            try:\n                os.mkdir(os.path.join(dst, os.path.relpath(os.path.join(root, d), src)))\n            except OSError as e:\n                if e.errno != errno.EEXIST:\n                    raise e\n\n        for f in files:\n            src_entry = os.path.join(root, f)\n            dst_entry = os.path.join(dst, os.path.relpath(src_entry, src))\n            try:\n                if link:\n                    try:\n                        os.link(src_entry, dst_entry)\n                        continue\n                    except OSError as e:\n                        if e.errno != errno.EXDEV:\n                            raise e\n                        link = False\n                shutil.copy(src_entry, dst_entry)\n            except OSError as e:\n                if e.errno == errno.EEXIST:\n                    pex_warnings.warn(\n                        \"Failed to overwrite {} with {}: {}\".format(dst_entry, src_entry, e)\n                    )\n                    if not collisions_ok:\n                        raise e\n\n\nclass Venv(Command):\n    \"\"\"Creates a venv from the PEX file.\"\"\"\n\n    def add_arguments(self, parser):\n        # type: (ArgumentParser) -> None\n        parser.add_argument(\n            \"venv\",\n            nargs=1,\n            metavar=\"PATH\",\n            help=\"The directory to create the virtual environment in.\",\n        )\n        parser.add_argument(\n            \"-b\",\n            \"--bin-path\",\n            choices=(\"prepend\", \"append\"),\n            default=None,\n            help=\"Add the venv bin dir to the PATH in the __main__.py script.\",\n        )\n        parser.add_argument(\n            \"-f\",\n            \"--force\",\n            action=\"store_true\",\n            default=False,\n            help=\"If the venv directory already exists, overwrite it.\",\n        )\n        parser.add_argument(\n            \"--collisions-ok\",\n            action=\"store_true\",\n            default=False,\n            help=(\n                \"Don't error if population of the venv encounters distributions in the PEX file \"\n                \"with colliding files.\"\n            ),\n        )\n        parser.add_argument(\n            \"-p\",\n            \"--pip\",\n            action=\"store_true\",\n            default=False,\n            help=\"Add pip to the venv.\",\n        )\n\n    def run(\n        self,\n        pex,  # type: PEX\n        options,  # type: Namespace\n    ):\n        # type: (...) -> Result\n\n        # 0. Create an empty virtual environment to populate with the PEX code and dependencies.\n        venv = Virtualenv.create(options.venv[0], interpreter=pex.interpreter, force=options.force)\n\n        # 1. Populate the venv with the PEX contents.\n        pex_info = pex.pex_info()\n        if zipfile.is_zipfile(pex.path()):\n            PEXEnvironment.explode_code(\n                pex.path(), pex_info, venv.site_packages_dir, exclude=(\"__main__.py\",)\n            )\n        else:\n            _copytree(\n                src=pex.path(),\n                dst=venv.site_packages_dir,\n                exclude=(pex_info.internal_cache, pex_builder.BOOTSTRAP_DIR, \"__main__.py\"),\n            )\n\n        for dist in pex.activate():\n            _copytree(\n                src=dist.location,\n                dst=venv.site_packages_dir,\n                exclude=(\"bin\",),\n                collisions_ok=options.collisions_ok,\n            )\n            dist_bin_dir = os.path.join(dist.location, \"bin\")\n            if os.path.isdir(dist_bin_dir):\n                _copytree(dist_bin_dir, venv.bin_dir, collisions_ok=options.collisions_ok)\n\n        # 2. Add a __main__ to the root of the venv for running the venv dir like a loose PEX dir\n        # and a main.py for running as a script.\n        main_contents = dedent(\n            \"\"\"\\\n            #!{venv_python} -sE\n\n            import os\n            import sys\n\n            python = {venv_python!r}\n            if sys.executable != python:\n                os.execv(python, [python, \"-sE\"] + sys.argv)\n\n            os.environ[\"VIRTUAL_ENV\"] = {venv_dir!r}\n            sys.path.extend(os.environ.get(\"PEX_EXTRA_SYS_PATH\", \"\").split(os.pathsep))\n\n            bin_dir = {venv_bin_dir!r}\n            bin_path = {bin_path!r}\n            if bin_path:\n                PATH = os.environ.get(\"PATH\", \"\").split(os.pathsep)\n                if bin_path == \"prepend\":\n                    PATH = [bin_dir] + PATH\n                else:\n                    PATH.append(bin_dir)\n                os.environ[\"PATH\"] = os.pathsep.join(PATH)\n\n            PEX_OVERRIDE_KEYS = (\"PEX_INTERPRETER\", \"PEX_SCRIPT\", \"PEX_MODULE\")\n            pex_overrides = dict(\n                (key, os.environ.pop(key)) for key in PEX_OVERRIDE_KEYS if key in os.environ\n            )\n            if len(pex_overrides) > 1:\n                sys.stderr.write(\n                    \"Can only specify one of {{overrides}}; found: {{found}}\\\\n\".format(\n                        overrides=\", \".join(PEX_OVERRIDE_KEYS),\n                        found=\" \".join(\"{{}}={{}}\".format(k, v) for k, v in pex_overrides.items())\n                    )\n                )\n                sys.exit(1)\n\n            pex_script = pex_overrides.get(\"PEX_SCRIPT\")\n            if pex_script:\n                script_path = os.path.join(bin_dir, pex_script)\n                os.execv(script_path, [script_path] + sys.argv[1:])\n\n            pex_interpreter = pex_overrides.get(\"PEX_INTERPRETER\", \"\").lower() in (\"1\", \"true\")\n            PEX_INTERPRETER_ENTRYPOINT = \"code:interact\"\n            entry_point = (\n                PEX_INTERPRETER_ENTRYPOINT\n                if pex_interpreter\n                else pex_overrides.get(\"PEX_MODULE\", {entry_point!r} or PEX_INTERPRETER_ENTRYPOINT)\n            )\n            if entry_point == PEX_INTERPRETER_ENTRYPOINT and len(sys.argv) > 1:\n                args = sys.argv[1:]\n                arg = args[0]\n                if arg == \"-m\":\n                    if len(args) < 2:\n                        sys.stderr.write(\"Argument expected for the -m option\\\\n\")\n                        sys.exit(2)\n                    entry_point = module = args[1]\n                    sys.argv = args[1:]\n                    # Fall through to entry_point handling below.\n                else:\n                    filename = arg\n                    sys.argv = args\n                    if arg == \"-c\":\n                        if len(args) < 2:\n                            sys.stderr.write(\"Argument expected for the -c option\\\\n\")\n                            sys.exit(2)\n                        filename = \"-c <cmd>\"\n                        content = args[1]\n                        sys.argv = [\"-c\"] + args[2:]\n                    elif arg == \"-\":\n                        content = sys.stdin.read()\n                    else:\n                        with open(arg) as fp:\n                            content = fp.read()\n                    \n                    ast = compile(content, filename, \"exec\", flags=0, dont_inherit=1)\n                    globals_map = globals().copy()\n                    globals_map[\"__name__\"] = \"__main__\"\n                    globals_map[\"__file__\"] = filename\n                    locals_map = globals_map\n                    {exec_ast}\n                    sys.exit(0)\n\n            module_name, _, function = entry_point.partition(\":\")\n            if not function:\n                import runpy\n                runpy.run_module(module_name, run_name=\"__main__\")\n            else:\n                import importlib\n                module = importlib.import_module(module_name)\n                # N.B.: Functions may be hung off top-level objects in the module namespace,\n                # e.g.: Class.method; so we drill down through any attributes to the final function\n                # object.\n                namespace, func = module, None\n                for attr in function.split(\".\"):\n                    func = namespace = getattr(namespace, attr)\n                func()\n            \"\"\".format(\n                venv_python=venv.interpreter.binary,\n                bin_path=options.bin_path,\n                venv_dir=venv.venv_dir,\n                venv_bin_dir=venv.bin_dir,\n                entry_point=pex_info.entry_point,\n                exec_ast=(\n                    \"exec ast in globals_map, locals_map\"\n                    if venv.interpreter.version[0] == 2\n                    else \"exec(ast, globals_map, locals_map)\"\n                ),\n            )\n        )\n        with open(venv.join_path(\"__main__.py\"), \"w\") as fp:\n            fp.write(main_contents)\n        chmod_plus_x(fp.name)\n        os.symlink(os.path.basename(fp.name), venv.join_path(\"pex\"))\n\n        # 3. Re-write any (console) scripts to use the venv Python.\n        for script in venv.rewrite_scripts(python_args=\"-sE\"):\n            TRACER.log(\"Re-writing {}\".format(script))\n\n        if options.pip:\n            try:\n                venv.install_pip()\n            except PipUnavailableError as e:\n                return Error(\n                    \"The virtual environment was successfully created, but Pip was not \"\n                    \"installed:\\n{}\".format(e)\n                )\n\n        return Ok()\n", "path": "pex/tools/commands/venv.py"}]}