We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
"No installers present" message is confusing.
```
No installers seem to be present and working on your system; fix that or try running certbot with the "certonly" command
```

This is one of the most common messages, received when running certbot on an Nginx installation, but it is confusing to a new user. What's an installer? Is it my mistake or certbot's that there are no installers? What does "certonly" do?

Instead how about:

```
Certbot doesn't know how to automatically configure the web server on this system. However, it can still get a certificate for you. Please run "certbot[-auto] certonly" to do so. You'll need to manually configure your web server to use the resulting certificate.
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `certbot/plugins/selection.py`
Content:
```
1 """Decide which plugins to use for authentication & installation"""
2 from __future__ import print_function
3 
4 import os
5 import logging
6 
7 import six
8 import zope.component
9 
10 from certbot import errors
11 from certbot import interfaces
12 
13 from certbot.display import util as display_util
14 
15 logger = logging.getLogger(__name__)
16 z_util = zope.component.getUtility
17 
18 def pick_configurator(
19         config, default, plugins,
20         question="How would you like to authenticate and install "
21                  "certificates?"):
22     """Pick configurator plugin."""
23     return pick_plugin(
24         config, default, plugins, question,
25         (interfaces.IAuthenticator, interfaces.IInstaller))
26 
27 
28 def pick_installer(config, default, plugins,
29                    question="How would you like to install certificates?"):
30     """Pick installer plugin."""
31     return pick_plugin(
32         config, default, plugins, question, (interfaces.IInstaller,))
33 
34 
35 def pick_authenticator(
36         config, default, plugins, question="How would you "
37         "like to authenticate with the ACME CA?"):
38     """Pick authentication plugin."""
39     return pick_plugin(
40         config, default, plugins, question, (interfaces.IAuthenticator,))
41 
42 
43 def pick_plugin(config, default, plugins, question, ifaces):
44     """Pick plugin.
45 
46     :param certbot.interfaces.IConfig: Configuration
47     :param str default: Plugin name supplied by user or ``None``.
48     :param certbot.plugins.disco.PluginsRegistry plugins:
49         All plugins registered as entry points.
50     :param str question: Question to be presented to the user in case
51         multiple candidates are found.
52     :param list ifaces: Interfaces that plugins must provide.
53 
54     :returns: Initialized plugin.
55     :rtype: IPlugin
56 
57     """
58     if default is not None:
59         # throw more UX-friendly error if default not in plugins
60         filtered = plugins.filter(lambda p_ep: p_ep.name == default)
61     else:
62         if config.noninteractive_mode:
63             # it's really bad to auto-select the single available plugin in
64             # non-interactive mode, because an update could later add a second
65             # available plugin
66             raise errors.MissingCommandlineFlag(
67                 "Missing command line flags. For non-interactive execution, "
68                 "you will need to specify a plugin on the command line.  Run "
69                 "with '--help plugins' to see a list of options, and see "
70                 "https://eff.org/letsencrypt-plugins for more detail on what "
71                 "the plugins do and how to use them.")
72 
73         filtered = plugins.visible().ifaces(ifaces)
74 
75     filtered.init(config)
76     verified = filtered.verify(ifaces)
77     verified.prepare()
78     prepared = verified.available()
79 
80     if len(prepared) > 1:
81         logger.debug("Multiple candidate plugins: %s", prepared)
82         plugin_ep = choose_plugin(list(six.itervalues(prepared)), question)
83         if plugin_ep is None:
84             return None
85         else:
86             return plugin_ep.init()
87     elif len(prepared) == 1:
88         plugin_ep = list(prepared.values())[0]
89         logger.debug("Single candidate plugin: %s", plugin_ep)
90         if plugin_ep.misconfigured:
91             return None
92         return plugin_ep.init()
93     else:
94         logger.debug("No candidate plugin")
95         return None
96 
97 
98 def choose_plugin(prepared, question):
99     """Allow the user to choose their plugin.
100 
101     :param list prepared: List of `~.PluginEntryPoint`.
102     :param str question: Question to be presented to the user.
103 
104     :returns: Plugin entry point chosen by the user.
105     :rtype: `~.PluginEntryPoint`
106 
107     """
108     opts = [plugin_ep.description_with_name +
109             (" [Misconfigured]" if plugin_ep.misconfigured else "")
110             for plugin_ep in prepared]
111 
112     while True:
113         disp = z_util(interfaces.IDisplay)
114         code, index = disp.menu(question, opts, help_label="More Info")
115 
116         if code == display_util.OK:
117             plugin_ep = prepared[index]
118             if plugin_ep.misconfigured:
119                 z_util(interfaces.IDisplay).notification(
120                     "The selected plugin encountered an error while parsing "
121                     "your server configuration and cannot be used. The error "
122                     "was:\n\n{0}".format(plugin_ep.prepare()),
123                     height=display_util.HEIGHT, pause=False)
124             else:
125                 return plugin_ep
126         elif code == display_util.HELP:
127             if prepared[index].misconfigured:
128                 msg = "Reported Error: %s" % prepared[index].prepare()
129             else:
130                 msg = prepared[index].init().more_info()
131             z_util(interfaces.IDisplay).notification(
132                 msg, height=display_util.HEIGHT)
133         else:
134             return None
135 
136 noninstaller_plugins = ["webroot", "manual", "standalone"]
137 
138 def record_chosen_plugins(config, plugins, auth, inst):
139     "Update the config entries to reflect the plugins we actually selected."
140     cn = config.namespace
141     cn.authenticator = plugins.find_init(auth).name if auth else "None"
142     cn.installer = plugins.find_init(inst).name if inst else "None"
143 
144 
145 def choose_configurator_plugins(config, plugins, verb):
146     """
147     Figure out which configurator we're going to use, modifies
148     config.authenticator and config.installer strings to reflect that choice if
149     necessary.
150 
151     :raises errors.PluginSelectionError if there was a problem
152 
153     :returns: (an `IAuthenticator` or None, an `IInstaller` or None)
154     :rtype: tuple
155     """
156 
157     req_auth, req_inst = cli_plugin_requests(config)
158 
159     # Which plugins do we need?
160     if verb == "run":
161         need_inst = need_auth = True
162         from certbot.cli import cli_command
163         if req_auth in noninstaller_plugins and not req_inst:
164             msg = ('With the {0} plugin, you probably want to use the "certonly" command, eg:{1}'
165                    '{1}    {2} certonly --{0}{1}{1}'
166                    '(Alternatively, add a --installer flag. See https://eff.org/letsencrypt-plugins'
167                    '{1} and "--help plugins" for more information.)'.format(
168                        req_auth, os.linesep, cli_command))
169 
170             raise errors.MissingCommandlineFlag(msg)
171     else:
172         need_inst = need_auth = False
173     if verb == "certonly":
174         need_auth = True
175     if verb == "install":
176         need_inst = True
177         if config.authenticator:
178             logger.warning("Specifying an authenticator doesn't make sense in install mode")
179 
180     # Try to meet the user's request and/or ask them to pick plugins
181     authenticator = installer = None
182     if verb == "run" and req_auth == req_inst:
183         # Unless the user has explicitly asked for different auth/install,
184         # only consider offering a single choice
185         authenticator = installer = pick_configurator(config, req_inst, plugins)
186     else:
187         if need_inst or req_inst:
188             installer = pick_installer(config, req_inst, plugins)
189         if need_auth:
190             authenticator = pick_authenticator(config, req_auth, plugins)
191     logger.debug("Selected authenticator %s and installer %s", authenticator, installer)
192 
193     # Report on any failures
194     if need_inst and not installer:
195         diagnose_configurator_problem("installer", req_inst, plugins)
196     if need_auth and not authenticator:
197         diagnose_configurator_problem("authenticator", req_auth, plugins)
198 
199     record_chosen_plugins(config, plugins, authenticator, installer)
200     return installer, authenticator
201 
202 
203 def set_configurator(previously, now):
204     """
205     Setting configurators multiple ways is okay, as long as they all agree
206     :param str previously: previously identified request for the installer/authenticator
207     :param str requested: the request currently being processed
208     """
209     if not now:
210         # we're not actually setting anything
211         return previously
212     if previously:
213         if previously != now:
214             msg = "Too many flags setting configurators/installers/authenticators {0} -> {1}"
215             raise errors.PluginSelectionError(msg.format(repr(previously), repr(now)))
216     return now
217 
218 
219 def cli_plugin_requests(config):
220     """
221     Figure out which plugins the user requested with CLI and config options
222 
223     :returns: (requested authenticator string or None, requested installer string or None)
224     :rtype: tuple
225     """
226     req_inst = req_auth = config.configurator
227     req_inst = set_configurator(req_inst, config.installer)
228     req_auth = set_configurator(req_auth, config.authenticator)
229     if config.nginx:
230         req_inst = set_configurator(req_inst, "nginx")
231         req_auth = set_configurator(req_auth, "nginx")
232     if config.apache:
233         req_inst = set_configurator(req_inst, "apache")
234         req_auth = set_configurator(req_auth, "apache")
235     if config.standalone:
236         req_auth = set_configurator(req_auth, "standalone")
237     if config.webroot:
238         req_auth = set_configurator(req_auth, "webroot")
239     if config.manual:
240         req_auth = set_configurator(req_auth, "manual")
241     logger.debug("Requested authenticator %s and installer %s", req_auth, req_inst)
242     return req_auth, req_inst
243 
244 
245 def diagnose_configurator_problem(cfg_type, requested, plugins):
246     """
247     Raise the most helpful error message about a plugin being unavailable
248 
249     :param str cfg_type: either "installer" or "authenticator"
250     :param str requested: the plugin that was requested
251     :param .PluginsRegistry plugins: available plugins
252 
253     :raises error.PluginSelectionError: if there was a problem
254     """
255 
256     if requested:
257         if requested not in plugins:
258             msg = "The requested {0} plugin does not appear to be installed".format(requested)
259         else:
260             msg = ("The {0} plugin is not working; there may be problems with "
261                    "your existing configuration.\nThe error was: {1!r}"
262                    .format(requested, plugins[requested].problem))
263     elif cfg_type == "installer":
264         msg = ('No installer plugins seem to be present and working on your system; '
265                'fix that or try running certbot with the "certonly" command to obtain'
266                ' a certificate you can install manually')
267     else:
268         msg = "{0} could not be determined or is not installed".format(cfg_type)
269     raise errors.PluginSelectionError(msg)
270
```
--- 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/certbot/plugins/selection.py b/certbot/plugins/selection.py
--- a/certbot/plugins/selection.py
+++ b/certbot/plugins/selection.py
@@ -261,9 +261,12 @@
                    "your existing configuration.\nThe error was: {1!r}"
                    .format(requested, plugins[requested].problem))
     elif cfg_type == "installer":
-        msg = ('No installer plugins seem to be present and working on your system; '
-               'fix that or try running certbot with the "certonly" command to obtain'
-               ' a certificate you can install manually')
+        from certbot.cli import cli_command
+        msg = ('Certbot doesn\'t know how to automatically configure the web '
+          'server on this system. However, it can still get a certificate for '
+          'you. Please run "{0} certonly" to do so. You\'ll need to '
+          'manually configure your web server to use the resulting '
+          'certificate.').format(cli_command)
     else:
         msg = "{0} could not be determined or is not installed".format(cfg_type)
     raise errors.PluginSelectionError(msg)

{"golden_diff": "diff --git a/certbot/plugins/selection.py b/certbot/plugins/selection.py\n--- a/certbot/plugins/selection.py\n+++ b/certbot/plugins/selection.py\n@@ -261,9 +261,12 @@\n                    \"your existing configuration.\\nThe error was: {1!r}\"\n                    .format(requested, plugins[requested].problem))\n     elif cfg_type == \"installer\":\n-        msg = ('No installer plugins seem to be present and working on your system; '\n-               'fix that or try running certbot with the \"certonly\" command to obtain'\n-               ' a certificate you can install manually')\n+        from certbot.cli import cli_command\n+        msg = ('Certbot doesn\\'t know how to automatically configure the web '\n+          'server on this system. However, it can still get a certificate for '\n+          'you. Please run \"{0} certonly\" to do so. You\\'ll need to '\n+          'manually configure your web server to use the resulting '\n+          'certificate.').format(cli_command)\n     else:\n         msg = \"{0} could not be determined or is not installed\".format(cfg_type)\n     raise errors.PluginSelectionError(msg)\n", "issue": "\"No installers present\" message is confusing.\n```\nNo installers seem to be present and working on your system; fix that or try running certbot with the \"certonly\" command\n```\n\nThis is one of the most common messages, received when running certbot on an Nginx installation, but it is confusing to a new user. What's an installer? Is it my mistake or certbot's that there are no installers? What does \"certonly\" do?\n\nInstead how about:\n\n```\nCertbot doesn't know how to automatically configure the web server on this system. However, it can still get a certificate for you. Please run \"certbot[-auto] certonly\" to do so. You'll need to manually configure your web server to use the resulting certificate.\n```\n\n", "before_files": [{"content": "\"\"\"Decide which plugins to use for authentication & installation\"\"\"\nfrom __future__ import print_function\n\nimport os\nimport logging\n\nimport six\nimport zope.component\n\nfrom certbot import errors\nfrom certbot import interfaces\n\nfrom certbot.display import util as display_util\n\nlogger = logging.getLogger(__name__)\nz_util = zope.component.getUtility\n\ndef pick_configurator(\n        config, default, plugins,\n        question=\"How would you like to authenticate and install \"\n                 \"certificates?\"):\n    \"\"\"Pick configurator plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question,\n        (interfaces.IAuthenticator, interfaces.IInstaller))\n\n\ndef pick_installer(config, default, plugins,\n                   question=\"How would you like to install certificates?\"):\n    \"\"\"Pick installer plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question, (interfaces.IInstaller,))\n\n\ndef pick_authenticator(\n        config, default, plugins, question=\"How would you \"\n        \"like to authenticate with the ACME CA?\"):\n    \"\"\"Pick authentication plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question, (interfaces.IAuthenticator,))\n\n\ndef pick_plugin(config, default, plugins, question, ifaces):\n    \"\"\"Pick plugin.\n\n    :param certbot.interfaces.IConfig: Configuration\n    :param str default: Plugin name supplied by user or ``None``.\n    :param certbot.plugins.disco.PluginsRegistry plugins:\n        All plugins registered as entry points.\n    :param str question: Question to be presented to the user in case\n        multiple candidates are found.\n    :param list ifaces: Interfaces that plugins must provide.\n\n    :returns: Initialized plugin.\n    :rtype: IPlugin\n\n    \"\"\"\n    if default is not None:\n        # throw more UX-friendly error if default not in plugins\n        filtered = plugins.filter(lambda p_ep: p_ep.name == default)\n    else:\n        if config.noninteractive_mode:\n            # it's really bad to auto-select the single available plugin in\n            # non-interactive mode, because an update could later add a second\n            # available plugin\n            raise errors.MissingCommandlineFlag(\n                \"Missing command line flags. For non-interactive execution, \"\n                \"you will need to specify a plugin on the command line.  Run \"\n                \"with '--help plugins' to see a list of options, and see \"\n                \"https://eff.org/letsencrypt-plugins for more detail on what \"\n                \"the plugins do and how to use them.\")\n\n        filtered = plugins.visible().ifaces(ifaces)\n\n    filtered.init(config)\n    verified = filtered.verify(ifaces)\n    verified.prepare()\n    prepared = verified.available()\n\n    if len(prepared) > 1:\n        logger.debug(\"Multiple candidate plugins: %s\", prepared)\n        plugin_ep = choose_plugin(list(six.itervalues(prepared)), question)\n        if plugin_ep is None:\n            return None\n        else:\n            return plugin_ep.init()\n    elif len(prepared) == 1:\n        plugin_ep = list(prepared.values())[0]\n        logger.debug(\"Single candidate plugin: %s\", plugin_ep)\n        if plugin_ep.misconfigured:\n            return None\n        return plugin_ep.init()\n    else:\n        logger.debug(\"No candidate plugin\")\n        return None\n\n\ndef choose_plugin(prepared, question):\n    \"\"\"Allow the user to choose their plugin.\n\n    :param list prepared: List of `~.PluginEntryPoint`.\n    :param str question: Question to be presented to the user.\n\n    :returns: Plugin entry point chosen by the user.\n    :rtype: `~.PluginEntryPoint`\n\n    \"\"\"\n    opts = [plugin_ep.description_with_name +\n            (\" [Misconfigured]\" if plugin_ep.misconfigured else \"\")\n            for plugin_ep in prepared]\n\n    while True:\n        disp = z_util(interfaces.IDisplay)\n        code, index = disp.menu(question, opts, help_label=\"More Info\")\n\n        if code == display_util.OK:\n            plugin_ep = prepared[index]\n            if plugin_ep.misconfigured:\n                z_util(interfaces.IDisplay).notification(\n                    \"The selected plugin encountered an error while parsing \"\n                    \"your server configuration and cannot be used. The error \"\n                    \"was:\\n\\n{0}\".format(plugin_ep.prepare()),\n                    height=display_util.HEIGHT, pause=False)\n            else:\n                return plugin_ep\n        elif code == display_util.HELP:\n            if prepared[index].misconfigured:\n                msg = \"Reported Error: %s\" % prepared[index].prepare()\n            else:\n                msg = prepared[index].init().more_info()\n            z_util(interfaces.IDisplay).notification(\n                msg, height=display_util.HEIGHT)\n        else:\n            return None\n\nnoninstaller_plugins = [\"webroot\", \"manual\", \"standalone\"]\n\ndef record_chosen_plugins(config, plugins, auth, inst):\n    \"Update the config entries to reflect the plugins we actually selected.\"\n    cn = config.namespace\n    cn.authenticator = plugins.find_init(auth).name if auth else \"None\"\n    cn.installer = plugins.find_init(inst).name if inst else \"None\"\n\n\ndef choose_configurator_plugins(config, plugins, verb):\n    \"\"\"\n    Figure out which configurator we're going to use, modifies\n    config.authenticator and config.installer strings to reflect that choice if\n    necessary.\n\n    :raises errors.PluginSelectionError if there was a problem\n\n    :returns: (an `IAuthenticator` or None, an `IInstaller` or None)\n    :rtype: tuple\n    \"\"\"\n\n    req_auth, req_inst = cli_plugin_requests(config)\n\n    # Which plugins do we need?\n    if verb == \"run\":\n        need_inst = need_auth = True\n        from certbot.cli import cli_command\n        if req_auth in noninstaller_plugins and not req_inst:\n            msg = ('With the {0} plugin, you probably want to use the \"certonly\" command, eg:{1}'\n                   '{1}    {2} certonly --{0}{1}{1}'\n                   '(Alternatively, add a --installer flag. See https://eff.org/letsencrypt-plugins'\n                   '{1} and \"--help plugins\" for more information.)'.format(\n                       req_auth, os.linesep, cli_command))\n\n            raise errors.MissingCommandlineFlag(msg)\n    else:\n        need_inst = need_auth = False\n    if verb == \"certonly\":\n        need_auth = True\n    if verb == \"install\":\n        need_inst = True\n        if config.authenticator:\n            logger.warning(\"Specifying an authenticator doesn't make sense in install mode\")\n\n    # Try to meet the user's request and/or ask them to pick plugins\n    authenticator = installer = None\n    if verb == \"run\" and req_auth == req_inst:\n        # Unless the user has explicitly asked for different auth/install,\n        # only consider offering a single choice\n        authenticator = installer = pick_configurator(config, req_inst, plugins)\n    else:\n        if need_inst or req_inst:\n            installer = pick_installer(config, req_inst, plugins)\n        if need_auth:\n            authenticator = pick_authenticator(config, req_auth, plugins)\n    logger.debug(\"Selected authenticator %s and installer %s\", authenticator, installer)\n\n    # Report on any failures\n    if need_inst and not installer:\n        diagnose_configurator_problem(\"installer\", req_inst, plugins)\n    if need_auth and not authenticator:\n        diagnose_configurator_problem(\"authenticator\", req_auth, plugins)\n\n    record_chosen_plugins(config, plugins, authenticator, installer)\n    return installer, authenticator\n\n\ndef set_configurator(previously, now):\n    \"\"\"\n    Setting configurators multiple ways is okay, as long as they all agree\n    :param str previously: previously identified request for the installer/authenticator\n    :param str requested: the request currently being processed\n    \"\"\"\n    if not now:\n        # we're not actually setting anything\n        return previously\n    if previously:\n        if previously != now:\n            msg = \"Too many flags setting configurators/installers/authenticators {0} -> {1}\"\n            raise errors.PluginSelectionError(msg.format(repr(previously), repr(now)))\n    return now\n\n\ndef cli_plugin_requests(config):\n    \"\"\"\n    Figure out which plugins the user requested with CLI and config options\n\n    :returns: (requested authenticator string or None, requested installer string or None)\n    :rtype: tuple\n    \"\"\"\n    req_inst = req_auth = config.configurator\n    req_inst = set_configurator(req_inst, config.installer)\n    req_auth = set_configurator(req_auth, config.authenticator)\n    if config.nginx:\n        req_inst = set_configurator(req_inst, \"nginx\")\n        req_auth = set_configurator(req_auth, \"nginx\")\n    if config.apache:\n        req_inst = set_configurator(req_inst, \"apache\")\n        req_auth = set_configurator(req_auth, \"apache\")\n    if config.standalone:\n        req_auth = set_configurator(req_auth, \"standalone\")\n    if config.webroot:\n        req_auth = set_configurator(req_auth, \"webroot\")\n    if config.manual:\n        req_auth = set_configurator(req_auth, \"manual\")\n    logger.debug(\"Requested authenticator %s and installer %s\", req_auth, req_inst)\n    return req_auth, req_inst\n\n\ndef diagnose_configurator_problem(cfg_type, requested, plugins):\n    \"\"\"\n    Raise the most helpful error message about a plugin being unavailable\n\n    :param str cfg_type: either \"installer\" or \"authenticator\"\n    :param str requested: the plugin that was requested\n    :param .PluginsRegistry plugins: available plugins\n\n    :raises error.PluginSelectionError: if there was a problem\n    \"\"\"\n\n    if requested:\n        if requested not in plugins:\n            msg = \"The requested {0} plugin does not appear to be installed\".format(requested)\n        else:\n            msg = (\"The {0} plugin is not working; there may be problems with \"\n                   \"your existing configuration.\\nThe error was: {1!r}\"\n                   .format(requested, plugins[requested].problem))\n    elif cfg_type == \"installer\":\n        msg = ('No installer plugins seem to be present and working on your system; '\n               'fix that or try running certbot with the \"certonly\" command to obtain'\n               ' a certificate you can install manually')\n    else:\n        msg = \"{0} could not be determined or is not installed\".format(cfg_type)\n    raise errors.PluginSelectionError(msg)\n", "path": "certbot/plugins/selection.py"}], "after_files": [{"content": "\"\"\"Decide which plugins to use for authentication & installation\"\"\"\nfrom __future__ import print_function\n\nimport os\nimport logging\n\nimport six\nimport zope.component\n\nfrom certbot import errors\nfrom certbot import interfaces\n\nfrom certbot.display import util as display_util\n\nlogger = logging.getLogger(__name__)\nz_util = zope.component.getUtility\n\ndef pick_configurator(\n        config, default, plugins,\n        question=\"How would you like to authenticate and install \"\n                 \"certificates?\"):\n    \"\"\"Pick configurator plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question,\n        (interfaces.IAuthenticator, interfaces.IInstaller))\n\n\ndef pick_installer(config, default, plugins,\n                   question=\"How would you like to install certificates?\"):\n    \"\"\"Pick installer plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question, (interfaces.IInstaller,))\n\n\ndef pick_authenticator(\n        config, default, plugins, question=\"How would you \"\n        \"like to authenticate with the ACME CA?\"):\n    \"\"\"Pick authentication plugin.\"\"\"\n    return pick_plugin(\n        config, default, plugins, question, (interfaces.IAuthenticator,))\n\n\ndef pick_plugin(config, default, plugins, question, ifaces):\n    \"\"\"Pick plugin.\n\n    :param certbot.interfaces.IConfig: Configuration\n    :param str default: Plugin name supplied by user or ``None``.\n    :param certbot.plugins.disco.PluginsRegistry plugins:\n        All plugins registered as entry points.\n    :param str question: Question to be presented to the user in case\n        multiple candidates are found.\n    :param list ifaces: Interfaces that plugins must provide.\n\n    :returns: Initialized plugin.\n    :rtype: IPlugin\n\n    \"\"\"\n    if default is not None:\n        # throw more UX-friendly error if default not in plugins\n        filtered = plugins.filter(lambda p_ep: p_ep.name == default)\n    else:\n        if config.noninteractive_mode:\n            # it's really bad to auto-select the single available plugin in\n            # non-interactive mode, because an update could later add a second\n            # available plugin\n            raise errors.MissingCommandlineFlag(\n                \"Missing command line flags. For non-interactive execution, \"\n                \"you will need to specify a plugin on the command line.  Run \"\n                \"with '--help plugins' to see a list of options, and see \"\n                \"https://eff.org/letsencrypt-plugins for more detail on what \"\n                \"the plugins do and how to use them.\")\n\n        filtered = plugins.visible().ifaces(ifaces)\n\n    filtered.init(config)\n    verified = filtered.verify(ifaces)\n    verified.prepare()\n    prepared = verified.available()\n\n    if len(prepared) > 1:\n        logger.debug(\"Multiple candidate plugins: %s\", prepared)\n        plugin_ep = choose_plugin(list(six.itervalues(prepared)), question)\n        if plugin_ep is None:\n            return None\n        else:\n            return plugin_ep.init()\n    elif len(prepared) == 1:\n        plugin_ep = list(prepared.values())[0]\n        logger.debug(\"Single candidate plugin: %s\", plugin_ep)\n        if plugin_ep.misconfigured:\n            return None\n        return plugin_ep.init()\n    else:\n        logger.debug(\"No candidate plugin\")\n        return None\n\n\ndef choose_plugin(prepared, question):\n    \"\"\"Allow the user to choose their plugin.\n\n    :param list prepared: List of `~.PluginEntryPoint`.\n    :param str question: Question to be presented to the user.\n\n    :returns: Plugin entry point chosen by the user.\n    :rtype: `~.PluginEntryPoint`\n\n    \"\"\"\n    opts = [plugin_ep.description_with_name +\n            (\" [Misconfigured]\" if plugin_ep.misconfigured else \"\")\n            for plugin_ep in prepared]\n\n    while True:\n        disp = z_util(interfaces.IDisplay)\n        code, index = disp.menu(question, opts, help_label=\"More Info\")\n\n        if code == display_util.OK:\n            plugin_ep = prepared[index]\n            if plugin_ep.misconfigured:\n                z_util(interfaces.IDisplay).notification(\n                    \"The selected plugin encountered an error while parsing \"\n                    \"your server configuration and cannot be used. The error \"\n                    \"was:\\n\\n{0}\".format(plugin_ep.prepare()),\n                    height=display_util.HEIGHT, pause=False)\n            else:\n                return plugin_ep\n        elif code == display_util.HELP:\n            if prepared[index].misconfigured:\n                msg = \"Reported Error: %s\" % prepared[index].prepare()\n            else:\n                msg = prepared[index].init().more_info()\n            z_util(interfaces.IDisplay).notification(\n                msg, height=display_util.HEIGHT)\n        else:\n            return None\n\nnoninstaller_plugins = [\"webroot\", \"manual\", \"standalone\"]\n\ndef record_chosen_plugins(config, plugins, auth, inst):\n    \"Update the config entries to reflect the plugins we actually selected.\"\n    cn = config.namespace\n    cn.authenticator = plugins.find_init(auth).name if auth else \"None\"\n    cn.installer = plugins.find_init(inst).name if inst else \"None\"\n\n\ndef choose_configurator_plugins(config, plugins, verb):\n    \"\"\"\n    Figure out which configurator we're going to use, modifies\n    config.authenticator and config.installer strings to reflect that choice if\n    necessary.\n\n    :raises errors.PluginSelectionError if there was a problem\n\n    :returns: (an `IAuthenticator` or None, an `IInstaller` or None)\n    :rtype: tuple\n    \"\"\"\n\n    req_auth, req_inst = cli_plugin_requests(config)\n\n    # Which plugins do we need?\n    if verb == \"run\":\n        need_inst = need_auth = True\n        from certbot.cli import cli_command\n        if req_auth in noninstaller_plugins and not req_inst:\n            msg = ('With the {0} plugin, you probably want to use the \"certonly\" command, eg:{1}'\n                   '{1}    {2} certonly --{0}{1}{1}'\n                   '(Alternatively, add a --installer flag. See https://eff.org/letsencrypt-plugins'\n                   '{1} and \"--help plugins\" for more information.)'.format(\n                       req_auth, os.linesep, cli_command))\n\n            raise errors.MissingCommandlineFlag(msg)\n    else:\n        need_inst = need_auth = False\n    if verb == \"certonly\":\n        need_auth = True\n    if verb == \"install\":\n        need_inst = True\n        if config.authenticator:\n            logger.warning(\"Specifying an authenticator doesn't make sense in install mode\")\n\n    # Try to meet the user's request and/or ask them to pick plugins\n    authenticator = installer = None\n    if verb == \"run\" and req_auth == req_inst:\n        # Unless the user has explicitly asked for different auth/install,\n        # only consider offering a single choice\n        authenticator = installer = pick_configurator(config, req_inst, plugins)\n    else:\n        if need_inst or req_inst:\n            installer = pick_installer(config, req_inst, plugins)\n        if need_auth:\n            authenticator = pick_authenticator(config, req_auth, plugins)\n    logger.debug(\"Selected authenticator %s and installer %s\", authenticator, installer)\n\n    # Report on any failures\n    if need_inst and not installer:\n        diagnose_configurator_problem(\"installer\", req_inst, plugins)\n    if need_auth and not authenticator:\n        diagnose_configurator_problem(\"authenticator\", req_auth, plugins)\n\n    record_chosen_plugins(config, plugins, authenticator, installer)\n    return installer, authenticator\n\n\ndef set_configurator(previously, now):\n    \"\"\"\n    Setting configurators multiple ways is okay, as long as they all agree\n    :param str previously: previously identified request for the installer/authenticator\n    :param str requested: the request currently being processed\n    \"\"\"\n    if not now:\n        # we're not actually setting anything\n        return previously\n    if previously:\n        if previously != now:\n            msg = \"Too many flags setting configurators/installers/authenticators {0} -> {1}\"\n            raise errors.PluginSelectionError(msg.format(repr(previously), repr(now)))\n    return now\n\n\ndef cli_plugin_requests(config):\n    \"\"\"\n    Figure out which plugins the user requested with CLI and config options\n\n    :returns: (requested authenticator string or None, requested installer string or None)\n    :rtype: tuple\n    \"\"\"\n    req_inst = req_auth = config.configurator\n    req_inst = set_configurator(req_inst, config.installer)\n    req_auth = set_configurator(req_auth, config.authenticator)\n    if config.nginx:\n        req_inst = set_configurator(req_inst, \"nginx\")\n        req_auth = set_configurator(req_auth, \"nginx\")\n    if config.apache:\n        req_inst = set_configurator(req_inst, \"apache\")\n        req_auth = set_configurator(req_auth, \"apache\")\n    if config.standalone:\n        req_auth = set_configurator(req_auth, \"standalone\")\n    if config.webroot:\n        req_auth = set_configurator(req_auth, \"webroot\")\n    if config.manual:\n        req_auth = set_configurator(req_auth, \"manual\")\n    logger.debug(\"Requested authenticator %s and installer %s\", req_auth, req_inst)\n    return req_auth, req_inst\n\n\ndef diagnose_configurator_problem(cfg_type, requested, plugins):\n    \"\"\"\n    Raise the most helpful error message about a plugin being unavailable\n\n    :param str cfg_type: either \"installer\" or \"authenticator\"\n    :param str requested: the plugin that was requested\n    :param .PluginsRegistry plugins: available plugins\n\n    :raises error.PluginSelectionError: if there was a problem\n    \"\"\"\n\n    if requested:\n        if requested not in plugins:\n            msg = \"The requested {0} plugin does not appear to be installed\".format(requested)\n        else:\n            msg = (\"The {0} plugin is not working; there may be problems with \"\n                   \"your existing configuration.\\nThe error was: {1!r}\"\n                   .format(requested, plugins[requested].problem))\n    elif cfg_type == \"installer\":\n        from certbot.cli import cli_command\n        msg = ('Certbot doesn\\'t know how to automatically configure the web '\n          'server on this system. However, it can still get a certificate for '\n          'you. Please run \"{0} certonly\" to do so. You\\'ll need to '\n          'manually configure your web server to use the resulting '\n          'certificate.').format(cli_command)\n    else:\n        msg = \"{0} could not be determined or is not installed\".format(cfg_type)\n    raise errors.PluginSelectionError(msg)\n", "path": "certbot/plugins/selection.py"}]}