Repository: gotcha/ipdb

Issue:
Support launch_ipdb_on_exception as a function decorator
For some time I have used this in my personal debug tools:

```python
import ipdb

class Decontext(object):
    """
    Makes a context manager also act as decorator
    """
    def __init__(self, context_manager):
        self._cm = context_manager

    def __enter__(self):
        return self._cm.__enter__()

    def __exit__(self, *args, **kwds):
        return self._cm.__exit__(*args, **kwds)

    def __call__(self, func):
        def wrapper(*args, **kwds):
            with self:
                return func(*args, **kwds)
        return wrapper

pm = Decontext(ipdb.launch_ipdb_on_exception())
```

It allows me to do this:

```
from mydebugtools import pm

@pm
def main():
    # whatever
```

And ipdb is triggered on an exception at any level below that function in the call stack. 

This is kind of a silly convenience thing, maybe just personal preference, but I thought others might benefit from seeing it. Would it be reasonable to add this functionality to ipdb? If so, would it be sensible to use this (not named `pm`), or is there a better way?

Discussion:
gotcha: Thanks for the PR. Is there a reason it is in draft state ?

Would you mind adding a commit which fixes `decorator` dependency for Python 3.4 in `setup.py` ?

Turn

      ':python_version == "3.4"': ['ipython >= 6.0.0, < 7.0.0', 'toml >= 0.10.2'],

into

      ':python_version == "3.4"': ['ipython >= 6.0.0, < 7.0.0', 'toml >= 0.10.2', 'decorator < 5'],

I think it should fix CI.
alanbernstein: > Thanks for the PR. Is there a reason it is in draft state ?

I'm happy to contribute, thanks for being welcoming. I didn't understand the version-specific dependency issue with 3.4, I had planned to look into it over the weekend.

> Would you mind adding a commit which fixes `decorator` dependency for Python 3.4 in `setup.py` ?

Added.
gotcha: > I suppose the addition of Decontext class is pulling down the coverage - maybe it makes more sense to just use the decorator package?

As `decorator` is a dependency of `IPython` since 4.0.0 and we depend on later versions of `IPython` for all Python versions, I think you are right that it would make sense to use `decorator`.

Can you fix that point ?

When specifying the dependency in `setup.py`, can you add a comment explaining that `decorator` is also a dependency of `Ipython` ? 
alanbernstein: Sorry to leave this hanging, this was a little more time consuming as I had to refresh my memory of how decorators and context managers interact here. I wasn't aware that the `decorator` library provides a replacement `contextmanager` that does just the right thing here (https://github.com/micheles/decorator/blob/master/docs/documentation.md#contextmanager).

I added a simple comment to `setup.py`, but I don't fully grok the dependency on decorator, I may have misunderstood what you asked for.

I also corrected some unrelated formatting issues in README.rst, would you prefer a different PR for that?
codecov-commenter: # [Codecov](https://codecov.io/gh/gotcha/ipdb/pull/226?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle) Report
> Merging [#226](https://codecov.io/gh/gotcha/ipdb/pull/226?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle) (76c34f1) into [master](https://codecov.io/gh/gotcha/ipdb/commit/d11ec0c29ef489b90c6cef3c42034d7e98854ad6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle) (d11ec0c) will **increase** coverage by `0.08%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/gotcha/ipdb/pull/226/graphs/tree.svg?width=650&height=150&src=pr&token=rLlY1IZ1E0&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle)](https://codecov.io/gh/gotcha/ipdb/pull/226?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle)

```diff
@@            Coverage Diff             @@
##           master     #226      +/-   ##
==========================================
+ Coverage   79.69%   79.78%   +0.08%     
==========================================
  Files           7        7              
  Lines         453      455       +2     
==========================================
+ Hits          361      363       +2     
  Misses         92       92              
```


| [Impacted Files](https://codecov.io/gh/gotcha/ipdb/pull/226?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle) | Coverage Δ | |
|---|---|---|
| [setup.py](https://codecov.io/gh/gotcha/ipdb/pull/226/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle#diff-c2V0dXAucHk=) | `100.00% <ø> (ø)` | |
| [ipdb/\_\_init\_\_.py](https://codecov.io/gh/gotcha/ipdb/pull/226/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle#diff-aXBkYi9fX2luaXRfXy5weQ==) | `100.00% <100.00%> (ø)` | |
| [ipdb/\_\_main\_\_.py](https://codecov.io/gh/gotcha/ipdb/pull/226/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle#diff-aXBkYi9fX21haW5fXy5weQ==) | `65.12% <100.00%> (+0.17%)` | :arrow_up: |
| [tests/test\_import.py](https://codecov.io/gh/gotcha/ipdb/pull/226/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle#diff-dGVzdHMvdGVzdF9pbXBvcnQucHk=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/gotcha/ipdb/pull/226?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/gotcha/ipdb/pull/226?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle). Last update [6694611...76c34f1](https://codecov.io/gh/gotcha/ipdb/pull/226?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Godefroid+Chapelle).


Patch:
diff --git a/HISTORY.txt b/HISTORY.txt
index 1d45eba..7bd94f2 100644
--- a/HISTORY.txt
+++ b/HISTORY.txt
@@ -4,7 +4,8 @@ Changelog
 0.13.8 (unreleased)
 -------------------
 
-- Nothing changed yet.
+- Add the convenience function ``iex()``.
+  [alanbernstein]
 
 
 0.13.7 (2021-03-16)
diff --git a/README.rst b/README.rst
index e8d7ef8..96f6882 100644
--- a/README.rst
+++ b/README.rst
@@ -106,6 +106,16 @@ You can also enclose code with the ``with`` statement to launch ipdb if an excep
    Adding a context manager implies dropping Python 2.4 support.
    Use ``ipdb==0.6`` with 2.4.
 
+Or you can use ``iex`` as a function decorator to launch ipdb if an exception is raised:
+
+.. code-block:: python
+
+        from ipdb import iex
+
+        @iex
+        def main():
+            [...]
+
 .. warning::
    Using ``from future import print_function`` for Python 3 compat implies dropping Python 2.5 support.
    Use ``ipdb<=0.8`` with 2.5.
diff --git a/ipdb/__init__.py b/ipdb/__init__.py
index fb0970e..75e8fed 100644
--- a/ipdb/__init__.py
+++ b/ipdb/__init__.py
@@ -4,7 +4,7 @@
 # Redistributable under the revised BSD license
 # https://opensource.org/licenses/BSD-3-Clause
 
-from ipdb.__main__ import set_trace, post_mortem, pm, run             # noqa
+from ipdb.__main__ import set_trace, post_mortem, pm, run, iex        # noqa
 from ipdb.__main__ import runcall, runeval, launch_ipdb_on_exception  # noqa
 
 from ipdb.stdout import sset_trace, spost_mortem, spm                 # noqa
diff --git a/ipdb/__main__.py b/ipdb/__main__.py
index f8f877c..beee6c9 100644
--- a/ipdb/__main__.py
+++ b/ipdb/__main__.py
@@ -8,7 +8,7 @@ from __future__ import print_function
 import os
 import sys
 
-from contextlib import contextmanager
+from decorator import contextmanager
 
 __version__ = '0.13.8.dev0'
 
@@ -228,6 +228,10 @@ def launch_ipdb_on_exception():
         pass
 
 
+# iex is a concise alias
+iex = launch_ipdb_on_exception()
+
+
 _usage = """\
 usage: python -m ipdb [-m] [-c command] ... pyfile [arg] ...
 
diff --git a/setup.py b/setup.py
index 1aa4203..6d2344c 100644
--- a/setup.py
+++ b/setup.py
@@ -61,7 +61,8 @@ setup(name='ipdb',
               'toml >= 0.10.2'
           ],
           # No support for python 3.0, 3.1, 3.2.
-          ':python_version == "3.4"': ['ipython >= 6.0.0, < 7.0.0', 'toml >= 0.10.2'],
+          # decorator is also a dependency of Ipython.
+          ':python_version == "3.4"': ['ipython >= 6.0.0, < 7.0.0', 'toml >= 0.10.2', 'decorator < 5.0.0'],
           ':python_version == "3.5"': ['ipython >= 7.0.0, < 7.10.0', 'toml >= 0.10.2'],
           ':python_version == "3.6"': ['ipython >= 7.10.0, < 7.17.0', 'toml >= 0.10.2'],
           ':python_version > "3.6"': ['ipython >= 7.17.0', 'toml >= 0.10.2'],


Test patch:
diff --git a/tests/test_import.py b/tests/test_import.py
index 5094792..7178d63 100644
--- a/tests/test_import.py
+++ b/tests/test_import.py
@@ -10,10 +10,11 @@ import unittest
 class ImportTest(unittest.TestCase):
 
     def test_import(self):
-        from ipdb import set_trace, post_mortem, pm, run, runcall, runeval
+        from ipdb import set_trace, post_mortem, pm, iex, run, runcall, runeval
         set_trace  # please pyflakes
         post_mortem  # please pyflakes
         pm  # please pyflakes
+        iex # please pyflakes
         run  # please pyflakes
         runcall  # please pyflakes
         runeval  # please pyflakes