Repository: pallets/click (python)

Issue:
Unable to see EOFError exception in the traceback with disabled standalone mode
According to the [docs](https://click.palletsprojects.com/en/8.0.x/exceptions/?highlight=standalone%20mode#what-if-i-don-t-want-that) disabled standalone mode disables exception handling. However, click still suppresses `EOFError` (as well as `KeyboardInterrupt`) exceptions from the stack trace due to the `raise ... from None` construction, see:

https://github.com/pallets/click/blob/0aec1168ac591e159baf6f61026d6ae322c53aaf/src/click/core.py#L1066-L1068

I'd suggest changing the [line](https://github.com/pallets/click/blob/0aec1168ac591e159baf6f61026d6ae322c53aaf/src/click/core.py#L1068) to `raise Abort() from e`, so that users can see the original exception in the stack trace.

Real world example. I use asyncio streams that can [raise IncompleteReadError](https://github.com/python/cpython/blob/d03acd7270d66ddb8e987f9743405147ecc15087/Lib/asyncio/exceptions.py#L29) which inherited from `EOFError`, but I can't see the exception in the stack trace even with disabled standalone mode which looks like a bug to me. 

**How to replicate the bug:**

1. Create the following script:
```python
# t.py
import click


@click.command()
def cli():
    raise EOFError('I want to see this exception')


if __name__ == '__main__':
    cli.main(standalone_mode=False)
```

2. Run module `python -m t`

**Actual behavior:**

```python
Traceback (most recent call last):
  File "/Users/albert/.pyenv/versions/3.10.4/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/Users/albert/.pyenv/versions/3.10.4/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/private/var/folders/l0/lnq1ghps5yqc4vkgszlcz92m0000gp/T/tmp.X1tRYLrm/t.py", line 10, in <module>
    cli.main(standalone_mode=False)
  File "/private/var/folders/l0/lnq1ghps5yqc4vkgszlcz92m0000gp/T/tmp.X1tRYLrm/.venv/lib/python3.10/site-packages/click/core.py", line 1068, in main
    raise Abort() from None
click.exceptions.Abort
```

**Expected behavior:**

```python
Traceback (most recent call last):
  File "/private/var/folders/l0/lnq1ghps5yqc4vkgszlcz92m0000gp/T/tmp.X1tRYLrm/.venv/lib/python3.10/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/private/var/folders/l0/lnq1ghps5yqc4vkgszlcz92m0000gp/T/tmp.X1tRYLrm/.venv/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/private/var/folders/l0/lnq1ghps5yqc4vkgszlcz92m0000gp/T/tmp.X1tRYLrm/.venv/lib/python3.10/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/private/var/folders/l0/lnq1ghps5yqc4vkgszlcz92m0000gp/T/tmp.X1tRYLrm/t.py", line 6, in cli
    raise EOFError('I want to see this exception')
EOFError: I want to see this exception

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/albert/.pyenv/versions/3.10.4/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/Users/albert/.pyenv/versions/3.10.4/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/private/var/folders/l0/lnq1ghps5yqc4vkgszlcz92m0000gp/T/tmp.X1tRYLrm/t.py", line 10, in <module>
    cli.main(standalone_mode=False)
  File "/private/var/folders/l0/lnq1ghps5yqc4vkgszlcz92m0000gp/T/tmp.X1tRYLrm/.venv/lib/python3.10/site-packages/click/core.py", line 1068, in main
    raise Abort() from e
click.exceptions.Abort
```


Environment:

- Python version: 3.10.4
- Click version: 8.1.3


Pull request description:
Do not suppress EOFError/KeyboardInterrupt when standalone is disabled
Raise `Abort()` exception in a chain with the original exception.

<!--
Before opening a PR, open a ticket describing the issue or feature the
PR will address. Follow the steps in CONTRIBUTING.rst.

Replace this comment with a description of the change. Describe how it
addresses the linked ticket.
-->

<!--
Link to relevant issues or previous PRs, one per line. Use "fixes" to
automatically close an issue.
-->

- fixes #2379 

<!--
Ensure each step in CONTRIBUTING.rst is complete by adding an "x" to
each box below.

If only docs were changed, these aren't relevant and can be removed.
-->

Checklist:

- [x] Add tests that demonstrate the correct behavior of the change. Tests should fail without the change.
- [ ] Add or update relevant docs, in the docs folder and in code.
- [ ] Add an entry in `CHANGES.rst` summarizing the change and linking to the issue.
- [ ] Add `.. versionchanged::` entries in any relevant code docs.
- [x] Run `pre-commit` hooks and fix any issues.
- [x] Run `pytest` and `tox`, no tests failed.


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 674e449..37646a9 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -18,6 +18,8 @@ Unreleased
 -   Improve command name detection when using Shiv or PEX. :issue:`2332`
 -   Avoid showing empty lines if command help text is empty. :issue:`2368`
 -   ZSH completion script works when loaded from ``fpath``. :issue:`2344`.
+-   ``EOFError`` and ``KeyboardInterrupt`` tracebacks are not suppressed when
+    ``standalone_mode`` is disabled. :issue:`2380`
 
 
 Version 8.1.3
diff --git a/src/click/core.py b/src/click/core.py
index 9a5f88a..a6fc501 100644
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -1086,9 +1086,9 @@ class BaseCommand:
                     # even always obvious that `rv` indicates success/failure
                     # by its truthiness/falsiness
                     ctx.exit()
-            except (EOFError, KeyboardInterrupt):
+            except (EOFError, KeyboardInterrupt) as e:
                 echo(file=sys.stderr)
-                raise Abort() from None
+                raise Abort() from e
             except ClickException as e:
                 if not standalone_mode:
                     raise


Test patch:
diff --git a/tests/test_commands.py b/tests/test_commands.py
index d72a182..ed9d96f 100644
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -397,3 +397,15 @@ def test_group_invoke_collects_used_option_prefixes(runner):
 
     runner.invoke(group, ["command1"])
     assert opt_prefixes == {"-", "--", "~", "+"}
+
+
+@pytest.mark.parametrize("exc", (EOFError, KeyboardInterrupt))
+def test_abort_exceptions_with_disabled_standalone_mode(runner, exc):
+    @click.command()
+    def cli():
+        raise exc("catch me!")
+
+    rv = runner.invoke(cli, standalone_mode=False)
+    assert rv.exit_code == 1
+    assert isinstance(rv.exception.__cause__, exc)
+    assert rv.exception.__cause__.args == ("catch me!",)