prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Prefer wheels during plugin install
  Pants has sprouted a dependency on a package that is most easily installed via a `whl` on pypi (`openssl` for `requests[security]`).
  
  But currently the plugin installation infrastructure does not enable usage of `WheelPackage` due to a defensive avoidance of assuming that `wheel` is installed in an environment that pants is being loaded from:
  https://github.com/pantsbuild/pants/blob/e0d5108ff75a41421a11321c73c817332f0a1c86/src/python/pants/init/plugin_resolver.py#L79-L82
  
  As far as @kwlzn or I can tell, `wheel` should always be on the PYTHONPATH, as pants itself depends on it: it's not clear that it actually needs to be "installed" on the PATH as well.
  
  cc @jsirois for any context he might have on that comment.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/python/pants/init/plugin_resolver.py`
  Content:
  ```
  1 # coding=utf-8
  2 # Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
  3 # Licensed under the Apache License, Version 2.0 (see LICENSE).
  4 
  5 from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
  6                         unicode_literals, with_statement)
  7 
  8 import hashlib
  9 import logging
  10 import os
  11 
  12 from pex import resolver
  13 from pex.base import requirement_is_exact
  14 from pex.package import EggPackage, SourcePackage
  15 from pkg_resources import working_set as global_working_set
  16 from pkg_resources import Requirement
  17 
  18 from pants.option.global_options import GlobalOptionsRegistrar
  19 from pants.python.python_repos import PythonRepos
  20 from pants.subsystem.subsystem import Subsystem
  21 from pants.util.dirutil import safe_open
  22 from pants.util.memo import memoized_property
  23 from pants.version import PANTS_SEMVER
  24 
  25 
  26 logger = logging.getLogger(__name__)
  27 
  28 
  29 class PluginResolver(object):
  30   def __init__(self, options_bootstrapper):
  31     self._options_bootstrapper = options_bootstrapper
  32 
  33     bootstrap_options = self._options_bootstrapper.get_bootstrap_options().for_global_scope()
  34     self._plugin_requirements = bootstrap_options.plugins
  35     self._plugin_cache_dir = bootstrap_options.plugin_cache_dir
  36 
  37   def resolve(self, working_set=None):
  38     """Resolves any configured plugins and adds them to the global working set.
  39 
  40     :param working_set: The working set to add the resolved plugins to instead of the global
  41                         working set (for testing).
  42     :type: :class:`pkg_resources.WorkingSet`
  43     """
  44     working_set = working_set or global_working_set
  45     if self._plugin_requirements:
  46       for plugin_location in self._resolve_plugin_locations():
  47         working_set.add_entry(plugin_location)
  48     return working_set
  49 
  50   def _resolve_plugin_locations(self):
  51     # We jump through some hoops here to avoid a live resolve if possible for purposes of speed.
  52     # Even with a local resolve cache fully up to date, running a resolve to activate a plugin
  53     # takes ~250ms whereas loading from a pre-cached list takes ~50ms.
  54     if all(requirement_is_exact(Requirement.parse(req)) for req in self._plugin_requirements):
  55       return self._resolve_exact_plugin_locations()
  56     else:
  57       return (plugin.location for plugin in self._resolve_plugins())
  58 
  59   def _resolve_exact_plugin_locations(self):
  60     hasher = hashlib.sha1()
  61     for req in sorted(self._plugin_requirements):
  62       hasher.update(req)
  63     resolve_hash = hasher.hexdigest()
  64     resolved_plugins_list = os.path.join(self.plugin_cache_dir,
  65                                          'plugins-{}.txt'.format(resolve_hash))
  66 
  67     if not os.path.exists(resolved_plugins_list):
  68       tmp_plugins_list = resolved_plugins_list + '~'
  69       with safe_open(tmp_plugins_list, 'w') as fp:
  70         for plugin in self._resolve_plugins():
  71           fp.write(plugin.location)
  72           fp.write('\n')
  73       os.rename(tmp_plugins_list, resolved_plugins_list)
  74     with open(resolved_plugins_list) as fp:
  75       for plugin_location in fp:
  76         yield plugin_location.strip()
  77 
  78   def _resolve_plugins(self):
  79     # When bootstrapping plugins without the full pants python backend machinery in-play, we are not
  80     # guaranteed a properly initialized interpreter with wheel support so we enforce eggs only for
  81     # bdists with this custom precedence.
  82     precedence = (EggPackage, SourcePackage)
  83     logger.info('Resolving new plugins...:\n  {}'.format('\n  '.join(self._plugin_requirements)))
  84     return resolver.resolve(self._plugin_requirements,
  85                             fetchers=self._python_repos.get_fetchers(),
  86                             context=self._python_repos.get_network_context(),
  87                             precedence=precedence,
  88                             cache=self.plugin_cache_dir,
  89                             cache_ttl=10 * 365 * 24 * 60 * 60,  # Effectively never expire.
  90                             allow_prereleases=PANTS_SEMVER.is_prerelease)
  91 
  92   @memoized_property
  93   def plugin_cache_dir(self):
  94     """The path of the directory pants plugins bdists are cached in."""
  95     return self._plugin_cache_dir
  96 
  97   @memoized_property
  98   def _python_repos(self):
  99     return self._create_global_subsystem(PythonRepos)
  100 
  101   def _create_global_subsystem(self, subsystem_type):
  102     options_scope = subsystem_type.options_scope
  103     return subsystem_type(options_scope, self._options.for_scope(options_scope))
  104 
  105   @memoized_property
  106   def _options(self):
  107     # NB: The PluginResolver runs very early in the pants startup sequence before the standard
  108     # Subsystem facility is wired up.  As a result PluginResolver is not itself a Subsystem with
  109     # PythonRepos as a dependency.  Instead it does the minimum possible work to hand-roll
  110     # bootstrapping of the Subsystem it needs.
  111     subsystems = Subsystem.closure([PythonRepos])
  112     known_scope_infos = [subsystem.get_scope_info() for subsystem in subsystems]
  113     options = self._options_bootstrapper.get_full_options(known_scope_infos)
  114 
  115     # Ignore command line flags since we'd blow up on any we don't understand (most of them).
  116     # If someone wants to bootstrap plugins in a one-off custom way they'll need to use env vars
  117     # or a --pants-config-files pointing to a custom pants.ini snippet.
  118     defaulted_only_options = options.drop_flag_values()
  119 
  120     GlobalOptionsRegistrar.register_options_on_scope(defaulted_only_options)
  121     for subsystem in subsystems:
  122       subsystem.register_options_on_scope(defaulted_only_options)
  123     return defaulted_only_options
  124
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/src/python/pants/init/plugin_resolver.py b/src/python/pants/init/plugin_resolver.py
  --- a/src/python/pants/init/plugin_resolver.py
  +++ b/src/python/pants/init/plugin_resolver.py
  @@ -8,17 +8,18 @@
   import hashlib
   import logging
   import os
  +import site
   
   from pex import resolver
   from pex.base import requirement_is_exact
  -from pex.package import EggPackage, SourcePackage
   from pkg_resources import working_set as global_working_set
   from pkg_resources import Requirement
  +from wheel.install import WheelFile
   
   from pants.option.global_options import GlobalOptionsRegistrar
   from pants.python.python_repos import PythonRepos
   from pants.subsystem.subsystem import Subsystem
  -from pants.util.dirutil import safe_open
  +from pants.util.dirutil import safe_mkdir, safe_open
   from pants.util.memo import memoized_property
   from pants.version import PANTS_SEMVER
   
  @@ -27,6 +28,26 @@
   
   
   class PluginResolver(object):
  +  @staticmethod
  +  def _is_wheel(path):
  +    return os.path.isfile(path) and path.endswith('.whl')
  +
  +  @staticmethod
  +  def _activate_wheel(wheel_path):
  +    install_dir = '{}-install'.format(wheel_path)
  +    safe_mkdir(install_dir, clean=True)
  +    WheelFile(wheel_path).install(force=True,
  +                                  overrides={
  +                                    'purelib': install_dir,
  +                                    'headers': os.path.join(install_dir, 'headers'),
  +                                    'scripts': os.path.join(install_dir, 'bin'),
  +                                    'platlib': install_dir,
  +                                    'data': install_dir
  +                                  })
  +    # Activate any .pth files installed above.
  +    site.addsitedir(install_dir)
  +    return install_dir
  +
     def __init__(self, options_bootstrapper):
       self._options_bootstrapper = options_bootstrapper
   
  @@ -44,6 +65,8 @@
       working_set = working_set or global_working_set
       if self._plugin_requirements:
         for plugin_location in self._resolve_plugin_locations():
  +        if self._is_wheel(plugin_location):
  +          plugin_location = self._activate_wheel(plugin_location)
           working_set.add_entry(plugin_location)
       return working_set
   
  @@ -76,15 +99,10 @@
           yield plugin_location.strip()
   
     def _resolve_plugins(self):
  -    # When bootstrapping plugins without the full pants python backend machinery in-play, we are not
  -    # guaranteed a properly initialized interpreter with wheel support so we enforce eggs only for
  -    # bdists with this custom precedence.
  -    precedence = (EggPackage, SourcePackage)
       logger.info('Resolving new plugins...:\n  {}'.format('\n  '.join(self._plugin_requirements)))
       return resolver.resolve(self._plugin_requirements,
                               fetchers=self._python_repos.get_fetchers(),
                               context=self._python_repos.get_network_context(),
  -                            precedence=precedence,
                               cache=self.plugin_cache_dir,
                               cache_ttl=10 * 365 * 24 * 60 * 60,  # Effectively never expire.
                               allow_prereleases=PANTS_SEMVER.is_prerelease)
  
verification_info: {"golden_diff": "diff --git a/src/python/pants/init/plugin_resolver.py b/src/python/pants/init/plugin_resolver.py\n--- a/src/python/pants/init/plugin_resolver.py\n+++ b/src/python/pants/init/plugin_resolver.py\n@@ -8,17 +8,18 @@\n import hashlib\n import logging\n import os\n+import site\n \n from pex import resolver\n from pex.base import requirement_is_exact\n-from pex.package import EggPackage, SourcePackage\n from pkg_resources import working_set as global_working_set\n from pkg_resources import Requirement\n+from wheel.install import WheelFile\n \n from pants.option.global_options import GlobalOptionsRegistrar\n from pants.python.python_repos import PythonRepos\n from pants.subsystem.subsystem import Subsystem\n-from pants.util.dirutil import safe_open\n+from pants.util.dirutil import safe_mkdir, safe_open\n from pants.util.memo import memoized_property\n from pants.version import PANTS_SEMVER\n \n@@ -27,6 +28,26 @@\n \n \n class PluginResolver(object):\n+  @staticmethod\n+  def _is_wheel(path):\n+    return os.path.isfile(path) and path.endswith('.whl')\n+\n+  @staticmethod\n+  def _activate_wheel(wheel_path):\n+    install_dir = '{}-install'.format(wheel_path)\n+    safe_mkdir(install_dir, clean=True)\n+    WheelFile(wheel_path).install(force=True,\n+                                  overrides={\n+                                    'purelib': install_dir,\n+                                    'headers': os.path.join(install_dir, 'headers'),\n+                                    'scripts': os.path.join(install_dir, 'bin'),\n+                                    'platlib': install_dir,\n+                                    'data': install_dir\n+                                  })\n+    # Activate any .pth files installed above.\n+    site.addsitedir(install_dir)\n+    return install_dir\n+\n   def __init__(self, options_bootstrapper):\n     self._options_bootstrapper = options_bootstrapper\n \n@@ -44,6 +65,8 @@\n     working_set = working_set or global_working_set\n     if self._plugin_requirements:\n       for plugin_location in self._resolve_plugin_locations():\n+        if self._is_wheel(plugin_location):\n+          plugin_location = self._activate_wheel(plugin_location)\n         working_set.add_entry(plugin_location)\n     return working_set\n \n@@ -76,15 +99,10 @@\n         yield plugin_location.strip()\n \n   def _resolve_plugins(self):\n-    # When bootstrapping plugins without the full pants python backend machinery in-play, we are not\n-    # guaranteed a properly initialized interpreter with wheel support so we enforce eggs only for\n-    # bdists with this custom precedence.\n-    precedence = (EggPackage, SourcePackage)\n     logger.info('Resolving new plugins...:\\n  {}'.format('\\n  '.join(self._plugin_requirements)))\n     return resolver.resolve(self._plugin_requirements,\n                             fetchers=self._python_repos.get_fetchers(),\n                             context=self._python_repos.get_network_context(),\n-                            precedence=precedence,\n                             cache=self.plugin_cache_dir,\n                             cache_ttl=10 * 365 * 24 * 60 * 60,  # Effectively never expire.\n                             allow_prereleases=PANTS_SEMVER.is_prerelease)\n", "issue": "Prefer wheels during plugin install\nPants has sprouted a dependency on a package that is most easily installed via a `whl` on pypi (`openssl` for `requests[security]`).\r\n\r\nBut currently the plugin installation infrastructure does not enable usage of `WheelPackage` due to a defensive avoidance of assuming that `wheel` is installed in an environment that pants is being loaded from:\r\nhttps://github.com/pantsbuild/pants/blob/e0d5108ff75a41421a11321c73c817332f0a1c86/src/python/pants/init/plugin_resolver.py#L79-L82\r\n\r\nAs far as @kwlzn or I can tell, `wheel` should always be on the PYTHONPATH, as pants itself depends on it: it's not clear that it actually needs to be \"installed\" on the PATH as well.\r\n\r\ncc @jsirois for any context he might have on that comment.\n", "before_files": [{"content": "# coding=utf-8\n# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import (absolute_import, division, generators, nested_scopes, print_function,\n                        unicode_literals, with_statement)\n\nimport hashlib\nimport logging\nimport os\n\nfrom pex import resolver\nfrom pex.base import requirement_is_exact\nfrom pex.package import EggPackage, SourcePackage\nfrom pkg_resources import working_set as global_working_set\nfrom pkg_resources import Requirement\n\nfrom pants.option.global_options import GlobalOptionsRegistrar\nfrom pants.python.python_repos import PythonRepos\nfrom pants.subsystem.subsystem import Subsystem\nfrom pants.util.dirutil import safe_open\nfrom pants.util.memo import memoized_property\nfrom pants.version import PANTS_SEMVER\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass PluginResolver(object):\n  def __init__(self, options_bootstrapper):\n    self._options_bootstrapper = options_bootstrapper\n\n    bootstrap_options = self._options_bootstrapper.get_bootstrap_options().for_global_scope()\n    self._plugin_requirements = bootstrap_options.plugins\n    self._plugin_cache_dir = bootstrap_options.plugin_cache_dir\n\n  def resolve(self, working_set=None):\n    \"\"\"Resolves any configured plugins and adds them to the global working set.\n\n    :param working_set: The working set to add the resolved plugins to instead of the global\n                        working set (for testing).\n    :type: :class:`pkg_resources.WorkingSet`\n    \"\"\"\n    working_set = working_set or global_working_set\n    if self._plugin_requirements:\n      for plugin_location in self._resolve_plugin_locations():\n        working_set.add_entry(plugin_location)\n    return working_set\n\n  def _resolve_plugin_locations(self):\n    # We jump through some hoops here to avoid a live resolve if possible for purposes of speed.\n    # Even with a local resolve cache fully up to date, running a resolve to activate a plugin\n    # takes ~250ms whereas loading from a pre-cached list takes ~50ms.\n    if all(requirement_is_exact(Requirement.parse(req)) for req in self._plugin_requirements):\n      return self._resolve_exact_plugin_locations()\n    else:\n      return (plugin.location for plugin in self._resolve_plugins())\n\n  def _resolve_exact_plugin_locations(self):\n    hasher = hashlib.sha1()\n    for req in sorted(self._plugin_requirements):\n      hasher.update(req)\n    resolve_hash = hasher.hexdigest()\n    resolved_plugins_list = os.path.join(self.plugin_cache_dir,\n                                         'plugins-{}.txt'.format(resolve_hash))\n\n    if not os.path.exists(resolved_plugins_list):\n      tmp_plugins_list = resolved_plugins_list + '~'\n      with safe_open(tmp_plugins_list, 'w') as fp:\n        for plugin in self._resolve_plugins():\n          fp.write(plugin.location)\n          fp.write('\\n')\n      os.rename(tmp_plugins_list, resolved_plugins_list)\n    with open(resolved_plugins_list) as fp:\n      for plugin_location in fp:\n        yield plugin_location.strip()\n\n  def _resolve_plugins(self):\n    # When bootstrapping plugins without the full pants python backend machinery in-play, we are not\n    # guaranteed a properly initialized interpreter with wheel support so we enforce eggs only for\n    # bdists with this custom precedence.\n    precedence = (EggPackage, SourcePackage)\n    logger.info('Resolving new plugins...:\\n  {}'.format('\\n  '.join(self._plugin_requirements)))\n    return resolver.resolve(self._plugin_requirements,\n                            fetchers=self._python_repos.get_fetchers(),\n                            context=self._python_repos.get_network_context(),\n                            precedence=precedence,\n                            cache=self.plugin_cache_dir,\n                            cache_ttl=10 * 365 * 24 * 60 * 60,  # Effectively never expire.\n                            allow_prereleases=PANTS_SEMVER.is_prerelease)\n\n  @memoized_property\n  def plugin_cache_dir(self):\n    \"\"\"The path of the directory pants plugins bdists are cached in.\"\"\"\n    return self._plugin_cache_dir\n\n  @memoized_property\n  def _python_repos(self):\n    return self._create_global_subsystem(PythonRepos)\n\n  def _create_global_subsystem(self, subsystem_type):\n    options_scope = subsystem_type.options_scope\n    return subsystem_type(options_scope, self._options.for_scope(options_scope))\n\n  @memoized_property\n  def _options(self):\n    # NB: The PluginResolver runs very early in the pants startup sequence before the standard\n    # Subsystem facility is wired up.  As a result PluginResolver is not itself a Subsystem with\n    # PythonRepos as a dependency.  Instead it does the minimum possible work to hand-roll\n    # bootstrapping of the Subsystem it needs.\n    subsystems = Subsystem.closure([PythonRepos])\n    known_scope_infos = [subsystem.get_scope_info() for subsystem in subsystems]\n    options = self._options_bootstrapper.get_full_options(known_scope_infos)\n\n    # Ignore command line flags since we'd blow up on any we don't understand (most of them).\n    # If someone wants to bootstrap plugins in a one-off custom way they'll need to use env vars\n    # or a --pants-config-files pointing to a custom pants.ini snippet.\n    defaulted_only_options = options.drop_flag_values()\n\n    GlobalOptionsRegistrar.register_options_on_scope(defaulted_only_options)\n    for subsystem in subsystems:\n      subsystem.register_options_on_scope(defaulted_only_options)\n    return defaulted_only_options\n", "path": "src/python/pants/init/plugin_resolver.py"}], "after_files": [{"content": "# coding=utf-8\n# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nfrom __future__ import (absolute_import, division, generators, nested_scopes, print_function,\n                        unicode_literals, with_statement)\n\nimport hashlib\nimport logging\nimport os\nimport site\n\nfrom pex import resolver\nfrom pex.base import requirement_is_exact\nfrom pkg_resources import working_set as global_working_set\nfrom pkg_resources import Requirement\nfrom wheel.install import WheelFile\n\nfrom pants.option.global_options import GlobalOptionsRegistrar\nfrom pants.python.python_repos import PythonRepos\nfrom pants.subsystem.subsystem import Subsystem\nfrom pants.util.dirutil import safe_mkdir, safe_open\nfrom pants.util.memo import memoized_property\nfrom pants.version import PANTS_SEMVER\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass PluginResolver(object):\n  @staticmethod\n  def _is_wheel(path):\n    return os.path.isfile(path) and path.endswith('.whl')\n\n  @staticmethod\n  def _activate_wheel(wheel_path):\n    install_dir = '{}-install'.format(wheel_path)\n    safe_mkdir(install_dir, clean=True)\n    WheelFile(wheel_path).install(force=True,\n                                  overrides={\n                                    'purelib': install_dir,\n                                    'headers': os.path.join(install_dir, 'headers'),\n                                    'scripts': os.path.join(install_dir, 'bin'),\n                                    'platlib': install_dir,\n                                    'data': install_dir\n                                  })\n    # Activate any .pth files installed above.\n    site.addsitedir(install_dir)\n    return install_dir\n\n  def __init__(self, options_bootstrapper):\n    self._options_bootstrapper = options_bootstrapper\n\n    bootstrap_options = self._options_bootstrapper.get_bootstrap_options().for_global_scope()\n    self._plugin_requirements = bootstrap_options.plugins\n    self._plugin_cache_dir = bootstrap_options.plugin_cache_dir\n\n  def resolve(self, working_set=None):\n    \"\"\"Resolves any configured plugins and adds them to the global working set.\n\n    :param working_set: The working set to add the resolved plugins to instead of the global\n                        working set (for testing).\n    :type: :class:`pkg_resources.WorkingSet`\n    \"\"\"\n    working_set = working_set or global_working_set\n    if self._plugin_requirements:\n      for plugin_location in self._resolve_plugin_locations():\n        if self._is_wheel(plugin_location):\n          plugin_location = self._activate_wheel(plugin_location)\n        working_set.add_entry(plugin_location)\n    return working_set\n\n  def _resolve_plugin_locations(self):\n    # We jump through some hoops here to avoid a live resolve if possible for purposes of speed.\n    # Even with a local resolve cache fully up to date, running a resolve to activate a plugin\n    # takes ~250ms whereas loading from a pre-cached list takes ~50ms.\n    if all(requirement_is_exact(Requirement.parse(req)) for req in self._plugin_requirements):\n      return self._resolve_exact_plugin_locations()\n    else:\n      return (plugin.location for plugin in self._resolve_plugins())\n\n  def _resolve_exact_plugin_locations(self):\n    hasher = hashlib.sha1()\n    for req in sorted(self._plugin_requirements):\n      hasher.update(req)\n    resolve_hash = hasher.hexdigest()\n    resolved_plugins_list = os.path.join(self.plugin_cache_dir,\n                                         'plugins-{}.txt'.format(resolve_hash))\n\n    if not os.path.exists(resolved_plugins_list):\n      tmp_plugins_list = resolved_plugins_list + '~'\n      with safe_open(tmp_plugins_list, 'w') as fp:\n        for plugin in self._resolve_plugins():\n          fp.write(plugin.location)\n          fp.write('\\n')\n      os.rename(tmp_plugins_list, resolved_plugins_list)\n    with open(resolved_plugins_list) as fp:\n      for plugin_location in fp:\n        yield plugin_location.strip()\n\n  def _resolve_plugins(self):\n    logger.info('Resolving new plugins...:\\n  {}'.format('\\n  '.join(self._plugin_requirements)))\n    return resolver.resolve(self._plugin_requirements,\n                            fetchers=self._python_repos.get_fetchers(),\n                            context=self._python_repos.get_network_context(),\n                            cache=self.plugin_cache_dir,\n                            cache_ttl=10 * 365 * 24 * 60 * 60,  # Effectively never expire.\n                            allow_prereleases=PANTS_SEMVER.is_prerelease)\n\n  @memoized_property\n  def plugin_cache_dir(self):\n    \"\"\"The path of the directory pants plugins bdists are cached in.\"\"\"\n    return self._plugin_cache_dir\n\n  @memoized_property\n  def _python_repos(self):\n    return self._create_global_subsystem(PythonRepos)\n\n  def _create_global_subsystem(self, subsystem_type):\n    options_scope = subsystem_type.options_scope\n    return subsystem_type(options_scope, self._options.for_scope(options_scope))\n\n  @memoized_property\n  def _options(self):\n    # NB: The PluginResolver runs very early in the pants startup sequence before the standard\n    # Subsystem facility is wired up.  As a result PluginResolver is not itself a Subsystem with\n    # PythonRepos as a dependency.  Instead it does the minimum possible work to hand-roll\n    # bootstrapping of the Subsystem it needs.\n    subsystems = Subsystem.closure([PythonRepos])\n    known_scope_infos = [subsystem.get_scope_info() for subsystem in subsystems]\n    options = self._options_bootstrapper.get_full_options(known_scope_infos)\n\n    # Ignore command line flags since we'd blow up on any we don't understand (most of them).\n    # If someone wants to bootstrap plugins in a one-off custom way they'll need to use env vars\n    # or a --pants-config-files pointing to a custom pants.ini snippet.\n    defaulted_only_options = options.drop_flag_values()\n\n    GlobalOptionsRegistrar.register_options_on_scope(defaulted_only_options)\n    for subsystem in subsystems:\n      subsystem.register_options_on_scope(defaulted_only_options)\n    return defaulted_only_options\n", "path": "src/python/pants/init/plugin_resolver.py"}]}