We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
pex 1.5.1 bombs with unhandled `pkg_resources._vendor.packaging.markers.UndefinedEnvironmentName` on certain resolves
repro:

```
[omerta show]$ wget -q https://github.com/pantsbuild/pex/releases/download/v1.5.1/pex27 -O pex27-1.5.1
[omerta show]$ ./pex27-1.5.1 apache-beam[gcp]==2.7.0
Traceback (most recent call last):
  File "/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/_pex/pex.py", line 349, in execute
    working_set = self._activate()
  File "/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/_pex/pex.py", line 90, in _activate
    for dist in env.activate():
  File "/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/_pex/environment.py", line 152, in activate
    self._working_set = self._activate()
  File "/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/_pex/environment.py", line 211, in _activate
    resolved = self._resolve(working_set, all_reqs)
  File "/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/_pex/environment.py", line 165, in _resolve
    if req.marker and not req.marker.evaluate(environment=self._target_interpreter_env):
  File "/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/pkg_resources/_vendor/packaging/markers.py", line 301, in evaluate
    return _evaluate_markers(self._markers, current_environment)
  File "/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/pkg_resources/_vendor/packaging/markers.py", line 226, in _evaluate_markers
    lhs_value = _get_env(environment, lhs.value)
  File "/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/pkg_resources/_vendor/packaging/markers.py", line 208, in _get_env
    "{0!r} does not exist in evaluation environment.".format(name)
pkg_resources._vendor.packaging.markers.UndefinedEnvironmentName: 'extra' does not exist in evaluation environment.
```

vs 1.4.4:

```
[omerta show]$ wget -q https://github.com/pantsbuild/pex/releases/download/v1.4.4/pex27 -O pex27-1.4.4
[omerta show]$ ./pex27-1.4.4 apache-beam[gcp]==2.7.0
Python 2.7.15 (default, Jun 20 2018, 17:40:09) 
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 
```

--- 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/environment.py`
Content:
```
1 # Copyright 2014 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 itertools
7 import os
8 import site
9 import sys
10 import uuid
11 
12 from pkg_resources import (
13     DistributionNotFound,
14     Environment,
15     Requirement,
16     WorkingSet,
17     find_distributions
18 )
19 
20 from .common import die, open_zip, rename_if_empty, safe_mkdir, safe_rmtree
21 from .interpreter import PythonInterpreter
22 from .package import distribution_compatible
23 from .pex_builder import PEXBuilder
24 from .pex_info import PexInfo
25 from .platforms import Platform
26 from .tracer import TRACER
27 from .util import CacheHelper, DistributionHelper
28 
29 
30 class PEXEnvironment(Environment):
31   @classmethod
32   def force_local(cls, pex, pex_info):
33     if pex_info.code_hash is None:
34       # Do not support force_local if code_hash is not set. (It should always be set.)
35       return pex
36     explode_dir = os.path.join(pex_info.zip_unsafe_cache, pex_info.code_hash)
37     TRACER.log('PEX is not zip safe, exploding to %s' % explode_dir)
38     if not os.path.exists(explode_dir):
39       explode_tmp = explode_dir + '.' + uuid.uuid4().hex
40       with TRACER.timed('Unzipping %s' % pex):
41         try:
42           safe_mkdir(explode_tmp)
43           with open_zip(pex) as pex_zip:
44             pex_files = (x for x in pex_zip.namelist()
45                          if not x.startswith(PEXBuilder.BOOTSTRAP_DIR) and
46                             not x.startswith(PexInfo.INTERNAL_CACHE))
47             pex_zip.extractall(explode_tmp, pex_files)
48         except:  # noqa: T803
49           safe_rmtree(explode_tmp)
50           raise
51       TRACER.log('Renaming %s to %s' % (explode_tmp, explode_dir))
52       rename_if_empty(explode_tmp, explode_dir)
53     return explode_dir
54 
55   @classmethod
56   def update_module_paths(cls, new_code_path):
57     # Force subsequent imports to come from the .pex directory rather than the .pex file.
58     TRACER.log('Adding to the head of sys.path: %s' % new_code_path)
59     sys.path.insert(0, new_code_path)
60     for name, module in sys.modules.items():
61       if hasattr(module, "__path__"):
62         module_dir = os.path.join(new_code_path, *name.split("."))
63         TRACER.log('Adding to the head of %s.__path__: %s' % (module.__name__, module_dir))
64         module.__path__.insert(0, module_dir)
65 
66   @classmethod
67   def write_zipped_internal_cache(cls, pex, pex_info):
68     prefix_length = len(pex_info.internal_cache) + 1
69     existing_cached_distributions = []
70     newly_cached_distributions = []
71     zip_safe_distributions = []
72     with open_zip(pex) as zf:
73       # Distribution names are the first element after ".deps/" and before the next "/"
74       distribution_names = set(filter(None, (filename[prefix_length:].split('/')[0]
75           for filename in zf.namelist() if filename.startswith(pex_info.internal_cache))))
76       # Create Distribution objects from these, and possibly write to disk if necessary.
77       for distribution_name in distribution_names:
78         internal_dist_path = '/'.join([pex_info.internal_cache, distribution_name])
79         # First check if this is already cached
80         dist_digest = pex_info.distributions.get(distribution_name) or CacheHelper.zip_hash(
81             zf, internal_dist_path)
82         cached_location = os.path.join(pex_info.install_cache, '%s.%s' % (
83           distribution_name, dist_digest))
84         if os.path.exists(cached_location):
85           dist = DistributionHelper.distribution_from_path(cached_location)
86           if dist is not None:
87             existing_cached_distributions.append(dist)
88             continue
89         else:
90           dist = DistributionHelper.distribution_from_path(os.path.join(pex, internal_dist_path))
91           if dist is not None:
92             if DistributionHelper.zipsafe(dist) and not pex_info.always_write_cache:
93               zip_safe_distributions.append(dist)
94               continue
95 
96         with TRACER.timed('Caching %s' % dist):
97           newly_cached_distributions.append(
98             CacheHelper.cache_distribution(zf, internal_dist_path, cached_location))
99 
100     return existing_cached_distributions, newly_cached_distributions, zip_safe_distributions
101 
102   @classmethod
103   def load_internal_cache(cls, pex, pex_info):
104     """Possibly cache out the internal cache."""
105     internal_cache = os.path.join(pex, pex_info.internal_cache)
106     with TRACER.timed('Searching dependency cache: %s' % internal_cache, V=2):
107       if os.path.isdir(pex):
108         for dist in find_distributions(internal_cache):
109           yield dist
110       else:
111         for dist in itertools.chain(*cls.write_zipped_internal_cache(pex, pex_info)):
112           yield dist
113 
114   def __init__(self, pex, pex_info, interpreter=None, **kw):
115     self._internal_cache = os.path.join(pex, pex_info.internal_cache)
116     self._pex = pex
117     self._pex_info = pex_info
118     self._activated = False
119     self._working_set = None
120     self._interpreter = interpreter or PythonInterpreter.get()
121     self._inherit_path = pex_info.inherit_path
122     self._supported_tags = []
123 
124     platform = Platform.current()
125     platform_name = platform.platform
126     super(PEXEnvironment, self).__init__(
127       search_path=[] if pex_info.inherit_path == 'false' else sys.path,
128       # NB: Our pkg_resources.Environment base-class wants the platform name string and not the
129       # pex.platform.Platform object.
130       platform=platform_name,
131       **kw
132     )
133     self._target_interpreter_env = self._interpreter.identity.pkg_resources_env(platform_name)
134     self._supported_tags.extend(platform.supported_tags(self._interpreter))
135     TRACER.log(
136       'E: tags for %r x %r -> %s' % (self.platform, self._interpreter, self._supported_tags),
137       V=9
138     )
139 
140   def update_candidate_distributions(self, distribution_iter):
141     for dist in distribution_iter:
142       if self.can_add(dist):
143         with TRACER.timed('Adding %s' % dist, V=2):
144           self.add(dist)
145 
146   def can_add(self, dist):
147     return distribution_compatible(dist, self._supported_tags)
148 
149   def activate(self):
150     if not self._activated:
151       with TRACER.timed('Activating PEX virtual environment from %s' % self._pex):
152         self._working_set = self._activate()
153       self._activated = True
154 
155     return self._working_set
156 
157   def _resolve(self, working_set, reqs):
158     reqs = reqs[:]
159     unresolved_reqs = set()
160     resolveds = set()
161 
162     # Resolve them one at a time so that we can figure out which ones we need to elide should
163     # there be an interpreter incompatibility.
164     for req in reqs:
165       if req.marker and not req.marker.evaluate(environment=self._target_interpreter_env):
166         TRACER.log('Skipping activation of `%s` due to environment marker de-selection' % req)
167         continue
168       with TRACER.timed('Resolving %s' % req, V=2):
169         try:
170           resolveds.update(working_set.resolve([req], env=self))
171         except DistributionNotFound as e:
172           TRACER.log('Failed to resolve a requirement: %s' % e)
173           unresolved_reqs.add(e.args[0].project_name)
174           # Older versions of pkg_resources just call `DistributionNotFound(req)` instead of the
175           # modern `DistributionNotFound(req, requirers)` and so we may not have the 2nd requirers
176           # slot at all.
177           if len(e.args) >= 2 and e.args[1]:
178             unresolved_reqs.update(e.args[1])
179 
180     unresolved_reqs = set([req.lower() for req in unresolved_reqs])
181 
182     if unresolved_reqs:
183       TRACER.log('Unresolved requirements:')
184       for req in unresolved_reqs:
185         TRACER.log('  - %s' % req)
186       TRACER.log('Distributions contained within this pex:')
187       if not self._pex_info.distributions:
188         TRACER.log('  None')
189       else:
190         for dist in self._pex_info.distributions:
191           TRACER.log('  - %s' % dist)
192       if not self._pex_info.ignore_errors:
193         die(
194           'Failed to execute PEX file, missing %s compatible dependencies for:\n%s' % (
195             Platform.current(),
196             '\n'.join(str(r) for r in unresolved_reqs)
197           )
198         )
199 
200     return resolveds
201 
202   def _activate(self):
203     self.update_candidate_distributions(self.load_internal_cache(self._pex, self._pex_info))
204 
205     if not self._pex_info.zip_safe and os.path.isfile(self._pex):
206       self.update_module_paths(self.force_local(self._pex, self._pex_info))
207 
208     all_reqs = [Requirement.parse(req) for req in self._pex_info.requirements]
209 
210     working_set = WorkingSet([])
211     resolved = self._resolve(working_set, all_reqs)
212 
213     for dist in resolved:
214       with TRACER.timed('Activating %s' % dist, V=2):
215         working_set.add(dist)
216 
217         if os.path.isdir(dist.location):
218           with TRACER.timed('Adding sitedir', V=2):
219             if dist.location not in sys.path and self._inherit_path == "fallback":
220               # Prepend location to sys.path.
221               # This ensures that bundled versions of libraries will be used before system-installed
222               # versions, in case something is installed in both, helping to favor hermeticity in
223               # the case of non-hermetic PEX files (i.e. those with inherit_path=True).
224               #
225               # If the path is not already in sys.path, site.addsitedir will append (not prepend)
226               # the path to sys.path. But if the path is already in sys.path, site.addsitedir will
227               # leave sys.path unmodified, but will do everything else it would do. This is not part
228               # of its advertised contract (which is very vague), but has been verified to be the
229               # case by inspecting its source for both cpython 2.7 and cpython 3.7.
230               sys.path.insert(0, dist.location)
231             site.addsitedir(dist.location)
232 
233         dist.activate()
234 
235     return working_set
236
```
--- 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/environment.py b/pex/environment.py
--- a/pex/environment.py
+++ b/pex/environment.py
@@ -159,10 +159,13 @@
     unresolved_reqs = set()
     resolveds = set()
 
+    environment = self._target_interpreter_env.copy()
+    environment['extra'] = list(set(itertools.chain(*(req.extras for req in reqs))))
+
     # 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:
-      if req.marker and not req.marker.evaluate(environment=self._target_interpreter_env):
+      if req.marker and not req.marker.evaluate(environment=environment):
         TRACER.log('Skipping activation of `%s` due to environment marker de-selection' % req)
         continue
       with TRACER.timed('Resolving %s' % req, V=2):

{"golden_diff": "diff --git a/pex/environment.py b/pex/environment.py\n--- a/pex/environment.py\n+++ b/pex/environment.py\n@@ -159,10 +159,13 @@\n     unresolved_reqs = set()\n     resolveds = set()\n \n+    environment = self._target_interpreter_env.copy()\n+    environment['extra'] = list(set(itertools.chain(*(req.extras for req in reqs))))\n+\n     # Resolve them one at a time so that we can figure out which ones we need to elide should\n     # there be an interpreter incompatibility.\n     for req in reqs:\n-      if req.marker and not req.marker.evaluate(environment=self._target_interpreter_env):\n+      if req.marker and not req.marker.evaluate(environment=environment):\n         TRACER.log('Skipping activation of `%s` due to environment marker de-selection' % req)\n         continue\n       with TRACER.timed('Resolving %s' % req, V=2):\n", "issue": "pex 1.5.1 bombs with unhandled `pkg_resources._vendor.packaging.markers.UndefinedEnvironmentName` on certain resolves\nrepro:\r\n\r\n```\r\n[omerta show]$ wget -q https://github.com/pantsbuild/pex/releases/download/v1.5.1/pex27 -O pex27-1.5.1\r\n[omerta show]$ ./pex27-1.5.1 apache-beam[gcp]==2.7.0\r\nTraceback (most recent call last):\r\n  File \"/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/_pex/pex.py\", line 349, in execute\r\n    working_set = self._activate()\r\n  File \"/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/_pex/pex.py\", line 90, in _activate\r\n    for dist in env.activate():\r\n  File \"/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/_pex/environment.py\", line 152, in activate\r\n    self._working_set = self._activate()\r\n  File \"/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/_pex/environment.py\", line 211, in _activate\r\n    resolved = self._resolve(working_set, all_reqs)\r\n  File \"/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/_pex/environment.py\", line 165, in _resolve\r\n    if req.marker and not req.marker.evaluate(environment=self._target_interpreter_env):\r\n  File \"/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/pkg_resources/_vendor/packaging/markers.py\", line 301, in evaluate\r\n    return _evaluate_markers(self._markers, current_environment)\r\n  File \"/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/pkg_resources/_vendor/packaging/markers.py\", line 226, in _evaluate_markers\r\n    lhs_value = _get_env(environment, lhs.value)\r\n  File \"/private/var/folders/kd/k5b__qgs0gl5yzp48gctmhz80000gn/T/tmp_XY7Uj/.bootstrap/pkg_resources/_vendor/packaging/markers.py\", line 208, in _get_env\r\n    \"{0!r} does not exist in evaluation environment.\".format(name)\r\npkg_resources._vendor.packaging.markers.UndefinedEnvironmentName: 'extra' does not exist in evaluation environment.\r\n```\r\n\r\nvs 1.4.4:\r\n\r\n```\r\n[omerta show]$ wget -q https://github.com/pantsbuild/pex/releases/download/v1.4.4/pex27 -O pex27-1.4.4\r\n[omerta show]$ ./pex27-1.4.4 apache-beam[gcp]==2.7.0\r\nPython 2.7.15 (default, Jun 20 2018, 17:40:09) \r\n[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n(InteractiveConsole)\r\n>>> \r\n```\n", "before_files": [{"content": "# Copyright 2014 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 itertools\nimport os\nimport site\nimport sys\nimport uuid\n\nfrom pkg_resources import (\n    DistributionNotFound,\n    Environment,\n    Requirement,\n    WorkingSet,\n    find_distributions\n)\n\nfrom .common import die, open_zip, rename_if_empty, safe_mkdir, safe_rmtree\nfrom .interpreter import PythonInterpreter\nfrom .package import distribution_compatible\nfrom .pex_builder import PEXBuilder\nfrom .pex_info import PexInfo\nfrom .platforms import Platform\nfrom .tracer import TRACER\nfrom .util import CacheHelper, DistributionHelper\n\n\nclass PEXEnvironment(Environment):\n  @classmethod\n  def force_local(cls, pex, pex_info):\n    if pex_info.code_hash is None:\n      # Do not support force_local if code_hash is not set. (It should always be set.)\n      return pex\n    explode_dir = os.path.join(pex_info.zip_unsafe_cache, pex_info.code_hash)\n    TRACER.log('PEX is not zip safe, exploding to %s' % explode_dir)\n    if not os.path.exists(explode_dir):\n      explode_tmp = explode_dir + '.' + uuid.uuid4().hex\n      with TRACER.timed('Unzipping %s' % pex):\n        try:\n          safe_mkdir(explode_tmp)\n          with open_zip(pex) as pex_zip:\n            pex_files = (x for x in pex_zip.namelist()\n                         if not x.startswith(PEXBuilder.BOOTSTRAP_DIR) and\n                            not x.startswith(PexInfo.INTERNAL_CACHE))\n            pex_zip.extractall(explode_tmp, pex_files)\n        except:  # noqa: T803\n          safe_rmtree(explode_tmp)\n          raise\n      TRACER.log('Renaming %s to %s' % (explode_tmp, explode_dir))\n      rename_if_empty(explode_tmp, explode_dir)\n    return explode_dir\n\n  @classmethod\n  def update_module_paths(cls, new_code_path):\n    # Force subsequent imports to come from the .pex directory rather than the .pex file.\n    TRACER.log('Adding to the head of sys.path: %s' % new_code_path)\n    sys.path.insert(0, new_code_path)\n    for name, module in sys.modules.items():\n      if hasattr(module, \"__path__\"):\n        module_dir = os.path.join(new_code_path, *name.split(\".\"))\n        TRACER.log('Adding to the head of %s.__path__: %s' % (module.__name__, module_dir))\n        module.__path__.insert(0, module_dir)\n\n  @classmethod\n  def write_zipped_internal_cache(cls, pex, pex_info):\n    prefix_length = len(pex_info.internal_cache) + 1\n    existing_cached_distributions = []\n    newly_cached_distributions = []\n    zip_safe_distributions = []\n    with open_zip(pex) as zf:\n      # Distribution names are the first element after \".deps/\" and before the next \"/\"\n      distribution_names = set(filter(None, (filename[prefix_length:].split('/')[0]\n          for filename in zf.namelist() if filename.startswith(pex_info.internal_cache))))\n      # Create Distribution objects from these, and possibly write to disk if necessary.\n      for distribution_name in distribution_names:\n        internal_dist_path = '/'.join([pex_info.internal_cache, distribution_name])\n        # First check if this is already cached\n        dist_digest = pex_info.distributions.get(distribution_name) or CacheHelper.zip_hash(\n            zf, internal_dist_path)\n        cached_location = os.path.join(pex_info.install_cache, '%s.%s' % (\n          distribution_name, dist_digest))\n        if os.path.exists(cached_location):\n          dist = DistributionHelper.distribution_from_path(cached_location)\n          if dist is not None:\n            existing_cached_distributions.append(dist)\n            continue\n        else:\n          dist = DistributionHelper.distribution_from_path(os.path.join(pex, internal_dist_path))\n          if dist is not None:\n            if DistributionHelper.zipsafe(dist) and not pex_info.always_write_cache:\n              zip_safe_distributions.append(dist)\n              continue\n\n        with TRACER.timed('Caching %s' % dist):\n          newly_cached_distributions.append(\n            CacheHelper.cache_distribution(zf, internal_dist_path, cached_location))\n\n    return existing_cached_distributions, newly_cached_distributions, zip_safe_distributions\n\n  @classmethod\n  def load_internal_cache(cls, pex, pex_info):\n    \"\"\"Possibly cache out the internal cache.\"\"\"\n    internal_cache = os.path.join(pex, pex_info.internal_cache)\n    with TRACER.timed('Searching dependency cache: %s' % internal_cache, V=2):\n      if os.path.isdir(pex):\n        for dist in find_distributions(internal_cache):\n          yield dist\n      else:\n        for dist in itertools.chain(*cls.write_zipped_internal_cache(pex, pex_info)):\n          yield dist\n\n  def __init__(self, pex, pex_info, interpreter=None, **kw):\n    self._internal_cache = os.path.join(pex, pex_info.internal_cache)\n    self._pex = pex\n    self._pex_info = pex_info\n    self._activated = False\n    self._working_set = None\n    self._interpreter = interpreter or PythonInterpreter.get()\n    self._inherit_path = pex_info.inherit_path\n    self._supported_tags = []\n\n    platform = Platform.current()\n    platform_name = platform.platform\n    super(PEXEnvironment, self).__init__(\n      search_path=[] if pex_info.inherit_path == 'false' else sys.path,\n      # NB: Our pkg_resources.Environment base-class wants the platform name string and not the\n      # pex.platform.Platform object.\n      platform=platform_name,\n      **kw\n    )\n    self._target_interpreter_env = self._interpreter.identity.pkg_resources_env(platform_name)\n    self._supported_tags.extend(platform.supported_tags(self._interpreter))\n    TRACER.log(\n      'E: tags for %r x %r -> %s' % (self.platform, self._interpreter, self._supported_tags),\n      V=9\n    )\n\n  def update_candidate_distributions(self, distribution_iter):\n    for dist in distribution_iter:\n      if self.can_add(dist):\n        with TRACER.timed('Adding %s' % dist, V=2):\n          self.add(dist)\n\n  def can_add(self, dist):\n    return distribution_compatible(dist, self._supported_tags)\n\n  def activate(self):\n    if not self._activated:\n      with TRACER.timed('Activating PEX virtual environment from %s' % self._pex):\n        self._working_set = self._activate()\n      self._activated = True\n\n    return self._working_set\n\n  def _resolve(self, working_set, reqs):\n    reqs = reqs[:]\n    unresolved_reqs = set()\n    resolveds = set()\n\n    # Resolve them one at a time so that we can figure out which ones we need to elide should\n    # there be an interpreter incompatibility.\n    for req in reqs:\n      if req.marker and not req.marker.evaluate(environment=self._target_interpreter_env):\n        TRACER.log('Skipping activation of `%s` due to environment marker de-selection' % req)\n        continue\n      with TRACER.timed('Resolving %s' % req, V=2):\n        try:\n          resolveds.update(working_set.resolve([req], env=self))\n        except DistributionNotFound as e:\n          TRACER.log('Failed to resolve a requirement: %s' % e)\n          unresolved_reqs.add(e.args[0].project_name)\n          # Older versions of pkg_resources just call `DistributionNotFound(req)` instead of the\n          # modern `DistributionNotFound(req, requirers)` and so we may not have the 2nd requirers\n          # slot at all.\n          if len(e.args) >= 2 and e.args[1]:\n            unresolved_reqs.update(e.args[1])\n\n    unresolved_reqs = set([req.lower() for req in unresolved_reqs])\n\n    if unresolved_reqs:\n      TRACER.log('Unresolved requirements:')\n      for req in unresolved_reqs:\n        TRACER.log('  - %s' % req)\n      TRACER.log('Distributions contained within this pex:')\n      if not self._pex_info.distributions:\n        TRACER.log('  None')\n      else:\n        for dist in self._pex_info.distributions:\n          TRACER.log('  - %s' % dist)\n      if not self._pex_info.ignore_errors:\n        die(\n          'Failed to execute PEX file, missing %s compatible dependencies for:\\n%s' % (\n            Platform.current(),\n            '\\n'.join(str(r) for r in unresolved_reqs)\n          )\n        )\n\n    return resolveds\n\n  def _activate(self):\n    self.update_candidate_distributions(self.load_internal_cache(self._pex, self._pex_info))\n\n    if not self._pex_info.zip_safe and os.path.isfile(self._pex):\n      self.update_module_paths(self.force_local(self._pex, self._pex_info))\n\n    all_reqs = [Requirement.parse(req) for req in self._pex_info.requirements]\n\n    working_set = WorkingSet([])\n    resolved = self._resolve(working_set, all_reqs)\n\n    for dist in resolved:\n      with TRACER.timed('Activating %s' % dist, V=2):\n        working_set.add(dist)\n\n        if os.path.isdir(dist.location):\n          with TRACER.timed('Adding sitedir', V=2):\n            if dist.location not in sys.path and self._inherit_path == \"fallback\":\n              # Prepend location to sys.path.\n              # This ensures that bundled versions of libraries will be used before system-installed\n              # versions, in case something is installed in both, helping to favor hermeticity in\n              # the case of non-hermetic PEX files (i.e. those with inherit_path=True).\n              #\n              # If the path is not already in sys.path, site.addsitedir will append (not prepend)\n              # the path to sys.path. But if the path is already in sys.path, site.addsitedir will\n              # leave sys.path unmodified, but will do everything else it would do. This is not part\n              # of its advertised contract (which is very vague), but has been verified to be the\n              # case by inspecting its source for both cpython 2.7 and cpython 3.7.\n              sys.path.insert(0, dist.location)\n            site.addsitedir(dist.location)\n\n        dist.activate()\n\n    return working_set\n", "path": "pex/environment.py"}], "after_files": [{"content": "# Copyright 2014 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 itertools\nimport os\nimport site\nimport sys\nimport uuid\n\nfrom pkg_resources import (\n    DistributionNotFound,\n    Environment,\n    Requirement,\n    WorkingSet,\n    find_distributions\n)\n\nfrom .common import die, open_zip, rename_if_empty, safe_mkdir, safe_rmtree\nfrom .interpreter import PythonInterpreter\nfrom .package import distribution_compatible\nfrom .pex_builder import PEXBuilder\nfrom .pex_info import PexInfo\nfrom .platforms import Platform\nfrom .tracer import TRACER\nfrom .util import CacheHelper, DistributionHelper\n\n\nclass PEXEnvironment(Environment):\n  @classmethod\n  def force_local(cls, pex, pex_info):\n    if pex_info.code_hash is None:\n      # Do not support force_local if code_hash is not set. (It should always be set.)\n      return pex\n    explode_dir = os.path.join(pex_info.zip_unsafe_cache, pex_info.code_hash)\n    TRACER.log('PEX is not zip safe, exploding to %s' % explode_dir)\n    if not os.path.exists(explode_dir):\n      explode_tmp = explode_dir + '.' + uuid.uuid4().hex\n      with TRACER.timed('Unzipping %s' % pex):\n        try:\n          safe_mkdir(explode_tmp)\n          with open_zip(pex) as pex_zip:\n            pex_files = (x for x in pex_zip.namelist()\n                         if not x.startswith(PEXBuilder.BOOTSTRAP_DIR) and\n                            not x.startswith(PexInfo.INTERNAL_CACHE))\n            pex_zip.extractall(explode_tmp, pex_files)\n        except:  # noqa: T803\n          safe_rmtree(explode_tmp)\n          raise\n      TRACER.log('Renaming %s to %s' % (explode_tmp, explode_dir))\n      rename_if_empty(explode_tmp, explode_dir)\n    return explode_dir\n\n  @classmethod\n  def update_module_paths(cls, new_code_path):\n    # Force subsequent imports to come from the .pex directory rather than the .pex file.\n    TRACER.log('Adding to the head of sys.path: %s' % new_code_path)\n    sys.path.insert(0, new_code_path)\n    for name, module in sys.modules.items():\n      if hasattr(module, \"__path__\"):\n        module_dir = os.path.join(new_code_path, *name.split(\".\"))\n        TRACER.log('Adding to the head of %s.__path__: %s' % (module.__name__, module_dir))\n        module.__path__.insert(0, module_dir)\n\n  @classmethod\n  def write_zipped_internal_cache(cls, pex, pex_info):\n    prefix_length = len(pex_info.internal_cache) + 1\n    existing_cached_distributions = []\n    newly_cached_distributions = []\n    zip_safe_distributions = []\n    with open_zip(pex) as zf:\n      # Distribution names are the first element after \".deps/\" and before the next \"/\"\n      distribution_names = set(filter(None, (filename[prefix_length:].split('/')[0]\n          for filename in zf.namelist() if filename.startswith(pex_info.internal_cache))))\n      # Create Distribution objects from these, and possibly write to disk if necessary.\n      for distribution_name in distribution_names:\n        internal_dist_path = '/'.join([pex_info.internal_cache, distribution_name])\n        # First check if this is already cached\n        dist_digest = pex_info.distributions.get(distribution_name) or CacheHelper.zip_hash(\n            zf, internal_dist_path)\n        cached_location = os.path.join(pex_info.install_cache, '%s.%s' % (\n          distribution_name, dist_digest))\n        if os.path.exists(cached_location):\n          dist = DistributionHelper.distribution_from_path(cached_location)\n          if dist is not None:\n            existing_cached_distributions.append(dist)\n            continue\n        else:\n          dist = DistributionHelper.distribution_from_path(os.path.join(pex, internal_dist_path))\n          if dist is not None:\n            if DistributionHelper.zipsafe(dist) and not pex_info.always_write_cache:\n              zip_safe_distributions.append(dist)\n              continue\n\n        with TRACER.timed('Caching %s' % dist):\n          newly_cached_distributions.append(\n            CacheHelper.cache_distribution(zf, internal_dist_path, cached_location))\n\n    return existing_cached_distributions, newly_cached_distributions, zip_safe_distributions\n\n  @classmethod\n  def load_internal_cache(cls, pex, pex_info):\n    \"\"\"Possibly cache out the internal cache.\"\"\"\n    internal_cache = os.path.join(pex, pex_info.internal_cache)\n    with TRACER.timed('Searching dependency cache: %s' % internal_cache, V=2):\n      if os.path.isdir(pex):\n        for dist in find_distributions(internal_cache):\n          yield dist\n      else:\n        for dist in itertools.chain(*cls.write_zipped_internal_cache(pex, pex_info)):\n          yield dist\n\n  def __init__(self, pex, pex_info, interpreter=None, **kw):\n    self._internal_cache = os.path.join(pex, pex_info.internal_cache)\n    self._pex = pex\n    self._pex_info = pex_info\n    self._activated = False\n    self._working_set = None\n    self._interpreter = interpreter or PythonInterpreter.get()\n    self._inherit_path = pex_info.inherit_path\n    self._supported_tags = []\n\n    platform = Platform.current()\n    platform_name = platform.platform\n    super(PEXEnvironment, self).__init__(\n      search_path=[] if pex_info.inherit_path == 'false' else sys.path,\n      # NB: Our pkg_resources.Environment base-class wants the platform name string and not the\n      # pex.platform.Platform object.\n      platform=platform_name,\n      **kw\n    )\n    self._target_interpreter_env = self._interpreter.identity.pkg_resources_env(platform_name)\n    self._supported_tags.extend(platform.supported_tags(self._interpreter))\n    TRACER.log(\n      'E: tags for %r x %r -> %s' % (self.platform, self._interpreter, self._supported_tags),\n      V=9\n    )\n\n  def update_candidate_distributions(self, distribution_iter):\n    for dist in distribution_iter:\n      if self.can_add(dist):\n        with TRACER.timed('Adding %s' % dist, V=2):\n          self.add(dist)\n\n  def can_add(self, dist):\n    return distribution_compatible(dist, self._supported_tags)\n\n  def activate(self):\n    if not self._activated:\n      with TRACER.timed('Activating PEX virtual environment from %s' % self._pex):\n        self._working_set = self._activate()\n      self._activated = True\n\n    return self._working_set\n\n  def _resolve(self, working_set, reqs):\n    reqs = reqs[:]\n    unresolved_reqs = set()\n    resolveds = set()\n\n    environment = self._target_interpreter_env.copy()\n    environment['extra'] = list(set(itertools.chain(*(req.extras for req in reqs))))\n\n    # Resolve them one at a time so that we can figure out which ones we need to elide should\n    # there be an interpreter incompatibility.\n    for req in reqs:\n      if req.marker and not req.marker.evaluate(environment=environment):\n        TRACER.log('Skipping activation of `%s` due to environment marker de-selection' % req)\n        continue\n      with TRACER.timed('Resolving %s' % req, V=2):\n        try:\n          resolveds.update(working_set.resolve([req], env=self))\n        except DistributionNotFound as e:\n          TRACER.log('Failed to resolve a requirement: %s' % e)\n          unresolved_reqs.add(e.args[0].project_name)\n          # Older versions of pkg_resources just call `DistributionNotFound(req)` instead of the\n          # modern `DistributionNotFound(req, requirers)` and so we may not have the 2nd requirers\n          # slot at all.\n          if len(e.args) >= 2 and e.args[1]:\n            unresolved_reqs.update(e.args[1])\n\n    unresolved_reqs = set([req.lower() for req in unresolved_reqs])\n\n    if unresolved_reqs:\n      TRACER.log('Unresolved requirements:')\n      for req in unresolved_reqs:\n        TRACER.log('  - %s' % req)\n      TRACER.log('Distributions contained within this pex:')\n      if not self._pex_info.distributions:\n        TRACER.log('  None')\n      else:\n        for dist in self._pex_info.distributions:\n          TRACER.log('  - %s' % dist)\n      if not self._pex_info.ignore_errors:\n        die(\n          'Failed to execute PEX file, missing %s compatible dependencies for:\\n%s' % (\n            Platform.current(),\n            '\\n'.join(str(r) for r in unresolved_reqs)\n          )\n        )\n\n    return resolveds\n\n  def _activate(self):\n    self.update_candidate_distributions(self.load_internal_cache(self._pex, self._pex_info))\n\n    if not self._pex_info.zip_safe and os.path.isfile(self._pex):\n      self.update_module_paths(self.force_local(self._pex, self._pex_info))\n\n    all_reqs = [Requirement.parse(req) for req in self._pex_info.requirements]\n\n    working_set = WorkingSet([])\n    resolved = self._resolve(working_set, all_reqs)\n\n    for dist in resolved:\n      with TRACER.timed('Activating %s' % dist, V=2):\n        working_set.add(dist)\n\n        if os.path.isdir(dist.location):\n          with TRACER.timed('Adding sitedir', V=2):\n            if dist.location not in sys.path and self._inherit_path == \"fallback\":\n              # Prepend location to sys.path.\n              # This ensures that bundled versions of libraries will be used before system-installed\n              # versions, in case something is installed in both, helping to favor hermeticity in\n              # the case of non-hermetic PEX files (i.e. those with inherit_path=True).\n              #\n              # If the path is not already in sys.path, site.addsitedir will append (not prepend)\n              # the path to sys.path. But if the path is already in sys.path, site.addsitedir will\n              # leave sys.path unmodified, but will do everything else it would do. This is not part\n              # of its advertised contract (which is very vague), but has been verified to be the\n              # case by inspecting its source for both cpython 2.7 and cpython 3.7.\n              sys.path.insert(0, dist.location)\n            site.addsitedir(dist.location)\n\n        dist.activate()\n\n    return working_set\n", "path": "pex/environment.py"}]}