diff --git a/IPython/core/debugger.py b/IPython/core/debugger.py
index 02d01ad..69cc55f 100644
--- a/IPython/core/debugger.py
+++ b/IPython/core/debugger.py
@@ -2,6 +2,71 @@
 """
 Pdb debugger class.
 
+
+This is an extension to PDB which adds a number of new features.
+Note that there is also the `IPython.terminal.debugger` class which provides UI
+improvements.
+
+We also strongly recommend to use this via the `ipdb` package, which provides
+extra configuration options.
+
+Among other things, this subclass of PDB:
+ - supports many IPython magics like pdef/psource
+ - hide frames in tracebacks based on `__tracebackhide__`
+ - allows to skip frames based on `__debuggerskip__`
+
+The skipping and hiding frames are configurable via the `skip_predicates`
+command.
+
+By default, frames from readonly files will be hidden, frames containing
+``__tracebackhide__=True`` will be hidden.
+
+Frames containing ``__debuggerskip__`` will be stepped over, frames who's parent
+frames value of ``__debuggerskip__`` is ``True`` will be skipped.
+
+    >>> def helper_1():
+    ...     print("don't step in me")
+    ...
+    ...
+    ... def helper_2():
+    ...     print("in me neither")
+    ...
+
+One can define a decorator that wraps a function between the two helpers:
+
+    >>> def pdb_skipped_decorator(function):
+    ...
+    ...
+    ...     def wrapped_fn(*args, **kwargs):
+    ...         __debuggerskip__ = True
+    ...         helper_1()
+    ...         __debuggerskip__ = False
+    ...         result = function(*args, **kwargs)
+    ...         __debuggerskip__ = True
+    ...         helper_2()
+    ...         return result
+    ...
+    ...     return wrapped_fn
+
+When decorating a function, ipdb will directly step into ``bar()`` by
+default:
+
+    >>> @foo_decorator
+    ... def bar(x, y):
+    ...     return x * y
+
+
+You can toggle the behavior with
+
+    ipdb> skip_predicates debuggerskip false
+
+or configure it in your ``.pdbrc``
+
+
+
+Licencse
+--------
+
 Modified from the standard pdb.Pdb class to avoid including readline, so that
 the command line completion of other programs which include this isn't
 damaged.
@@ -9,11 +74,16 @@ damaged.
 In the future, this class will be expanded with improvements over the standard
 pdb.
 
-The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
-changes. Licensing should therefore be under the standard Python terms.  For
-details on the PSF (Python Software Foundation) standard license, see:
+The original code in this file is mainly lifted out of cmd.py in Python 2.2,
+with minor changes. Licensing should therefore be under the standard Python
+terms.  For details on the PSF (Python Software Foundation) standard license,
+see:
 
 https://docs.python.org/2/license.html
+
+
+All the changes since then are under the same license as IPython.
+
 """
 
 #*****************************************************************************
@@ -51,6 +121,9 @@ from pdb import Pdb as OldPdb
 # it does so with some limitations.  The rest of this support is implemented in
 # the Tracer constructor.
 
+DEBUGGERSKIP = "__debuggerskip__"
+
+
 def make_arrow(pad):
     """generate the leading arrow in front of traceback or debugger"""
     if pad >= 2:
@@ -206,7 +279,12 @@ class Pdb(OldPdb):
 
     """
 
-    default_predicates = {"tbhide": True, "readonly": False, "ipython_internal": True}
+    default_predicates = {
+        "tbhide": True,
+        "readonly": False,
+        "ipython_internal": True,
+        "debuggerskip": True,
+    }
 
     def __init__(self, color_scheme=None, completekey=None,
                  stdin=None, stdout=None, context=5, **kwargs):
@@ -305,6 +383,7 @@ class Pdb(OldPdb):
         # list of predicates we use to skip frames
         self._predicates = self.default_predicates
 
+    #
     def set_colors(self, scheme):
         """Shorthand access to the color table scheme selector method."""
         self.color_scheme_table.set_active_scheme(scheme)
@@ -804,10 +883,53 @@ class Pdb(OldPdb):
 
     do_w = do_where
 
+    def break_anywhere(self, frame):
+        """
+
+        _stop_in_decorator_internals is overly restrictive, as we may still want
+        to trace function calls, so we need to also update break_anywhere so
+        that is we don't `stop_here`, because of debugger skip, we may still
+        stop at any point inside the function
+
+        """
+        if self._predicates["debuggerskip"]:
+            if DEBUGGERSKIP in frame.f_code.co_varnames:
+                return True
+            if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
+                return True
+        return super().break_anywhere(frame)
+
+    @skip_doctest
+    def _is_in_decorator_internal_and_should_skip(self, frame):
+        """
+        Utility to tell us whether we are in a decorator internal and should stop.
+
+
+
+        """
+
+        # if we are disabled don't skip
+        if not self._predicates["debuggerskip"]:
+            return False
+
+        # if frame is tagged, skip by default.
+        if DEBUGGERSKIP in frame.f_code.co_varnames:
+            return True
+
+        # if parent frame value set to True skip as well.
+        if frame.f_back and self._get_frame_locals(frame.f_back).get(DEBUGGERSKIP):
+            return True
+
+        return False
+
     def stop_here(self, frame):
         """Check if pdb should stop here"""
         if not super().stop_here(frame):
             return False
+
+        if self._is_in_decorator_internal_and_should_skip(frame) is True:
+            return False
+
         hidden = False
         if self.skip_hidden:
             hidden = self._hidden_predicate(frame)
@@ -929,10 +1051,10 @@ class Pdb(OldPdb):
 class InterruptiblePdb(Pdb):
     """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
 
-    def cmdloop(self):
+    def cmdloop(self, intro=None):
         """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
         try:
-            return OldPdb.cmdloop(self)
+            return OldPdb.cmdloop(self, intro=intro)
         except KeyboardInterrupt:
             self.stop_here = lambda frame: False
             self.do_quit("")
diff --git a/IPython/core/tests/test_debugger.py b/IPython/core/tests/test_debugger.py
index 7c94592..08a2473 100644
--- a/IPython/core/tests/test_debugger.py
+++ b/IPython/core/tests/test_debugger.py
@@ -326,6 +326,118 @@ def test_xmode_skip():
     child.close()
 
 
+skip_decorators_blocks = (
+    """
+    def helper_1():
+        pass # should not stop here
+    """,
+    """
+    def helper_2():
+        pass # should not stop here
+    """,
+    """
+    def pdb_skipped_decorator(function):
+        def wrapped_fn(*args, **kwargs):
+            __debuggerskip__ = True
+            helper_1()
+            __debuggerskip__ = False
+            result = function(*args, **kwargs)
+            __debuggerskip__ = True
+            helper_2()
+            return result
+        return wrapped_fn
+    """,
+    """
+    @pdb_skipped_decorator
+    def bar(x, y):
+        return x * y
+    """,
+    """import IPython.terminal.debugger as ipdb""",
+    """
+    def f():
+        ipdb.set_trace()
+        bar(3, 4)
+    """,
+    """
+    f()
+    """,
+)
+
+
+def _decorator_skip_setup():
+    import pexpect
+
+    env = os.environ.copy()
+    env["IPY_TEST_SIMPLE_PROMPT"] = "1"
+
+    child = pexpect.spawn(
+        sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
+    )
+    child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
+
+    child.expect("IPython")
+    child.expect("\n")
+
+    dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
+    in_prompt_number = 1
+    for cblock in dedented_blocks:
+        child.expect_exact(f"In [{in_prompt_number}]:")
+        in_prompt_number += 1
+        for line in cblock.splitlines():
+            child.sendline(line)
+            child.expect_exact(line)
+        child.sendline("")
+    return child
+
+
+@skip_win32
+def test_decorator_skip():
+    """test that decorator frames can be skipped."""
+
+    child = _decorator_skip_setup()
+
+    child.expect_exact("3     bar(3, 4)")
+    child.expect("ipdb>")
+
+    child.expect("ipdb>")
+    child.sendline("step")
+    child.expect_exact("step")
+
+    child.expect_exact("1 @pdb_skipped_decorator")
+
+    child.sendline("s")
+    child.expect_exact("return x * y")
+
+    child.close()
+
+
+@skip_win32
+def test_decorator_skip_disabled():
+    """test that decorator frame skipping can be disabled"""
+
+    child = _decorator_skip_setup()
+
+    child.expect_exact("3     bar(3, 4)")
+
+    for input_, expected in [
+        ("skip_predicates debuggerskip False", ""),
+        ("skip_predicates", "debuggerskip : False"),
+        ("step", "---> 2     def wrapped_fn"),
+        ("step", "----> 3         __debuggerskip__"),
+        ("step", "----> 4         helper_1()"),
+        ("step", "---> 1 def helper_1():"),
+        ("next", "----> 2     pass"),
+        ("next", "--Return--"),
+        ("next", "----> 5         __debuggerskip__ = False"),
+    ]:
+        child.expect("ipdb>")
+        child.sendline(input_)
+        child.expect_exact(input_)
+        child.expect_exact(expected)
+
+    child.close()
+
+
 @skip_win32
 def test_where_erase_value():
     """Test that `where` does not access f_locals and erase values."""
diff --git a/IPython/terminal/debugger.py b/IPython/terminal/debugger.py
index 9ec2466..66ffc19 100644
--- a/IPython/terminal/debugger.py
+++ b/IPython/terminal/debugger.py
@@ -71,7 +71,7 @@ class TerminalPdb(Pdb):
             enable_history_search=True,
             mouse_support=self.shell.mouse_support,
             complete_style=self.shell.pt_complete_style,
-            style=self.shell.style,
+            style=getattr(self.shell, "style", None),
             color_depth=self.shell.color_depth,
         )
 
@@ -96,7 +96,6 @@ class TerminalPdb(Pdb):
         # prompt itself in a different thread (we can't start an event loop
         # within an event loop). This new thread won't have any event loop
         # running, and here we run our prompt-loop.
-
         self.preloop()
 
         try:
@@ -131,7 +130,6 @@ class TerminalPdb(Pdb):
 
                     if keyboard_interrupt:
                         raise KeyboardInterrupt
-
                 line = self.precmd(line)
                 stop = self.onecmd(line)
                 stop = self.postcmd(stop, line)
diff --git a/docs/source/whatsnew/version7.rst b/docs/source/whatsnew/version7.rst
index 84fccd2..990e0eb 100644
--- a/docs/source/whatsnew/version7.rst
+++ b/docs/source/whatsnew/version7.rst
@@ -2,6 +2,7 @@
  7.x Series
 ============
 
+
 .. _version 7.28:
 
 IPython 7.28
@@ -543,7 +544,7 @@ Change of API and exposed objects automatically detected using `frappuccino
 <https://pypi.org/project/frappuccino/>`_ (still in beta):
 
 
-The following items are new and mostly related to understanding ``__tracebackbide__``::
+The following items are new and mostly related to understanding ``__tracebackhide__``::
 
     + IPython.core.debugger.Pdb.do_down(self, arg)
     + IPython.core.debugger.Pdb.do_skip_hidden(self, arg)