We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Event callbacks which register/unregister other callbacks lead to surprising behavior
tl;dr: the loop in [this line](https://github.com/ipython/ipython/blob/1d63c56f6d69d9f4cc47adcb87a7f80744a48260/IPython/core/events.py#L72) isn't robust to `register`/`unregister`. What's the desired behavior?
### The problem

We ran into a funny situation in colaboratory, involving `post_run_cell` callbacks which need to potentially add or remove other `post_run_cell` callbacks. Here's a really simplified version:

``` python
ip = get_ipython()
ev = ip.events

def func1(*unused_args):
    print 'invoking func1'
    ev.unregister('post_run_cell', func1)
    ev.register('post_run_cell', func3)

def func2(*unused_args):
    print 'invoking func2'
    ev.unregister('post_run_cell', func2)

def func3(*unused_args):
    print 'invoking func3'

ev.register('post_run_cell', func1)
ev.register('post_run_cell', func2)
```

In principle, this should invoke the three functions in order. In reality, it invokes `func1` and `func3`, but _not_ `func2`. Even worse, at the end of this cell's execution, the list of registered callbacks is `[func2, func3]`, which is _not_ the list of callbacks we saw execute. So `func2`, the only function that stays in the list of callbacks the whole time, is the only one we don't execute. 😉 

The cause is easy to see after checking out [this line](https://github.com/ipython/ipython/blob/1d63c56f6d69d9f4cc47adcb87a7f80744a48260/IPython/core/events.py#L72): 
- on the first iteration of the loop, `func1` is our callback, and `[func1, func2]` is our list of callbacks. `func1` executes, and our list of callbacks is now `[func2, func3]`.
- the next loop iteration picks up at the second element of the list, namely `func3`. unfortunately, we've now skipped `func2`. sadface.

The lesson is, of course, never mutate a list as you iterate over it.
## Potential solutions

I'm happy to send a PR, but first, I want to make sure we're on the same page about what the **desired** semantics are here. 

I think we want to be flexible: given that the only exposed operations for callbacks are remove and append (and thankfully [**not** reset](https://github.com/ipython/ipython/commit/4043b271fee4f6c36c99c8038018d54cd86b94eb)), we can ensure
1. we execute new callbacks appended to the list, and 
2. we skip removed callbacks if we haven't already executed them.

Other options all involve preventing this behavior. We could freeze state by instead making a copy of the list before we iterate over it, whence any modifications would only be visible on the next triggering of the event. Or we could add code to completely disallow modifications to the currently-executing list of callbacks for the duration of `trigger`, meaning a user would get an error if they tried.
## Our use case

A common pattern we've used is "one-shot callbacks" -- a callback which, when invoked, removes itself from the list of callbacks. We use this for some forms of output manipulation, as well as occasionally to do one-time cleanup that needs to happen after the user's current code completes. 

With the proposed solution, this is easy -- the body of a callback just includes a call to deregister itself. This currently works great, unless the one-shot callback is not the last callback in the list. (The example above was distilled from a variant of this -- a one-shot callback was registering another one-shot callback.) It also allows for forcing a callback to be idempotent -- register as many times as you'd like, and the first one can deregister any copies it finds.

With either of the other solutions, we're stuck basically inventing our own event system, and using it to handle cleanup.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `IPython/core/events.py`
Content:
```
1 """Infrastructure for registering and firing callbacks on application events.
2 
3 Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to
4 be called at specific times, or a collection of alternative methods to try,
5 callbacks are designed to be used by extension authors. A number of callbacks
6 can be registered for the same event without needing to be aware of one another.
7 
8 The functions defined in this module are no-ops indicating the names of available
9 events and the arguments which will be passed to them.
10 
11 .. note::
12 
13    This API is experimental in IPython 2.0, and may be revised in future versions.
14 """
15 from __future__ import print_function
16 
17 class EventManager(object):
18     """Manage a collection of events and a sequence of callbacks for each.
19     
20     This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
21     instances as an ``events`` attribute.
22     
23     .. note::
24 
25        This API is experimental in IPython 2.0, and may be revised in future versions.
26     """
27     def __init__(self, shell, available_events):
28         """Initialise the :class:`CallbackManager`.
29         
30         Parameters
31         ----------
32         shell
33           The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
34         available_callbacks
35           An iterable of names for callback events.
36         """
37         self.shell = shell
38         self.callbacks = {n:[] for n in available_events}
39     
40     def register(self, event, function):
41         """Register a new event callback
42         
43         Parameters
44         ----------
45         event : str
46           The event for which to register this callback.
47         function : callable
48           A function to be called on the given event. It should take the same
49           parameters as the appropriate callback prototype.
50         
51         Raises
52         ------
53         TypeError
54           If ``function`` is not callable.
55         KeyError
56           If ``event`` is not one of the known events.
57         """
58         if not callable(function):
59             raise TypeError('Need a callable, got %r' % function)
60         self.callbacks[event].append(function)
61     
62     def unregister(self, event, function):
63         """Remove a callback from the given event."""
64         self.callbacks[event].remove(function)
65     
66     def trigger(self, event, *args, **kwargs):
67         """Call callbacks for ``event``.
68         
69         Any additional arguments are passed to all callbacks registered for this
70         event. Exceptions raised by callbacks are caught, and a message printed.
71         """
72         for func in self.callbacks[event]:
73             try:
74                 func(*args, **kwargs)
75             except Exception:
76                 print("Error in callback {} (for {}):".format(func, event))
77                 self.shell.showtraceback()
78 
79 # event_name -> prototype mapping
80 available_events = {}
81 
82 def _define_event(callback_proto):
83     available_events[callback_proto.__name__] = callback_proto
84     return callback_proto
85 
86 # ------------------------------------------------------------------------------
87 # Callback prototypes
88 #
89 # No-op functions which describe the names of available events and the
90 # signatures of callbacks for those events.
91 # ------------------------------------------------------------------------------
92 
93 @_define_event
94 def pre_execute():
95     """Fires before code is executed in response to user/frontend action.
96     
97     This includes comm and widget messages and silent execution, as well as user
98     code cells."""
99     pass
100 
101 @_define_event
102 def pre_run_cell():
103     """Fires before user-entered code runs."""
104     pass
105 
106 @_define_event
107 def post_execute():
108     """Fires after code is executed in response to user/frontend action.
109     
110     This includes comm and widget messages and silent execution, as well as user
111     code cells."""
112     pass
113 
114 @_define_event
115 def post_run_cell():
116     """Fires after user-entered code runs."""
117     pass
118 
119 @_define_event
120 def shell_initialized(ip):
121     """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
122     
123     This is before extensions and startup scripts are loaded, so it can only be
124     set by subclassing.
125     
126     Parameters
127     ----------
128     ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
129       The newly initialised shell.
130     """
131     pass
132
```
--- 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/IPython/core/events.py b/IPython/core/events.py
--- a/IPython/core/events.py
+++ b/IPython/core/events.py
@@ -69,7 +69,7 @@
         Any additional arguments are passed to all callbacks registered for this
         event. Exceptions raised by callbacks are caught, and a message printed.
         """
-        for func in self.callbacks[event]:
+        for func in self.callbacks[event][:]:
             try:
                 func(*args, **kwargs)
             except Exception:

{"golden_diff": "diff --git a/IPython/core/events.py b/IPython/core/events.py\n--- a/IPython/core/events.py\n+++ b/IPython/core/events.py\n@@ -69,7 +69,7 @@\n         Any additional arguments are passed to all callbacks registered for this\n         event. Exceptions raised by callbacks are caught, and a message printed.\n         \"\"\"\n-        for func in self.callbacks[event]:\n+        for func in self.callbacks[event][:]:\n             try:\n                 func(*args, **kwargs)\n             except Exception:\n", "issue": "Event callbacks which register/unregister other callbacks lead to surprising behavior\ntl;dr: the loop in [this line](https://github.com/ipython/ipython/blob/1d63c56f6d69d9f4cc47adcb87a7f80744a48260/IPython/core/events.py#L72) isn't robust to `register`/`unregister`. What's the desired behavior?\n### The problem\n\nWe ran into a funny situation in colaboratory, involving `post_run_cell` callbacks which need to potentially add or remove other `post_run_cell` callbacks. Here's a really simplified version:\n\n``` python\nip = get_ipython()\nev = ip.events\n\ndef func1(*unused_args):\n    print 'invoking func1'\n    ev.unregister('post_run_cell', func1)\n    ev.register('post_run_cell', func3)\n\ndef func2(*unused_args):\n    print 'invoking func2'\n    ev.unregister('post_run_cell', func2)\n\ndef func3(*unused_args):\n    print 'invoking func3'\n\nev.register('post_run_cell', func1)\nev.register('post_run_cell', func2)\n```\n\nIn principle, this should invoke the three functions in order. In reality, it invokes `func1` and `func3`, but _not_ `func2`. Even worse, at the end of this cell's execution, the list of registered callbacks is `[func2, func3]`, which is _not_ the list of callbacks we saw execute. So `func2`, the only function that stays in the list of callbacks the whole time, is the only one we don't execute. \ud83d\ude09 \n\nThe cause is easy to see after checking out [this line](https://github.com/ipython/ipython/blob/1d63c56f6d69d9f4cc47adcb87a7f80744a48260/IPython/core/events.py#L72): \n- on the first iteration of the loop, `func1` is our callback, and `[func1, func2]` is our list of callbacks. `func1` executes, and our list of callbacks is now `[func2, func3]`.\n- the next loop iteration picks up at the second element of the list, namely `func3`. unfortunately, we've now skipped `func2`. sadface.\n\nThe lesson is, of course, never mutate a list as you iterate over it.\n## Potential solutions\n\nI'm happy to send a PR, but first, I want to make sure we're on the same page about what the **desired** semantics are here. \n\nI think we want to be flexible: given that the only exposed operations for callbacks are remove and append (and thankfully [**not** reset](https://github.com/ipython/ipython/commit/4043b271fee4f6c36c99c8038018d54cd86b94eb)), we can ensure\n1. we execute new callbacks appended to the list, and \n2. we skip removed callbacks if we haven't already executed them.\n\nOther options all involve preventing this behavior. We could freeze state by instead making a copy of the list before we iterate over it, whence any modifications would only be visible on the next triggering of the event. Or we could add code to completely disallow modifications to the currently-executing list of callbacks for the duration of `trigger`, meaning a user would get an error if they tried.\n## Our use case\n\nA common pattern we've used is \"one-shot callbacks\" -- a callback which, when invoked, removes itself from the list of callbacks. We use this for some forms of output manipulation, as well as occasionally to do one-time cleanup that needs to happen after the user's current code completes. \n\nWith the proposed solution, this is easy -- the body of a callback just includes a call to deregister itself. This currently works great, unless the one-shot callback is not the last callback in the list. (The example above was distilled from a variant of this -- a one-shot callback was registering another one-shot callback.) It also allows for forcing a callback to be idempotent -- register as many times as you'd like, and the first one can deregister any copies it finds.\n\nWith either of the other solutions, we're stuck basically inventing our own event system, and using it to handle cleanup.\n\n", "before_files": [{"content": "\"\"\"Infrastructure for registering and firing callbacks on application events.\n\nUnlike :mod:`IPython.core.hooks`, which lets end users set single functions to\nbe called at specific times, or a collection of alternative methods to try,\ncallbacks are designed to be used by extension authors. A number of callbacks\ncan be registered for the same event without needing to be aware of one another.\n\nThe functions defined in this module are no-ops indicating the names of available\nevents and the arguments which will be passed to them.\n\n.. note::\n\n   This API is experimental in IPython 2.0, and may be revised in future versions.\n\"\"\"\nfrom __future__ import print_function\n\nclass EventManager(object):\n    \"\"\"Manage a collection of events and a sequence of callbacks for each.\n    \n    This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`\n    instances as an ``events`` attribute.\n    \n    .. note::\n\n       This API is experimental in IPython 2.0, and may be revised in future versions.\n    \"\"\"\n    def __init__(self, shell, available_events):\n        \"\"\"Initialise the :class:`CallbackManager`.\n        \n        Parameters\n        ----------\n        shell\n          The :class:`~IPython.core.interactiveshell.InteractiveShell` instance\n        available_callbacks\n          An iterable of names for callback events.\n        \"\"\"\n        self.shell = shell\n        self.callbacks = {n:[] for n in available_events}\n    \n    def register(self, event, function):\n        \"\"\"Register a new event callback\n        \n        Parameters\n        ----------\n        event : str\n          The event for which to register this callback.\n        function : callable\n          A function to be called on the given event. It should take the same\n          parameters as the appropriate callback prototype.\n        \n        Raises\n        ------\n        TypeError\n          If ``function`` is not callable.\n        KeyError\n          If ``event`` is not one of the known events.\n        \"\"\"\n        if not callable(function):\n            raise TypeError('Need a callable, got %r' % function)\n        self.callbacks[event].append(function)\n    \n    def unregister(self, event, function):\n        \"\"\"Remove a callback from the given event.\"\"\"\n        self.callbacks[event].remove(function)\n    \n    def trigger(self, event, *args, **kwargs):\n        \"\"\"Call callbacks for ``event``.\n        \n        Any additional arguments are passed to all callbacks registered for this\n        event. Exceptions raised by callbacks are caught, and a message printed.\n        \"\"\"\n        for func in self.callbacks[event]:\n            try:\n                func(*args, **kwargs)\n            except Exception:\n                print(\"Error in callback {} (for {}):\".format(func, event))\n                self.shell.showtraceback()\n\n# event_name -> prototype mapping\navailable_events = {}\n\ndef _define_event(callback_proto):\n    available_events[callback_proto.__name__] = callback_proto\n    return callback_proto\n\n# ------------------------------------------------------------------------------\n# Callback prototypes\n#\n# No-op functions which describe the names of available events and the\n# signatures of callbacks for those events.\n# ------------------------------------------------------------------------------\n\n@_define_event\ndef pre_execute():\n    \"\"\"Fires before code is executed in response to user/frontend action.\n    \n    This includes comm and widget messages and silent execution, as well as user\n    code cells.\"\"\"\n    pass\n\n@_define_event\ndef pre_run_cell():\n    \"\"\"Fires before user-entered code runs.\"\"\"\n    pass\n\n@_define_event\ndef post_execute():\n    \"\"\"Fires after code is executed in response to user/frontend action.\n    \n    This includes comm and widget messages and silent execution, as well as user\n    code cells.\"\"\"\n    pass\n\n@_define_event\ndef post_run_cell():\n    \"\"\"Fires after user-entered code runs.\"\"\"\n    pass\n\n@_define_event\ndef shell_initialized(ip):\n    \"\"\"Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.\n    \n    This is before extensions and startup scripts are loaded, so it can only be\n    set by subclassing.\n    \n    Parameters\n    ----------\n    ip : :class:`~IPython.core.interactiveshell.InteractiveShell`\n      The newly initialised shell.\n    \"\"\"\n    pass\n", "path": "IPython/core/events.py"}], "after_files": [{"content": "\"\"\"Infrastructure for registering and firing callbacks on application events.\n\nUnlike :mod:`IPython.core.hooks`, which lets end users set single functions to\nbe called at specific times, or a collection of alternative methods to try,\ncallbacks are designed to be used by extension authors. A number of callbacks\ncan be registered for the same event without needing to be aware of one another.\n\nThe functions defined in this module are no-ops indicating the names of available\nevents and the arguments which will be passed to them.\n\n.. note::\n\n   This API is experimental in IPython 2.0, and may be revised in future versions.\n\"\"\"\nfrom __future__ import print_function\n\nclass EventManager(object):\n    \"\"\"Manage a collection of events and a sequence of callbacks for each.\n    \n    This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`\n    instances as an ``events`` attribute.\n    \n    .. note::\n\n       This API is experimental in IPython 2.0, and may be revised in future versions.\n    \"\"\"\n    def __init__(self, shell, available_events):\n        \"\"\"Initialise the :class:`CallbackManager`.\n        \n        Parameters\n        ----------\n        shell\n          The :class:`~IPython.core.interactiveshell.InteractiveShell` instance\n        available_callbacks\n          An iterable of names for callback events.\n        \"\"\"\n        self.shell = shell\n        self.callbacks = {n:[] for n in available_events}\n    \n    def register(self, event, function):\n        \"\"\"Register a new event callback\n        \n        Parameters\n        ----------\n        event : str\n          The event for which to register this callback.\n        function : callable\n          A function to be called on the given event. It should take the same\n          parameters as the appropriate callback prototype.\n        \n        Raises\n        ------\n        TypeError\n          If ``function`` is not callable.\n        KeyError\n          If ``event`` is not one of the known events.\n        \"\"\"\n        if not callable(function):\n            raise TypeError('Need a callable, got %r' % function)\n        self.callbacks[event].append(function)\n    \n    def unregister(self, event, function):\n        \"\"\"Remove a callback from the given event.\"\"\"\n        self.callbacks[event].remove(function)\n    \n    def trigger(self, event, *args, **kwargs):\n        \"\"\"Call callbacks for ``event``.\n        \n        Any additional arguments are passed to all callbacks registered for this\n        event. Exceptions raised by callbacks are caught, and a message printed.\n        \"\"\"\n        for func in self.callbacks[event][:]:\n            try:\n                func(*args, **kwargs)\n            except Exception:\n                print(\"Error in callback {} (for {}):\".format(func, event))\n                self.shell.showtraceback()\n\n# event_name -> prototype mapping\navailable_events = {}\n\ndef _define_event(callback_proto):\n    available_events[callback_proto.__name__] = callback_proto\n    return callback_proto\n\n# ------------------------------------------------------------------------------\n# Callback prototypes\n#\n# No-op functions which describe the names of available events and the\n# signatures of callbacks for those events.\n# ------------------------------------------------------------------------------\n\n@_define_event\ndef pre_execute():\n    \"\"\"Fires before code is executed in response to user/frontend action.\n    \n    This includes comm and widget messages and silent execution, as well as user\n    code cells.\"\"\"\n    pass\n\n@_define_event\ndef pre_run_cell():\n    \"\"\"Fires before user-entered code runs.\"\"\"\n    pass\n\n@_define_event\ndef post_execute():\n    \"\"\"Fires after code is executed in response to user/frontend action.\n    \n    This includes comm and widget messages and silent execution, as well as user\n    code cells.\"\"\"\n    pass\n\n@_define_event\ndef post_run_cell():\n    \"\"\"Fires after user-entered code runs.\"\"\"\n    pass\n\n@_define_event\ndef shell_initialized(ip):\n    \"\"\"Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.\n    \n    This is before extensions and startup scripts are loaded, so it can only be\n    set by subclassing.\n    \n    Parameters\n    ----------\n    ip : :class:`~IPython.core.interactiveshell.InteractiveShell`\n      The newly initialised shell.\n    \"\"\"\n    pass\n", "path": "IPython/core/events.py"}]}