Repository: xonsh/xonsh

Issue:
An exception in a completer crashes xonsh
<!--- Provide a general summary of the issue in the Title above -->
<!--- If you have a question along the lines of "How do I do this Bash command in xonsh"
please first look over the Bash to Xonsh translation guide: https://xon.sh/bash_to_xsh.html
If you don't find an answer there, please do open an issue! -->

## xonfig

<details>

```
+------------------+-----------------+
| xonsh            | 0.9.20          |
| Python           | 3.8.5           |
| PLY              | 3.11            |
| have readline    | True            |
| prompt toolkit   | 3.0.7           |
| shell type       | prompt_toolkit  |
| pygments         | 2.7.1           |
| on posix         | True            |
| on linux         | True            |
| distro           | unknown         |
| on darwin        | False           |
| on windows       | False           |
| on cygwin        | False           |
| on msys2         | False           |
| is superuser     | False           |
| default encoding | utf-8           |
| xonsh encoding   | utf-8           |
| encoding errors  | surrogateescape |
+------------------+-----------------+

```

</details>

## Expected Behavior
Falling back to `sh` is much better than completely losing your shell, but it is still disconcerting, especially for a new user first trying to write a completer. I think that when an exception is raised by a completer xonsh should skip over that completer when finding completion results. Possibly xonsh could prompt the user that completer X encountered an error.

I am not a xonsh expert, but based on the traceback I think it would suffice to put a blanket `except` clause here that calls `continue`, possibly after printing some warning to the user about a bad completer and logging the traceback in some way:

https://github.com/xonsh/xonsh/blob/68993e4b3df51c1972098232e327e1510cc86c4c/xonsh/completer.py#L34-L38

## Current Behavior
<!--- Tell us what happens instead of the expected behavior -->
<!--- If part of your bug report is a traceback, please first enter debug mode before triggering the error
To enter debug mode, set the environment variable `XONSH_DEBUG=1` _before_ starting `xonsh`.
On Linux and OSX, an easy way to to do this is to run `env XONSH_DEBUG=1 xonsh` -->

### Traceback (if applicable)

<details>

```
Traceback (most recent call last):
  File "/home/user/conda/lib/python3.8/site-packages/xonsh/__amalgam__.py", line 24534, in main
    sys.exit(main_xonsh(args))
  File "/home/user/conda/lib/python3.8/site-packages/xonsh/__amalgam__.py", line 24565, in main_xonsh
    shell.shell.cmdloop()
  File "/home/user/conda/lib/python3.8/site-packages/xonsh/ptk_shell/shell.py", line 224, in cmdloop
    line = self.singleline(auto_suggest=auto_suggest)
  File "/home/user/conda/lib/python3.8/site-packages/xonsh/ptk_shell/shell.py", line 193, in singleline
    line = self.prompter.prompt(**prompt_args)
  File "/home/user/conda/lib/python3.8/site-packages/prompt_toolkit/shortcuts/prompt.py", line 1013, in prompt
    return self.app.run(set_exception_handler=set_exception_handler)
  File "/home/user/conda/lib/python3.8/site-packages/prompt_toolkit/application/application.py", line 814, in run
    return loop.run_until_complete(
  File "/home/user/conda/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "/home/user/conda/lib/python3.8/site-packages/prompt_toolkit/application/application.py", line 781, in run_async
    return await _run_async2()
  File "/home/user/conda/lib/python3.8/site-packages/prompt_toolkit/application/application.py", line 769, in _run_async2
    await self.cancel_and_wait_for_background_tasks()
  File "/home/user/conda/lib/python3.8/site-packages/prompt_toolkit/application/application.py", line 870, in cancel_and_wait_for_background_tasks
    await task
  File "/home/user/conda/lib/python3.8/site-packages/prompt_toolkit/buffer.py", line 1854, in new_coroutine
    await coroutine(*a, **kw)
  File "/home/user/conda/lib/python3.8/site-packages/prompt_toolkit/buffer.py", line 1683, in async_completer
    async for completion in self.completer.get_completions_async(
  File "/home/user/conda/lib/python3.8/site-packages/prompt_toolkit/completion/base.py", line 269, in get_completions_async
    async for completion in completer.get_completions_async(
  File "/home/user/conda/lib/python3.8/site-packages/prompt_toolkit/completion/base.py", line 196, in get_completions_async
    for item in self.get_completions(document, complete_event):
  File "/home/user/conda/lib/python3.8/site-packages/xonsh/ptk_shell/completer.py", line 51, in get_completions
    completions, l = self.completer.complete(
  File "/home/user/conda/lib/python3.8/site-packages/xonsh/__amalgam__.py", line 125, in complete
    out = func(prefix, line, begidx, endidx, ctx)
  File "<xonsh-code>", line 2, in bad_completer
Exception
Xonsh encountered an issue during launch
Failback to /bin/sh
```

</details>

## Steps to Reproduce
<!--- Please try to write out a minimal reproducible snippet to trigger the bug, it will help us fix it! -->

Declare a bad completer:

```
def bad_completer(pref, line, *args):
    raise Exception

completer add test bad_completer start
```

Then start typing a command and hit `Tab`. No completion will  happen (that behavior is okay). When you hit enter, `xonsh` will crash and failback to `sh`.

## For community
⬇️  **Please click the 👍 reaction instead of leaving a `+1` or 👍  comment**



Discussion:
I think this may have been fixed on master or in a more recent version. Could you try updating to 0.9.22, or if it still fails there trying master, to see if this still occurs?
I tried with 0.9.22 using conda and master using `pip install -e .` and I see the same behavior for both cases.

Resolving patch:
diff --git a/xonsh/completer.py b/xonsh/completer.py
--- a/xonsh/completer.py
+++ b/xonsh/completer.py
@@ -3,6 +3,8 @@
 import builtins
 import collections.abc as cabc
 
+from xonsh.tools import print_exception
+
 
 class Completer(object):
     """This provides a list of optional completions for the xonsh shell."""
@@ -36,6 +38,13 @@ def complete(self, prefix, line, begidx, endidx, ctx=None):
                 out = func(prefix, line, begidx, endidx, ctx)
             except StopIteration:
                 return set(), len(prefix)
+            except Exception as e:
+                print_exception(
+                    f"Completer {func.__name__} raises exception when get "
+                    f"(prefix={repr(prefix)}, line={repr(line)}, begidx={repr(begidx)}, endidx={repr(endidx)}):\n"
+                    f"{e}"
+                )
+                return set(), len(prefix)
             if isinstance(out, cabc.Sequence):
                 res, lprefix = out
             else:


Test patch: