internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: pex-tool/pex

Issue:
index urls without trailing / are improperly handled
given `https://user:pass@my.python-index.com/simple` pex 1.0.0dev0 fails to construct the appropriate url for packages in the index.

Expected:

```
--extra-index-url https://user:pass@python-pkgs.ernest.ly/simple
my-private-dep==1.0.0
```

should search `https://user:pass@python-pkgs.ernest.ly/simple/my-private-dep` for version matching 1.0.0

instead, it searches `https://user:pass@python-pkgs.ernest.ly/my-private-dep`



Discussion:

Resolving patch:
diff --git a/pex/fetcher.py b/pex/fetcher.py
--- a/pex/fetcher.py
+++ b/pex/fetcher.py
@@ -47,6 +47,9 @@ def __init__(self, pypi_base=PYPI_BASE, use_mirrors=False):
     if use_mirrors:
       warnings.warn('use_mirrors is now deprecated.')
 
+    if not pypi_base.endswith('/'):
+      pypi_base += '/'
+
     pypi_url = urlparse.urlparse(pypi_base)
     if not pypi_url.scheme:
       self._pypi_base = 'http://' + pypi_base


Test patch:
diff --git a/tests/test_fetcher.py b/tests/test_fetcher.py
new file mode 100644
--- /dev/null
+++ b/tests/test_fetcher.py
@@ -0,0 +1,18 @@
+# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
+# Licensed under the Apache License, Version 2.0 (see LICENSE).
+
+from pex.fetcher import PyPIFetcher
+
+
+def test_pypifetcher():
+  fetcher = PyPIFetcher('https://pypi.python.org/simple')
+  assert fetcher._pypi_base == 'https://pypi.python.org/simple/'
+  assert fetcher.urls('setuptools') == ['https://pypi.python.org/simple/setuptools/']
+
+  fetcher = PyPIFetcher()
+  assert fetcher._pypi_base == 'https://pypi.python.org/simple/'
+  assert fetcher.urls('setuptools') == ['https://pypi.python.org/simple/setuptools/']
+
+  fetcher = PyPIFetcher('file:///srv/simple')
+  assert fetcher._pypi_base == 'file:///srv/simple/'
+  assert fetcher.urls('setuptools') == ['file:///srv/simple/setuptools/']
Repository: pex-tool/pex

Issue:
ValueError: list.remove(x): x not in list
Something must be broken with PEX_IGNORE_ERRORS

```
$ pex flask -o /tmp/flask.pex
$ PEX_PYTHON=pypy /tmp/flask.pex
Traceback (most recent call last):
  File "/tmp/flask.pex/.bootstrap/_pex/pex.py", line 311, in execute
    working_set = self._activate()
  File "/tmp/flask.pex/.bootstrap/_pex/pex.py", line 76, in _activate
    for dist in env.activate():
  File "/tmp/flask.pex/.bootstrap/_pex/environment.py", line 132, in activate
    self._working_set = self._activate()
  File "/tmp/flask.pex/.bootstrap/_pex/environment.py", line 179, in _activate
    resolved = self._resolve(working_set, all_reqs)
  File "/tmp/flask.pex/.bootstrap/_pex/environment.py", line 150, in _resolve
    reqs.remove(unresolved_req)
ValueError: list.remove(x): x not in list

```



Discussion:

Resolving patch:
diff --git a/pex/environment.py b/pex/environment.py
--- a/pex/environment.py
+++ b/pex/environment.py
@@ -120,7 +120,7 @@ def __init__(self, pex, pex_info, interpreter=None, **kw):
   def update_candidate_distributions(self, distribution_iter):
     for dist in distribution_iter:
       if self.can_add(dist):
-        with TRACER.timed('Adding %s:%s' % (dist, dist.location), V=2):
+        with TRACER.timed('Adding %s' % dist, V=2):
           self.add(dist)
 
   def can_add(self, dist):
@@ -136,20 +136,22 @@ def activate(self):
 
   def _resolve(self, working_set, reqs):
     reqs = reqs[:]
-    unresolved_reqs = []
+    unresolved_reqs = set()
+    resolveds = set()
 
-    while True:
-      with TRACER.timed('Resolving %s' %
-          ' '.join(map(str, reqs)) if reqs else 'empty dependency list', V=2):
+    # Resolve them one at a time so that we can figure out which ones we need to elide should
+    # there be an interpreter incompatibility.
+    for req in reqs:
+      with TRACER.timed('Resolving %s' % req, V=2):
         try:
-          resolved = working_set.resolve(reqs, env=self)
+          resolveds.update(working_set.resolve([req], env=self))
         except DistributionNotFound as e:
           TRACER.log('Failed to resolve a requirement: %s' % e)
-          unresolved_req = e.args[0]
-          unresolved_reqs.append(unresolved_req)
-          reqs.remove(unresolved_req)
-        else:
-          break
+          unresolved_reqs.add(e.args[0].project_name)
+          if e.args[1]:
+            unresolved_reqs.update(e.args[1])
+
+    unresolved_reqs = set([req.lower() for req in unresolved_reqs])
 
     if unresolved_reqs:
       TRACER.log('Unresolved requirements:')
@@ -165,7 +167,7 @@ def _resolve(self, working_set, reqs):
         die('Failed to execute PEX file, missing compatible dependencies for:\n%s' % (
             '\n'.join(map(str, unresolved_reqs))))
 
-    return resolved
+    return resolveds
 
   def _activate(self):
     self.update_candidate_distributions(self.load_internal_cache(self._pex, self._pex_info))


Test patch:
Repository: pex-tool/pex

Issue:
'pex <dir>' doesn't actually work unless an .egg-info is already there
It turns out that pex for some reason generates an .egg-info missing setup.py, which causes 'pex .' to fail in a fresh clone.

``` bash
mba=flask=; ~/clients/pex/dist/pex -v -v -v -v -v .
**** Failed to install Flask-0.11.dev0. stdout:

**** Failed to install Flask-0.11.dev0. stderr:
Traceback (most recent call last):
  File "<stdin>", line 6, in <module>
IOError: [Errno 2] No such file or directory: 'setup.py'
```



Discussion:
this is a tiny diff

``` diff
diff --git a/pex/installer.py b/pex/installer.py
index 38958a6..acb0bf0 100644
--- a/pex/installer.py
+++ b/pex/installer.py
@@ -35,6 +35,7 @@ class InstallerBase(object):
   SETUP_BOOTSTRAP_MODULE = "sys.path.insert(0, %(path)r); import %(module)s"
   SETUP_BOOTSTRAP_FOOTER = """
 __file__ = 'setup.py'
+sys.argv[0] = 'setup.py'
 exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
 """
```

but instead of pushing a quick fix, going to spend a little time on a proper integration test for this case.


Resolving patch:
diff --git a/pex/installer.py b/pex/installer.py
--- a/pex/installer.py
+++ b/pex/installer.py
@@ -35,6 +35,7 @@ class InstallerBase(object):
   SETUP_BOOTSTRAP_MODULE = "sys.path.insert(0, %(path)r); import %(module)s"
   SETUP_BOOTSTRAP_FOOTER = """
 __file__ = 'setup.py'
+sys.argv[0] = 'setup.py'
 exec(compile(open(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
 """
 


Test patch:
diff --git a/pex/testing.py b/pex/testing.py
--- a/pex/testing.py
+++ b/pex/testing.py
@@ -79,9 +79,6 @@ def write_zipfile(directory, dest, reverse=False):
           package_data={'my_package': ['package_data/*.dat']},
       )
   '''),
-  'MANIFEST.in': dedent('''
-  include setup.py
-  '''),
   'scripts/hello_world': '#!/usr/bin/env python\nprint("hello world!")\n',
   'scripts/shell_script': '#!/usr/bin/env bash\necho hello world\n',
   'my_package/__init__.py': 0,
Repository: pex-tool/pex

Issue:
pex swallows SystemExit output
docopt is a command-line parsing library. If a user presents an invalid command-line, docopt raises  SystemExit with help output. (https://github.com/docopt/docopt/blob/master/docopt.py#L22)

Since https://rbcommons.com/s/twitter/r/276/diff/1#index_header, pex swallows this output, leading to no output on the command-line. This severely restricts the usefulness of docopt when used with pex files. 

```
$ cat sysexit.py 
raise SystemExit('whoops')

$ python sysexit.py 
whoops # exit 1

Current pex:
$ pex -o sysexit.pex -m sysexit  ; ./sysexit.pex
<nothing>  # exit 1
```

See the attached pull request, which does:

```
Patched pex:
$ pex -o sysexit.pex -m sysexit  ; ./sysexit.pex
whoops  # exit 1
```

For full context:

```
$ cat simple.py 
"""
Usage:
    simple.py FOO
Arguments
    FOO    it's just a foo...
"""
from __future__ import print_function

from docopt import docopt

def main(args):
    print('I got to main()!')

if __name__ == '__main__':
    args = docopt(__doc__)
    main(args)
```

```
$ pex docopt -o ./simple.pex -m simple
$ ./simple.pex 
Usage:
    simple.py FOO
$ ./simple.pex asdf
I got to main()!
```



Discussion:

Resolving patch:
diff --git a/pex/pex.py b/pex/pex.py
--- a/pex/pex.py
+++ b/pex/pex.py
@@ -320,6 +320,12 @@ def execute(self):
       # finally, then reraise so that the exit status is reflected correctly.
       sys.excepthook(*sys.exc_info())
       raise
+    except SystemExit as se:
+      # Print a SystemExit error message, avoiding a traceback in python3.
+      # This must happen here, as sys.stderr is about to be torn down
+      if not isinstance(se.code, int):
+        print(se.code, file=sys.stderr)
+      raise
     finally:
       # squash all exceptions on interpreter teardown -- the primary type here are
       # atexit handlers failing to run because of things such as:


Test patch:
diff --git a/tests/test_pex.py b/tests/test_pex.py
--- a/tests/test_pex.py
+++ b/tests/test_pex.py
@@ -7,6 +7,7 @@
 
 import pytest
 
+from pex.compatibility import to_bytes
 from pex.installer import EggInstaller, WheelInstaller
 from pex.pex import PEX
 from pex.testing import make_installer, run_simple_pex_test
@@ -39,11 +40,24 @@ def excepthook(ex_type, ex, tb):
   assert rc == 42
 
 
-def test_pex_sys_exit_does_not_raise():
-  body = "import sys; sys.exit(2)"
+def _test_sys_exit(arg, expected_output, expected_rc):
+  body = "import sys; sys.exit({arg})".format(arg=arg)
   so, rc = run_simple_pex_test(body)
-  assert so == b'', 'Should not print SystemExit exception.'
-  assert rc == 2
+  assert so == expected_output, 'Should not print SystemExit traceback.'
+  assert rc == expected_rc
+
+
+def test_pex_sys_exit_does_not_print_for_numeric_value():
+  _test_sys_exit(2, b'', 2)
+
+
+def test_pex_sys_exit_prints_non_numeric_value_no_traceback():
+  text = 'something went wrong'
+
+  sys_exit_arg = '"' + text + '"'
+  # encode the string somehow that's compatible with 2 and 3
+  expected_output = to_bytes(text) + b'\n'
+  _test_sys_exit(sys_exit_arg, expected_output, 1)
 
 
 @pytest.mark.skipif('hasattr(sys, "pypy_version_info")')
Repository: pex-tool/pex

Issue:
the pex cli doesn't return the correct exit code
exit.py:

```
    import sys
    sys.exit(1)
```

pex:

```
    $ python exit.py
    $ echo $?
    1

    $ pex -- exit.py
    $ echo $?
    0
```



Discussion:

Resolving patch:
diff --git a/pex/bin/pex.py b/pex/bin/pex.py
--- a/pex/bin/pex.py
+++ b/pex/bin/pex.py
@@ -523,7 +523,7 @@ def main():
 
     log('Running PEX file at %s with args %s' % (pex_builder.path(), cmdline), v=options.verbosity)
     pex = PEX(pex_builder.path(), interpreter=pex_builder.interpreter)
-    return pex.run(args=list(cmdline))
+    sys.exit(pex.run(args=list(cmdline)))
 
 
 if __name__ == '__main__':


Test patch:
Repository: pex-tool/pex

Issue:
Readthedocs versions don't match up with delivered versions
http://pex.readthedocs.org/en/latest/ has two versions:
- latest: docs for 0.7.0-rc0
- stable: does not exist

Latest should probably point to 1.0.



Discussion:

Resolving patch:
diff --git a/docs/conf.py b/docs/conf.py
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -12,14 +12,17 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys
 import os
+import sys
+from datetime import datetime
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
 sys.path.insert(0, os.path.abspath('..'))
 
+from pex.version import __version__ as PEX_VERSION
+
 # -- General configuration ------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
@@ -47,16 +50,17 @@
 
 # General information about the project.
 project = u'pex'
-copyright = u'2014, Pants project contributors'
+copyright = u'%s, Pants project contributors' % datetime.now().year
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = '0.7'
+version = '.'.join(PEX_VERSION.split('.')[0:2])
+
 # The full version, including alpha/beta/rc tags.
-release = '0.7.0-rc0'
+release = PEX_VERSION
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


Test patch:
Repository: pex-tool/pex

Issue:
set user-agent to something pex-identifiable
Donald did some interesting research into where downloads were coming from on PyPI.  Realized that we have no custom user-agent to identify pex.



Discussion:

Resolving patch:
diff --git a/pex/http.py b/pex/http.py
--- a/pex/http.py
+++ b/pex/http.py
@@ -13,6 +13,7 @@
 from .compatibility import PY3, AbstractClass
 from .tracer import TRACER
 from .variables import ENV
+from .version import __version__ as PEX_VERSION
 
 try:
   import requests
@@ -185,6 +186,7 @@ def close(self):
 
 class RequestsContext(Context):
   """A requests-based Context."""
+  USER_AGENT = 'pex/%s' % PEX_VERSION
 
   @staticmethod
   def _create_session(max_retries):
@@ -192,7 +194,6 @@ def _create_session(max_retries):
     retrying_adapter = requests.adapters.HTTPAdapter(max_retries=max_retries)
     session.mount('http://', retrying_adapter)
     session.mount('https://', retrying_adapter)
-
     return session
 
   def __init__(self, session=None, verify=True, env=ENV):
@@ -214,7 +215,9 @@ def open(self, link):
       return open(link.path, 'rb')  # noqa: T802
     for attempt in range(self._max_retries + 1):
       try:
-        return StreamFilelike(self._session.get(link.url, verify=self._verify, stream=True), link)
+        return StreamFilelike(self._session.get(
+            link.url, verify=self._verify, stream=True, headers={'User-Agent': self.USER_AGENT}),
+            link)
       except requests.exceptions.ReadTimeout:
         # Connect timeouts are handled by the HTTPAdapter, unfortunately read timeouts are not
         # so we'll retry them ourselves.


Test patch:
Repository: pex-tool/pex

Issue:
PEX_PROFILE_FILENAME not respected
https://github.com/pantsbuild/pex/commit/c69213893bdbe7aa1648799d9d1a94753748942c changed _execute s.t. it blows away the PEX_\* env vars, but the wrap profile code attempts to read them after.

PEX_PROFILE_FILENAME not respected
https://github.com/pantsbuild/pex/commit/c69213893bdbe7aa1648799d9d1a94753748942c changed _execute s.t. it blows away the PEX_\* env vars, but the wrap profile code attempts to read them after.



Discussion:

Resolving patch:
diff --git a/pex/pex.py b/pex/pex.py
--- a/pex/pex.py
+++ b/pex/pex.py
@@ -282,6 +282,8 @@ def _wrap_profiling(self, runner, *args):
       runner(*args)
       return
 
+    pex_profile_filename = self._vars.PEX_PROFILE_FILENAME
+    pex_profile_sort = self._vars.PEX_PROFILE_SORT
     try:
       import cProfile as profile
     except ImportError:
@@ -292,10 +294,10 @@ def _wrap_profiling(self, runner, *args):
     try:
       return profiler.runcall(runner, *args)
     finally:
-      if self._vars.PEX_PROFILE_FILENAME is not None:
-        profiler.dump_stats(self._vars.PEX_PROFILE_FILENAME)
+      if pex_profile_filename is not None:
+        profiler.dump_stats(pex_profile_filename)
       else:
-        profiler.print_stats(sort=self._vars.PEX_PROFILE_SORT)
+        profiler.print_stats(sort=pex_profile_sort)
 
   def execute(self):
     """Execute the PEX.


Test patch:
Repository: pex-tool/pex

Issue:
`py27-package` tox target broken.
symptom:

```
$ tox -e py27-package
GLOB sdist-make: /Users/billg/ws/git/pex/setup.py
py27-package inst-nodeps: /Users/billg/ws/git/pex/.tox/dist/pex-1.1.6.zip
py27-package installed: funcsigs==1.0.2,mock==2.0.0,pbr==1.9.1,pex==1.1.6,py==1.4.31,pytest==2.9.1,six==1.10.0,twitter.common.contextutil==0.3.4,twitter.common.dirutil==0.3.4,twitter.common.lang==0.3.4,twitter.common.testing==0.3.4
py27-package runtests: PYTHONHASHSEED='374850053'
py27-package runtests: commands[0] | pex --cache-dir /Users/billg/ws/git/pex/.tox/py27-package/tmp/buildcache wheel requests . -o dist/pex27 -e pex.bin.pex:main -v
Traceback (most recent call last):
  File ".tox/py27-package/bin/pex", line 11, in <module>
    sys.exit(main())
TypeError: main() takes exactly 1 argument (0 given)
ERROR: InvocationError: '/Users/billg/ws/git/pex/.tox/py27-package/bin/pex --cache-dir /Users/billg/ws/git/pex/.tox/py27-package/tmp/buildcache wheel requests . -o dist/pex27 -e pex.bin.pex:main -v'
________________________________________________________________________________________ summary _________________________________________________________________________________________
ERROR:   py27-package: commands failed
```

which I believe relates to the change of the main() function signature in: https://github.com/pantsbuild/pex/commit/2cfcb7a3d53f08618b98ff19b609ac98aa5bc528

jumping to `git checkout 2cfcb7^` allows the tox command to proceed.



Discussion:

Resolving patch:
diff --git a/pex/bin/pex.py b/pex/bin/pex.py
--- a/pex/bin/pex.py
+++ b/pex/bin/pex.py
@@ -514,7 +514,8 @@ def make_relative_to_root(path):
   return os.path.normpath(path.format(pex_root=ENV.PEX_ROOT))
 
 
-def main(args):
+def main(args=None):
+  args = args or sys.argv[1:]
   parser, resolver_options_builder = configure_clp()
 
   try:
@@ -555,5 +556,4 @@ def main(args):
 
 
 if __name__ == '__main__':
-  # split arguments early because optparse is dumb
-  main(sys.argv[1:])
+  main()


Test patch:
Repository: pex-tool/pex

Issue:
Update PyPI page
It would be nice if the `README.rst` were included in the `setup.py` `long_description` in addition to the `CHANGES.rst` so that users browsing PyPI could read the README without having to travel to GitHub.

Would also be nice if the trove classifiers in `setup.py` reflected which versions of Python were officially supported (e.g. `'Programming Language :: Python :: 3.5'`).



Discussion:
SGTM, happy to review your pull request(s) if you have time.


Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -5,9 +5,11 @@
 
 from setuptools import setup
 
+with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as fp:
+  LONG_DESCRIPTION = fp.read() + '\n'
 
 with open(os.path.join(os.path.dirname(__file__), 'CHANGES.rst')) as fp:
-  LONG_DESCRIPTION = fp.read()
+  LONG_DESCRIPTION += fp.read()
 
 
 # This seems to be a fairly standard version file pattern.
@@ -33,8 +35,17 @@
   classifiers = [
     'Intended Audience :: Developers',
     'License :: OSI Approved :: Apache Software License',
-    'Operating System :: OS Independent',
+    'Operating System :: Unix',
+    'Operating System :: POSIX :: Linux',
+    'Operating System :: MacOS :: MacOS X',
     'Programming Language :: Python',
+    'Programming Language :: Python :: 2',
+    'Programming Language :: Python :: 2.6',
+    'Programming Language :: Python :: 2.7',
+    'Programming Language :: Python :: 3',
+    'Programming Language :: Python :: 3.3',
+    'Programming Language :: Python :: 3.4',
+    'Programming Language :: Python :: 3.5',
   ],
   packages = [
     'pex',


Test patch:
Repository: pex-tool/pex

Issue:
later versions of setuptools add new wheel finders
yay!  this may obviate the need for pex/finders.py, except now the following breaks:
https://github.com/pantsbuild/pex/blob/85ef56ad/pex/util.py#L93

the reason: now both the monkeypatched finder provided by pex and the new setuptools finder find a distribution in the pex cache, thus len(distributions) == 2 even though the distributions are identical.  for whatever reason, pex throws up its hands and returns None instead, causing PEXEnvironment.update_candidate_distributions to barf.

i have no idea why the code was written the way it was (senility has crept in), but if you replace list() with set() in order to dedupe and return distributions.pop(), things work regardless of setuptools version.



Discussion:

Resolving patch:
diff --git a/pex/environment.py b/pex/environment.py
--- a/pex/environment.py
+++ b/pex/environment.py
@@ -82,17 +82,20 @@ def write_zipped_internal_cache(cls, pex, pex_info):
           distribution_name, dist_digest))
         if os.path.exists(cached_location):
           dist = DistributionHelper.distribution_from_path(cached_location)
-          existing_cached_distributions.append(dist)
-          continue
+          if dist is not None:
+            existing_cached_distributions.append(dist)
+            continue
         else:
           dist = DistributionHelper.distribution_from_path(os.path.join(pex, internal_dist_path))
-          if DistributionHelper.zipsafe(dist) and not pex_info.always_write_cache:
-            zip_safe_distributions.append(dist)
-            continue
+          if dist is not None:
+            if DistributionHelper.zipsafe(dist) and not pex_info.always_write_cache:
+              zip_safe_distributions.append(dist)
+              continue
 
         with TRACER.timed('Caching %s' % dist):
           newly_cached_distributions.append(
             CacheHelper.cache_distribution(zf, internal_dist_path, cached_location))
+
     return existing_cached_distributions, newly_cached_distributions, zip_safe_distributions
 
   @classmethod
diff --git a/pex/util.py b/pex/util.py
--- a/pex/util.py
+++ b/pex/util.py
@@ -89,9 +89,9 @@ def distribution_from_path(cls, path, name=None):
     # Monkeypatch pkg_resources finders should it not already be so.
     register_finders()
     if name is None:
-      distributions = list(find_distributions(path))
+      distributions = set(find_distributions(path))
       if len(distributions) == 1:
-        return distributions[0]
+        return distributions.pop()
     else:
       for dist in find_distributions(path):
         if dist.project_name == name:


Test patch:
Repository: pex-tool/pex

Issue:
Remove pkg_resources.build_zipmanifest monkeypatching
This may involve increasing the minimum setuptools version.  Another alternative is vendoring setuptools.



Discussion:
From pypa/setuptools#154, it appears the reason for the monkeypatching was fixed in setuptools 5.4, so setting the minimum version to at least that should do the trick.

Resolving patch:
diff --git a/pex/pex_bootstrapper.py b/pex/pex_bootstrapper.py
--- a/pex/pex_bootstrapper.py
+++ b/pex/pex_bootstrapper.py
@@ -46,20 +46,6 @@ def get_pex_info(entry_point):
   raise ValueError('Invalid entry_point: %s' % entry_point)
 
 
-# TODO(wickman) Remove once resolved (#91):
-#   https://bitbucket.org/pypa/setuptools/issue/154/build_zipmanifest-results-should-be
-def monkeypatch_build_zipmanifest():
-  import pkg_resources
-  if not hasattr(pkg_resources, 'build_zipmanifest'):
-    return
-  old_build_zipmanifest = pkg_resources.build_zipmanifest
-  def memoized_build_zipmanifest(archive, memo={}):
-    if archive not in memo:
-      memo[archive] = old_build_zipmanifest(archive)
-    return memo[archive]
-  pkg_resources.build_zipmanifest = memoized_build_zipmanifest
-
-
 def find_in_path(target_interpreter):
   if os.path.exists(target_interpreter):
     return target_interpreter
@@ -90,7 +76,6 @@ def maybe_reexec_pex():
 
 def bootstrap_pex(entry_point):
   from .finders import register_finders
-  monkeypatch_build_zipmanifest()
   register_finders()
   maybe_reexec_pex()
 
@@ -104,7 +89,6 @@ def bootstrap_pex_env(entry_point):
   from .finders import register_finders
   from .pex_info import PexInfo
 
-  monkeypatch_build_zipmanifest()
   register_finders()
 
   PEXEnvironment(entry_point, PexInfo.from_pex(entry_point)).activate()
diff --git a/pex/version.py b/pex/version.py
--- a/pex/version.py
+++ b/pex/version.py
@@ -3,5 +3,5 @@
 
 __version__ = '1.1.15'
 
-SETUPTOOLS_REQUIREMENT = 'setuptools>=2.2,<20.11'
+SETUPTOOLS_REQUIREMENT = 'setuptools>=5.7,<20.11'
 WHEEL_REQUIREMENT = 'wheel>=0.26.0,<0.30.0'


Test patch: