We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Write a proper commonprefix / nearest ancestor path function.
As discussed in this thread, the stdlib is lacking here: https://github.com/pantsbuild/pex/pull/1914#discussion_r974846450

--- 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/compatibility.py`
Content:
```
1 # Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
2 # Licensed under the Apache License, Version 2.0 (see LICENSE).
3 
4 # This file contains several 2.x/3.x compatibility checkstyle violations for a reason
5 # checkstyle: noqa
6 
7 from __future__ import absolute_import
8 
9 import os
10 import re
11 import sys
12 from abc import ABCMeta
13 from sys import version_info as sys_version_info
14 
15 from pex.typing import TYPE_CHECKING, cast
16 
17 if TYPE_CHECKING:
18     from typing import IO, AnyStr, BinaryIO, Callable, Optional, Text, Tuple, Type
19 
20 
21 try:
22     # Python 2.x
23     from ConfigParser import ConfigParser as ConfigParser
24 except ImportError:
25     # Python 3.x
26     from configparser import ConfigParser as ConfigParser  # type: ignore[import, no-redef]
27 
28 
29 AbstractClass = ABCMeta("AbstractClass", (object,), {})
30 PY2 = sys_version_info[0] == 2
31 PY3 = sys_version_info[0] == 3
32 
33 string = cast("Tuple[Type, ...]", (str,) if PY3 else (str, unicode))  # type: ignore[name-defined]
34 text = cast("Type[Text]", str if PY3 else unicode)  # type: ignore[name-defined]
35 
36 
37 if PY2:
38     from collections import Iterable as Iterable
39     from collections import MutableSet as MutableSet
40 else:
41     from collections.abc import Iterable as Iterable
42     from collections.abc import MutableSet as MutableSet
43 
44 if PY2:
45 
46     def to_bytes(st, encoding="utf-8"):
47         # type: (AnyStr, Text) -> bytes
48         if isinstance(st, unicode):
49             return st.encode(encoding)
50         elif isinstance(st, bytes):
51             return st
52         else:
53             raise ValueError("Cannot convert %s to bytes" % type(st))
54 
55     def to_unicode(st, encoding="utf-8"):
56         # type: (AnyStr, Text) -> Text
57         if isinstance(st, unicode):
58             return st
59         elif isinstance(st, (str, bytes)):
60             return unicode(st, encoding)
61         else:
62             raise ValueError("Cannot convert %s to a unicode string" % type(st))
63 
64 else:
65 
66     def to_bytes(st, encoding="utf-8"):
67         # type: (AnyStr, Text) -> bytes
68         if isinstance(st, str):
69             return st.encode(encoding)
70         elif isinstance(st, bytes):
71             return st
72         else:
73             raise ValueError("Cannot convert %s to bytes." % type(st))
74 
75     def to_unicode(st, encoding="utf-8"):
76         # type: (AnyStr, Text) -> Text
77         if isinstance(st, str):
78             return st
79         elif isinstance(st, bytes):
80             return str(st, encoding)
81         else:
82             raise ValueError("Cannot convert %s to a unicode string" % type(st))
83 
84 
85 _PY3_EXEC_FUNCTION = """
86 def exec_function(ast, globals_map):
87   locals_map = globals_map
88   exec ast in globals_map, locals_map
89   return locals_map
90 """
91 
92 if PY3:
93 
94     def exec_function(ast, globals_map):
95         locals_map = globals_map
96         exec (ast, globals_map, locals_map)
97         return locals_map
98 
99 else:
100 
101     def exec_function(ast, globals_map):
102         raise AssertionError("Expected this function to be re-defined at runtime.")
103 
104     # This will result in `exec_function` being re-defined at runtime.
105     eval(compile(_PY3_EXEC_FUNCTION, "<exec_function>", "exec"))
106 
107 
108 if PY3:
109     from urllib import parse as urlparse
110     from urllib.error import HTTPError as HTTPError
111     from urllib.parse import unquote as unquote
112     from urllib.request import FileHandler as FileHandler
113     from urllib.request import HTTPBasicAuthHandler as HTTPBasicAuthHandler
114     from urllib.request import HTTPDigestAuthHandler as HTTPDigestAuthHandler
115     from urllib.request import HTTPPasswordMgrWithDefaultRealm as HTTPPasswordMgrWithDefaultRealm
116     from urllib.request import HTTPSHandler as HTTPSHandler
117     from urllib.request import ProxyHandler as ProxyHandler
118     from urllib.request import Request as Request
119     from urllib.request import build_opener as build_opener
120 else:
121     from urllib import unquote as unquote
122 
123     import urlparse as urlparse
124     from urllib2 import FileHandler as FileHandler
125     from urllib2 import HTTPBasicAuthHandler as HTTPBasicAuthHandler
126     from urllib2 import HTTPDigestAuthHandler as HTTPDigestAuthHandler
127     from urllib2 import HTTPError as HTTPError
128     from urllib2 import HTTPPasswordMgrWithDefaultRealm as HTTPPasswordMgrWithDefaultRealm
129     from urllib2 import HTTPSHandler as HTTPSHandler
130     from urllib2 import ProxyHandler as ProxyHandler
131     from urllib2 import Request as Request
132     from urllib2 import build_opener as build_opener
133 
134 if PY3:
135     from queue import Queue as Queue
136 
137     # The `os.sched_getaffinity` function appears to be supported on Linux but not OSX.
138     if not hasattr(os, "sched_getaffinity"):
139         from os import cpu_count as cpu_count
140     else:
141 
142         def cpu_count():
143             # type: () -> Optional[int]
144             # The set of CPUs accessible to the current process (pid 0).
145             cpu_set = os.sched_getaffinity(0)
146             return len(cpu_set)
147 
148 else:
149     from multiprocessing import cpu_count as cpu_count
150 
151     from Queue import Queue as Queue
152 
153 WINDOWS = os.name == "nt"
154 
155 
156 # Universal newlines is the default in Python 3.
157 MODE_READ_UNIVERSAL_NEWLINES = "rU" if PY2 else "r"
158 
159 
160 def _get_stdio_bytes_buffer(stdio):
161     # type: (IO[str]) -> BinaryIO
162     return cast("BinaryIO", getattr(stdio, "buffer", stdio))
163 
164 
165 def get_stdout_bytes_buffer():
166     # type: () -> BinaryIO
167     return _get_stdio_bytes_buffer(sys.stdout)
168 
169 
170 def get_stderr_bytes_buffer():
171     # type: () -> BinaryIO
172     return _get_stdio_bytes_buffer(sys.stderr)
173 
174 
175 if PY3:
176     is_valid_python_identifier = str.isidentifier
177 else:
178 
179     def is_valid_python_identifier(text):
180         # type: (str) -> bool
181 
182         # N.B.: Python 2.7 only supports ASCII characters so the check is easy and this is probably
183         # why it's nt in the stdlib.
184         # See: https://docs.python.org/2.7/reference/lexical_analysis.html#identifiers
185         return re.match(r"^[_a-zA-Z][_a-zA-Z0-9]*$", text) is not None
186 
187 
188 if PY2:
189 
190     def indent(
191         text,  # type: Text
192         prefix,  # type: Text
193         predicate=None,  # type: Optional[Callable[[Text], bool]]
194     ):
195         add_prefix = predicate if predicate else lambda line: bool(line.strip())
196         return "".join(
197             prefix + line if add_prefix(line) else line for line in text.splitlines(True)
198         )
199 
200 else:
201     from textwrap import indent as indent
202
```

Path: `pex/tools/main.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, print_function
5 
6 import os
7 from argparse import ArgumentParser, Namespace
8 
9 from pex import pex_bootstrapper
10 from pex.commands.command import GlobalConfigurationError, Main
11 from pex.pex import PEX
12 from pex.pex_bootstrapper import InterpreterTest
13 from pex.pex_info import PexInfo
14 from pex.result import Result, catch
15 from pex.tools import commands
16 from pex.tools.command import PEXCommand
17 from pex.tracer import TRACER
18 from pex.typing import TYPE_CHECKING
19 
20 if TYPE_CHECKING:
21     from typing import Callable, Optional, Union
22 
23     CommandFunc = Callable[[PEX, Namespace], Result]
24 
25 
26 def simplify_pex_path(pex_path):
27     # type: (str) -> str
28     # Generate the most concise path possible that is still cut/paste-able to the command line.
29     pex_path = os.path.abspath(pex_path)
30     cwd = os.getcwd()
31     if os.path.commonprefix((pex_path, cwd)) == cwd:
32         pex_path = os.path.relpath(pex_path, cwd)
33         # Handle users that do not have . as a PATH entry.
34         if not os.path.dirname(pex_path) and os.curdir not in os.environ.get("PATH", "").split(
35             os.pathsep
36         ):
37             pex_path = os.path.join(os.curdir, pex_path)
38     return pex_path
39 
40 
41 class PexTools(Main[PEXCommand]):
42     def __init__(self, pex=None):
43         # type: (Optional[PEX]) -> None
44 
45         pex_prog_path = simplify_pex_path(pex.path()) if pex else None
46 
47         # By default, let argparse derive prog from sys.argv[0].
48         prog = None  # type: Optional[str]
49         if pex:
50             prog = "PEX_TOOLS=1 {pex_path}".format(pex_path=pex_prog_path)
51 
52         description = "Tools for working with {}.".format(pex_prog_path if pex else "PEX files")
53         subparsers_description = (
54             "{} can be operated on using any of the following subcommands.".format(
55                 "The PEX file {}".format(pex_prog_path) if pex else "A PEX file"
56             )
57         )
58 
59         super(PexTools, self).__init__(
60             description=description,
61             subparsers_description=subparsers_description,
62             command_types=commands.all_commands(),
63             prog=prog,
64         )
65         self._pex = pex
66 
67     def add_arguments(self, parser):
68         # type: (ArgumentParser) -> None
69         if self._pex is None:
70             parser.add_argument(
71                 "pex", nargs=1, metavar="PATH", help="The path of the PEX file to operate on."
72             )
73 
74 
75 def main(pex=None):
76     # type: (Optional[PEX]) -> Union[int, str]
77 
78     pex_tools = PexTools(pex=pex)
79     try:
80         with pex_tools.parsed_command() as pex_command, TRACER.timed(
81             "Executing PEX_TOOLS {}".format(pex_command.name())
82         ):
83             if pex is None:
84                 pex_file_path = pex_command.options.pex[0]
85                 pex_info = PexInfo.from_pex(pex_file_path)
86                 pex_info.update(PexInfo.from_env())
87                 interpreter = pex_bootstrapper.find_compatible_interpreter(
88                     interpreter_test=InterpreterTest(entry_point=pex_file_path, pex_info=pex_info)
89                 )
90                 pex = PEX(pex_file_path, interpreter=interpreter)
91 
92             result = catch(pex_command.run, pex)
93             result.maybe_display()
94             return result.exit_code
95     except GlobalConfigurationError as e:
96         return str(e)
97
```
--- 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/compatibility.py b/pex/compatibility.py
--- a/pex/compatibility.py
+++ b/pex/compatibility.py
@@ -15,7 +15,19 @@
 from pex.typing import TYPE_CHECKING, cast
 
 if TYPE_CHECKING:
-    from typing import IO, AnyStr, BinaryIO, Callable, Optional, Text, Tuple, Type
+    from typing import (
+        IO,
+        AnyStr,
+        BinaryIO,
+        Callable,
+        Deque,
+        List,
+        Optional,
+        Sequence,
+        Text,
+        Tuple,
+        Type,
+    )
 
 
 try:
@@ -37,6 +49,7 @@
 if PY2:
     from collections import Iterable as Iterable
     from collections import MutableSet as MutableSet
+    from collections import deque
 else:
     from collections.abc import Iterable as Iterable
     from collections.abc import MutableSet as MutableSet
@@ -199,3 +212,50 @@
 
 else:
     from textwrap import indent as indent
+
+
+if PY3:
+    from os.path import commonpath as commonpath
+else:
+
+    def commonpath(paths):
+        # type: (Sequence[Text]) -> Text
+        if not paths:
+            raise ValueError("The paths given must be a non-empty sequence")
+        if len(paths) == 1:
+            return paths[0]
+        if len({os.path.isabs(path) for path in paths}) > 1:
+            raise ValueError(
+                "Can't mix absolute and relative paths, given:\n{paths}".format(
+                    paths="\n".join(paths)
+                )
+            )
+
+        def components(path):
+            # type: (Text) -> Iterable[Text]
+
+            pieces = deque()  # type: Deque[Text]
+
+            def append(piece):
+                if piece and piece != ".":
+                    pieces.appendleft(piece)
+
+            head, tail = os.path.split(path)
+            append(tail)
+            while head:
+                if "/" == head:
+                    append(head)
+                    break
+                head, tail = os.path.split(head)
+                append(tail)
+            return pieces
+
+        prefix = []  # type: List[Text]
+        for atoms in zip(*(components(path) for path in paths)):
+            if len(set(atoms)) == 1:
+                prefix.append(atoms[0])
+            else:
+                break
+        if not prefix:
+            return ""
+        return os.path.join(*prefix)
diff --git a/pex/tools/main.py b/pex/tools/main.py
--- a/pex/tools/main.py
+++ b/pex/tools/main.py
@@ -8,6 +8,7 @@
 
 from pex import pex_bootstrapper
 from pex.commands.command import GlobalConfigurationError, Main
+from pex.compatibility import commonpath
 from pex.pex import PEX
 from pex.pex_bootstrapper import InterpreterTest
 from pex.pex_info import PexInfo
@@ -28,7 +29,7 @@
     # Generate the most concise path possible that is still cut/paste-able to the command line.
     pex_path = os.path.abspath(pex_path)
     cwd = os.getcwd()
-    if os.path.commonprefix((pex_path, cwd)) == cwd:
+    if commonpath((pex_path, cwd)) == cwd:
         pex_path = os.path.relpath(pex_path, cwd)
         # Handle users that do not have . as a PATH entry.
         if not os.path.dirname(pex_path) and os.curdir not in os.environ.get("PATH", "").split(

{"golden_diff": "diff --git a/pex/compatibility.py b/pex/compatibility.py\n--- a/pex/compatibility.py\n+++ b/pex/compatibility.py\n@@ -15,7 +15,19 @@\n from pex.typing import TYPE_CHECKING, cast\n \n if TYPE_CHECKING:\n-    from typing import IO, AnyStr, BinaryIO, Callable, Optional, Text, Tuple, Type\n+    from typing import (\n+        IO,\n+        AnyStr,\n+        BinaryIO,\n+        Callable,\n+        Deque,\n+        List,\n+        Optional,\n+        Sequence,\n+        Text,\n+        Tuple,\n+        Type,\n+    )\n \n \n try:\n@@ -37,6 +49,7 @@\n if PY2:\n     from collections import Iterable as Iterable\n     from collections import MutableSet as MutableSet\n+    from collections import deque\n else:\n     from collections.abc import Iterable as Iterable\n     from collections.abc import MutableSet as MutableSet\n@@ -199,3 +212,50 @@\n \n else:\n     from textwrap import indent as indent\n+\n+\n+if PY3:\n+    from os.path import commonpath as commonpath\n+else:\n+\n+    def commonpath(paths):\n+        # type: (Sequence[Text]) -> Text\n+        if not paths:\n+            raise ValueError(\"The paths given must be a non-empty sequence\")\n+        if len(paths) == 1:\n+            return paths[0]\n+        if len({os.path.isabs(path) for path in paths}) > 1:\n+            raise ValueError(\n+                \"Can't mix absolute and relative paths, given:\\n{paths}\".format(\n+                    paths=\"\\n\".join(paths)\n+                )\n+            )\n+\n+        def components(path):\n+            # type: (Text) -> Iterable[Text]\n+\n+            pieces = deque()  # type: Deque[Text]\n+\n+            def append(piece):\n+                if piece and piece != \".\":\n+                    pieces.appendleft(piece)\n+\n+            head, tail = os.path.split(path)\n+            append(tail)\n+            while head:\n+                if \"/\" == head:\n+                    append(head)\n+                    break\n+                head, tail = os.path.split(head)\n+                append(tail)\n+            return pieces\n+\n+        prefix = []  # type: List[Text]\n+        for atoms in zip(*(components(path) for path in paths)):\n+            if len(set(atoms)) == 1:\n+                prefix.append(atoms[0])\n+            else:\n+                break\n+        if not prefix:\n+            return \"\"\n+        return os.path.join(*prefix)\ndiff --git a/pex/tools/main.py b/pex/tools/main.py\n--- a/pex/tools/main.py\n+++ b/pex/tools/main.py\n@@ -8,6 +8,7 @@\n \n from pex import pex_bootstrapper\n from pex.commands.command import GlobalConfigurationError, Main\n+from pex.compatibility import commonpath\n from pex.pex import PEX\n from pex.pex_bootstrapper import InterpreterTest\n from pex.pex_info import PexInfo\n@@ -28,7 +29,7 @@\n     # Generate the most concise path possible that is still cut/paste-able to the command line.\n     pex_path = os.path.abspath(pex_path)\n     cwd = os.getcwd()\n-    if os.path.commonprefix((pex_path, cwd)) == cwd:\n+    if commonpath((pex_path, cwd)) == cwd:\n         pex_path = os.path.relpath(pex_path, cwd)\n         # Handle users that do not have . as a PATH entry.\n         if not os.path.dirname(pex_path) and os.curdir not in os.environ.get(\"PATH\", \"\").split(\n", "issue": "Write a proper commonprefix / nearest ancestor path function.\nAs discussed in this thread, the stdlib is lacking here: https://github.com/pantsbuild/pex/pull/1914#discussion_r974846450\n", "before_files": [{"content": "# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\n# This file contains several 2.x/3.x compatibility checkstyle violations for a reason\n# checkstyle: noqa\n\nfrom __future__ import absolute_import\n\nimport os\nimport re\nimport sys\nfrom abc import ABCMeta\nfrom sys import version_info as sys_version_info\n\nfrom pex.typing import TYPE_CHECKING, cast\n\nif TYPE_CHECKING:\n    from typing import IO, AnyStr, BinaryIO, Callable, Optional, Text, Tuple, Type\n\n\ntry:\n    # Python 2.x\n    from ConfigParser import ConfigParser as ConfigParser\nexcept ImportError:\n    # Python 3.x\n    from configparser import ConfigParser as ConfigParser  # type: ignore[import, no-redef]\n\n\nAbstractClass = ABCMeta(\"AbstractClass\", (object,), {})\nPY2 = sys_version_info[0] == 2\nPY3 = sys_version_info[0] == 3\n\nstring = cast(\"Tuple[Type, ...]\", (str,) if PY3 else (str, unicode))  # type: ignore[name-defined]\ntext = cast(\"Type[Text]\", str if PY3 else unicode)  # type: ignore[name-defined]\n\n\nif PY2:\n    from collections import Iterable as Iterable\n    from collections import MutableSet as MutableSet\nelse:\n    from collections.abc import Iterable as Iterable\n    from collections.abc import MutableSet as MutableSet\n\nif PY2:\n\n    def to_bytes(st, encoding=\"utf-8\"):\n        # type: (AnyStr, Text) -> bytes\n        if isinstance(st, unicode):\n            return st.encode(encoding)\n        elif isinstance(st, bytes):\n            return st\n        else:\n            raise ValueError(\"Cannot convert %s to bytes\" % type(st))\n\n    def to_unicode(st, encoding=\"utf-8\"):\n        # type: (AnyStr, Text) -> Text\n        if isinstance(st, unicode):\n            return st\n        elif isinstance(st, (str, bytes)):\n            return unicode(st, encoding)\n        else:\n            raise ValueError(\"Cannot convert %s to a unicode string\" % type(st))\n\nelse:\n\n    def to_bytes(st, encoding=\"utf-8\"):\n        # type: (AnyStr, Text) -> bytes\n        if isinstance(st, str):\n            return st.encode(encoding)\n        elif isinstance(st, bytes):\n            return st\n        else:\n            raise ValueError(\"Cannot convert %s to bytes.\" % type(st))\n\n    def to_unicode(st, encoding=\"utf-8\"):\n        # type: (AnyStr, Text) -> Text\n        if isinstance(st, str):\n            return st\n        elif isinstance(st, bytes):\n            return str(st, encoding)\n        else:\n            raise ValueError(\"Cannot convert %s to a unicode string\" % type(st))\n\n\n_PY3_EXEC_FUNCTION = \"\"\"\ndef exec_function(ast, globals_map):\n  locals_map = globals_map\n  exec ast in globals_map, locals_map\n  return locals_map\n\"\"\"\n\nif PY3:\n\n    def exec_function(ast, globals_map):\n        locals_map = globals_map\n        exec (ast, globals_map, locals_map)\n        return locals_map\n\nelse:\n\n    def exec_function(ast, globals_map):\n        raise AssertionError(\"Expected this function to be re-defined at runtime.\")\n\n    # This will result in `exec_function` being re-defined at runtime.\n    eval(compile(_PY3_EXEC_FUNCTION, \"<exec_function>\", \"exec\"))\n\n\nif PY3:\n    from urllib import parse as urlparse\n    from urllib.error import HTTPError as HTTPError\n    from urllib.parse import unquote as unquote\n    from urllib.request import FileHandler as FileHandler\n    from urllib.request import HTTPBasicAuthHandler as HTTPBasicAuthHandler\n    from urllib.request import HTTPDigestAuthHandler as HTTPDigestAuthHandler\n    from urllib.request import HTTPPasswordMgrWithDefaultRealm as HTTPPasswordMgrWithDefaultRealm\n    from urllib.request import HTTPSHandler as HTTPSHandler\n    from urllib.request import ProxyHandler as ProxyHandler\n    from urllib.request import Request as Request\n    from urllib.request import build_opener as build_opener\nelse:\n    from urllib import unquote as unquote\n\n    import urlparse as urlparse\n    from urllib2 import FileHandler as FileHandler\n    from urllib2 import HTTPBasicAuthHandler as HTTPBasicAuthHandler\n    from urllib2 import HTTPDigestAuthHandler as HTTPDigestAuthHandler\n    from urllib2 import HTTPError as HTTPError\n    from urllib2 import HTTPPasswordMgrWithDefaultRealm as HTTPPasswordMgrWithDefaultRealm\n    from urllib2 import HTTPSHandler as HTTPSHandler\n    from urllib2 import ProxyHandler as ProxyHandler\n    from urllib2 import Request as Request\n    from urllib2 import build_opener as build_opener\n\nif PY3:\n    from queue import Queue as Queue\n\n    # The `os.sched_getaffinity` function appears to be supported on Linux but not OSX.\n    if not hasattr(os, \"sched_getaffinity\"):\n        from os import cpu_count as cpu_count\n    else:\n\n        def cpu_count():\n            # type: () -> Optional[int]\n            # The set of CPUs accessible to the current process (pid 0).\n            cpu_set = os.sched_getaffinity(0)\n            return len(cpu_set)\n\nelse:\n    from multiprocessing import cpu_count as cpu_count\n\n    from Queue import Queue as Queue\n\nWINDOWS = os.name == \"nt\"\n\n\n# Universal newlines is the default in Python 3.\nMODE_READ_UNIVERSAL_NEWLINES = \"rU\" if PY2 else \"r\"\n\n\ndef _get_stdio_bytes_buffer(stdio):\n    # type: (IO[str]) -> BinaryIO\n    return cast(\"BinaryIO\", getattr(stdio, \"buffer\", stdio))\n\n\ndef get_stdout_bytes_buffer():\n    # type: () -> BinaryIO\n    return _get_stdio_bytes_buffer(sys.stdout)\n\n\ndef get_stderr_bytes_buffer():\n    # type: () -> BinaryIO\n    return _get_stdio_bytes_buffer(sys.stderr)\n\n\nif PY3:\n    is_valid_python_identifier = str.isidentifier\nelse:\n\n    def is_valid_python_identifier(text):\n        # type: (str) -> bool\n\n        # N.B.: Python 2.7 only supports ASCII characters so the check is easy and this is probably\n        # why it's nt in the stdlib.\n        # See: https://docs.python.org/2.7/reference/lexical_analysis.html#identifiers\n        return re.match(r\"^[_a-zA-Z][_a-zA-Z0-9]*$\", text) is not None\n\n\nif PY2:\n\n    def indent(\n        text,  # type: Text\n        prefix,  # type: Text\n        predicate=None,  # type: Optional[Callable[[Text], bool]]\n    ):\n        add_prefix = predicate if predicate else lambda line: bool(line.strip())\n        return \"\".join(\n            prefix + line if add_prefix(line) else line for line in text.splitlines(True)\n        )\n\nelse:\n    from textwrap import indent as indent\n", "path": "pex/compatibility.py"}, {"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, print_function\n\nimport os\nfrom argparse import ArgumentParser, Namespace\n\nfrom pex import pex_bootstrapper\nfrom pex.commands.command import GlobalConfigurationError, Main\nfrom pex.pex import PEX\nfrom pex.pex_bootstrapper import InterpreterTest\nfrom pex.pex_info import PexInfo\nfrom pex.result import Result, catch\nfrom pex.tools import commands\nfrom pex.tools.command import PEXCommand\nfrom pex.tracer import TRACER\nfrom pex.typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from typing import Callable, Optional, Union\n\n    CommandFunc = Callable[[PEX, Namespace], Result]\n\n\ndef simplify_pex_path(pex_path):\n    # type: (str) -> str\n    # Generate the most concise path possible that is still cut/paste-able to the command line.\n    pex_path = os.path.abspath(pex_path)\n    cwd = os.getcwd()\n    if os.path.commonprefix((pex_path, cwd)) == cwd:\n        pex_path = os.path.relpath(pex_path, cwd)\n        # Handle users that do not have . as a PATH entry.\n        if not os.path.dirname(pex_path) and os.curdir not in os.environ.get(\"PATH\", \"\").split(\n            os.pathsep\n        ):\n            pex_path = os.path.join(os.curdir, pex_path)\n    return pex_path\n\n\nclass PexTools(Main[PEXCommand]):\n    def __init__(self, pex=None):\n        # type: (Optional[PEX]) -> None\n\n        pex_prog_path = simplify_pex_path(pex.path()) if pex else None\n\n        # By default, let argparse derive prog from sys.argv[0].\n        prog = None  # type: Optional[str]\n        if pex:\n            prog = \"PEX_TOOLS=1 {pex_path}\".format(pex_path=pex_prog_path)\n\n        description = \"Tools for working with {}.\".format(pex_prog_path if pex else \"PEX files\")\n        subparsers_description = (\n            \"{} can be operated on using any of the following subcommands.\".format(\n                \"The PEX file {}\".format(pex_prog_path) if pex else \"A PEX file\"\n            )\n        )\n\n        super(PexTools, self).__init__(\n            description=description,\n            subparsers_description=subparsers_description,\n            command_types=commands.all_commands(),\n            prog=prog,\n        )\n        self._pex = pex\n\n    def add_arguments(self, parser):\n        # type: (ArgumentParser) -> None\n        if self._pex is None:\n            parser.add_argument(\n                \"pex\", nargs=1, metavar=\"PATH\", help=\"The path of the PEX file to operate on.\"\n            )\n\n\ndef main(pex=None):\n    # type: (Optional[PEX]) -> Union[int, str]\n\n    pex_tools = PexTools(pex=pex)\n    try:\n        with pex_tools.parsed_command() as pex_command, TRACER.timed(\n            \"Executing PEX_TOOLS {}\".format(pex_command.name())\n        ):\n            if pex is None:\n                pex_file_path = pex_command.options.pex[0]\n                pex_info = PexInfo.from_pex(pex_file_path)\n                pex_info.update(PexInfo.from_env())\n                interpreter = pex_bootstrapper.find_compatible_interpreter(\n                    interpreter_test=InterpreterTest(entry_point=pex_file_path, pex_info=pex_info)\n                )\n                pex = PEX(pex_file_path, interpreter=interpreter)\n\n            result = catch(pex_command.run, pex)\n            result.maybe_display()\n            return result.exit_code\n    except GlobalConfigurationError as e:\n        return str(e)\n", "path": "pex/tools/main.py"}], "after_files": [{"content": "# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\n# This file contains several 2.x/3.x compatibility checkstyle violations for a reason\n# checkstyle: noqa\n\nfrom __future__ import absolute_import\n\nimport os\nimport re\nimport sys\nfrom abc import ABCMeta\nfrom sys import version_info as sys_version_info\n\nfrom pex.typing import TYPE_CHECKING, cast\n\nif TYPE_CHECKING:\n    from typing import (\n        IO,\n        AnyStr,\n        BinaryIO,\n        Callable,\n        Deque,\n        List,\n        Optional,\n        Sequence,\n        Text,\n        Tuple,\n        Type,\n    )\n\n\ntry:\n    # Python 2.x\n    from ConfigParser import ConfigParser as ConfigParser\nexcept ImportError:\n    # Python 3.x\n    from configparser import ConfigParser as ConfigParser  # type: ignore[import, no-redef]\n\n\nAbstractClass = ABCMeta(\"AbstractClass\", (object,), {})\nPY2 = sys_version_info[0] == 2\nPY3 = sys_version_info[0] == 3\n\nstring = cast(\"Tuple[Type, ...]\", (str,) if PY3 else (str, unicode))  # type: ignore[name-defined]\ntext = cast(\"Type[Text]\", str if PY3 else unicode)  # type: ignore[name-defined]\n\n\nif PY2:\n    from collections import Iterable as Iterable\n    from collections import MutableSet as MutableSet\n    from collections import deque\nelse:\n    from collections.abc import Iterable as Iterable\n    from collections.abc import MutableSet as MutableSet\n\nif PY2:\n\n    def to_bytes(st, encoding=\"utf-8\"):\n        # type: (AnyStr, Text) -> bytes\n        if isinstance(st, unicode):\n            return st.encode(encoding)\n        elif isinstance(st, bytes):\n            return st\n        else:\n            raise ValueError(\"Cannot convert %s to bytes\" % type(st))\n\n    def to_unicode(st, encoding=\"utf-8\"):\n        # type: (AnyStr, Text) -> Text\n        if isinstance(st, unicode):\n            return st\n        elif isinstance(st, (str, bytes)):\n            return unicode(st, encoding)\n        else:\n            raise ValueError(\"Cannot convert %s to a unicode string\" % type(st))\n\nelse:\n\n    def to_bytes(st, encoding=\"utf-8\"):\n        # type: (AnyStr, Text) -> bytes\n        if isinstance(st, str):\n            return st.encode(encoding)\n        elif isinstance(st, bytes):\n            return st\n        else:\n            raise ValueError(\"Cannot convert %s to bytes.\" % type(st))\n\n    def to_unicode(st, encoding=\"utf-8\"):\n        # type: (AnyStr, Text) -> Text\n        if isinstance(st, str):\n            return st\n        elif isinstance(st, bytes):\n            return str(st, encoding)\n        else:\n            raise ValueError(\"Cannot convert %s to a unicode string\" % type(st))\n\n\n_PY3_EXEC_FUNCTION = \"\"\"\ndef exec_function(ast, globals_map):\n  locals_map = globals_map\n  exec ast in globals_map, locals_map\n  return locals_map\n\"\"\"\n\nif PY3:\n\n    def exec_function(ast, globals_map):\n        locals_map = globals_map\n        exec (ast, globals_map, locals_map)\n        return locals_map\n\nelse:\n\n    def exec_function(ast, globals_map):\n        raise AssertionError(\"Expected this function to be re-defined at runtime.\")\n\n    # This will result in `exec_function` being re-defined at runtime.\n    eval(compile(_PY3_EXEC_FUNCTION, \"<exec_function>\", \"exec\"))\n\n\nif PY3:\n    from urllib import parse as urlparse\n    from urllib.error import HTTPError as HTTPError\n    from urllib.parse import unquote as unquote\n    from urllib.request import FileHandler as FileHandler\n    from urllib.request import HTTPBasicAuthHandler as HTTPBasicAuthHandler\n    from urllib.request import HTTPDigestAuthHandler as HTTPDigestAuthHandler\n    from urllib.request import HTTPPasswordMgrWithDefaultRealm as HTTPPasswordMgrWithDefaultRealm\n    from urllib.request import HTTPSHandler as HTTPSHandler\n    from urllib.request import ProxyHandler as ProxyHandler\n    from urllib.request import Request as Request\n    from urllib.request import build_opener as build_opener\nelse:\n    from urllib import unquote as unquote\n\n    import urlparse as urlparse\n    from urllib2 import FileHandler as FileHandler\n    from urllib2 import HTTPBasicAuthHandler as HTTPBasicAuthHandler\n    from urllib2 import HTTPDigestAuthHandler as HTTPDigestAuthHandler\n    from urllib2 import HTTPError as HTTPError\n    from urllib2 import HTTPPasswordMgrWithDefaultRealm as HTTPPasswordMgrWithDefaultRealm\n    from urllib2 import HTTPSHandler as HTTPSHandler\n    from urllib2 import ProxyHandler as ProxyHandler\n    from urllib2 import Request as Request\n    from urllib2 import build_opener as build_opener\n\nif PY3:\n    from queue import Queue as Queue\n\n    # The `os.sched_getaffinity` function appears to be supported on Linux but not OSX.\n    if not hasattr(os, \"sched_getaffinity\"):\n        from os import cpu_count as cpu_count\n    else:\n\n        def cpu_count():\n            # type: () -> Optional[int]\n            # The set of CPUs accessible to the current process (pid 0).\n            cpu_set = os.sched_getaffinity(0)\n            return len(cpu_set)\n\nelse:\n    from multiprocessing import cpu_count as cpu_count\n\n    from Queue import Queue as Queue\n\nWINDOWS = os.name == \"nt\"\n\n\n# Universal newlines is the default in Python 3.\nMODE_READ_UNIVERSAL_NEWLINES = \"rU\" if PY2 else \"r\"\n\n\ndef _get_stdio_bytes_buffer(stdio):\n    # type: (IO[str]) -> BinaryIO\n    return cast(\"BinaryIO\", getattr(stdio, \"buffer\", stdio))\n\n\ndef get_stdout_bytes_buffer():\n    # type: () -> BinaryIO\n    return _get_stdio_bytes_buffer(sys.stdout)\n\n\ndef get_stderr_bytes_buffer():\n    # type: () -> BinaryIO\n    return _get_stdio_bytes_buffer(sys.stderr)\n\n\nif PY3:\n    is_valid_python_identifier = str.isidentifier\nelse:\n\n    def is_valid_python_identifier(text):\n        # type: (str) -> bool\n\n        # N.B.: Python 2.7 only supports ASCII characters so the check is easy and this is probably\n        # why it's nt in the stdlib.\n        # See: https://docs.python.org/2.7/reference/lexical_analysis.html#identifiers\n        return re.match(r\"^[_a-zA-Z][_a-zA-Z0-9]*$\", text) is not None\n\n\nif PY2:\n\n    def indent(\n        text,  # type: Text\n        prefix,  # type: Text\n        predicate=None,  # type: Optional[Callable[[Text], bool]]\n    ):\n        add_prefix = predicate if predicate else lambda line: bool(line.strip())\n        return \"\".join(\n            prefix + line if add_prefix(line) else line for line in text.splitlines(True)\n        )\n\nelse:\n    from textwrap import indent as indent\n\n\nif PY3:\n    from os.path import commonpath as commonpath\nelse:\n\n    def commonpath(paths):\n        # type: (Sequence[Text]) -> Text\n        if not paths:\n            raise ValueError(\"The paths given must be a non-empty sequence\")\n        if len(paths) == 1:\n            return paths[0]\n        if len({os.path.isabs(path) for path in paths}) > 1:\n            raise ValueError(\n                \"Can't mix absolute and relative paths, given:\\n{paths}\".format(\n                    paths=\"\\n\".join(paths)\n                )\n            )\n\n        def components(path):\n            # type: (Text) -> Iterable[Text]\n\n            pieces = deque()  # type: Deque[Text]\n\n            def append(piece):\n                if piece and piece != \".\":\n                    pieces.appendleft(piece)\n\n            head, tail = os.path.split(path)\n            append(tail)\n            while head:\n                if \"/\" == head:\n                    append(head)\n                    break\n                head, tail = os.path.split(head)\n                append(tail)\n            return pieces\n\n        prefix = []  # type: List[Text]\n        for atoms in zip(*(components(path) for path in paths)):\n            if len(set(atoms)) == 1:\n                prefix.append(atoms[0])\n            else:\n                break\n        if not prefix:\n            return \"\"\n        return os.path.join(*prefix)\n", "path": "pex/compatibility.py"}, {"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, print_function\n\nimport os\nfrom argparse import ArgumentParser, Namespace\n\nfrom pex import pex_bootstrapper\nfrom pex.commands.command import GlobalConfigurationError, Main\nfrom pex.compatibility import commonpath\nfrom pex.pex import PEX\nfrom pex.pex_bootstrapper import InterpreterTest\nfrom pex.pex_info import PexInfo\nfrom pex.result import Result, catch\nfrom pex.tools import commands\nfrom pex.tools.command import PEXCommand\nfrom pex.tracer import TRACER\nfrom pex.typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from typing import Callable, Optional, Union\n\n    CommandFunc = Callable[[PEX, Namespace], Result]\n\n\ndef simplify_pex_path(pex_path):\n    # type: (str) -> str\n    # Generate the most concise path possible that is still cut/paste-able to the command line.\n    pex_path = os.path.abspath(pex_path)\n    cwd = os.getcwd()\n    if commonpath((pex_path, cwd)) == cwd:\n        pex_path = os.path.relpath(pex_path, cwd)\n        # Handle users that do not have . as a PATH entry.\n        if not os.path.dirname(pex_path) and os.curdir not in os.environ.get(\"PATH\", \"\").split(\n            os.pathsep\n        ):\n            pex_path = os.path.join(os.curdir, pex_path)\n    return pex_path\n\n\nclass PexTools(Main[PEXCommand]):\n    def __init__(self, pex=None):\n        # type: (Optional[PEX]) -> None\n\n        pex_prog_path = simplify_pex_path(pex.path()) if pex else None\n\n        # By default, let argparse derive prog from sys.argv[0].\n        prog = None  # type: Optional[str]\n        if pex:\n            prog = \"PEX_TOOLS=1 {pex_path}\".format(pex_path=pex_prog_path)\n\n        description = \"Tools for working with {}.\".format(pex_prog_path if pex else \"PEX files\")\n        subparsers_description = (\n            \"{} can be operated on using any of the following subcommands.\".format(\n                \"The PEX file {}\".format(pex_prog_path) if pex else \"A PEX file\"\n            )\n        )\n\n        super(PexTools, self).__init__(\n            description=description,\n            subparsers_description=subparsers_description,\n            command_types=commands.all_commands(),\n            prog=prog,\n        )\n        self._pex = pex\n\n    def add_arguments(self, parser):\n        # type: (ArgumentParser) -> None\n        if self._pex is None:\n            parser.add_argument(\n                \"pex\", nargs=1, metavar=\"PATH\", help=\"The path of the PEX file to operate on.\"\n            )\n\n\ndef main(pex=None):\n    # type: (Optional[PEX]) -> Union[int, str]\n\n    pex_tools = PexTools(pex=pex)\n    try:\n        with pex_tools.parsed_command() as pex_command, TRACER.timed(\n            \"Executing PEX_TOOLS {}\".format(pex_command.name())\n        ):\n            if pex is None:\n                pex_file_path = pex_command.options.pex[0]\n                pex_info = PexInfo.from_pex(pex_file_path)\n                pex_info.update(PexInfo.from_env())\n                interpreter = pex_bootstrapper.find_compatible_interpreter(\n                    interpreter_test=InterpreterTest(entry_point=pex_file_path, pex_info=pex_info)\n                )\n                pex = PEX(pex_file_path, interpreter=interpreter)\n\n            result = catch(pex_command.run, pex)\n            result.maybe_display()\n            return result.exit_code\n    except GlobalConfigurationError as e:\n        return str(e)\n", "path": "pex/tools/main.py"}]}