Repository: bokeh/bokeh

Issue:
[BUG] RuntimeError on adding callbacks after rendering
hi,
I have observed following behaviour :

#### Versions
bokeh 1.1.0
python 3.7
Mac OSX
browser safari

#### Description of expected behavior and the observed behavior
I have the use-case where I need to add a button with callback inside a callback of another button.
so initial rendering adds button1 with a callback. inside the callback of button 1 I add button2 to the document, which also has a callback
the example looks a bit odd to do it this way, but in my real app this is used to wait for user input(file location) before procceding
however I have seen that a runtime error is thrown when adding the second button.
I have tried around a bit and it appears to only be related to Button models

#### Example
```
from bokeh.io import curdoc
from bokeh.models import Button

button1 = Button(label="1")
button2 = Button(label="2")


def add_button():
    button1.on_click(clicked_1)
    curdoc().add_root(button1)


def clicked_1():
    button2.on_click(clicked_2)
    curdoc().add_root(button2)


def clicked_2():
    print("ok")


add_button()
```

#### Stack traceback and/or browser JavaScript console output
```
python -m bokeh serve --show test.py
2019-04-13 12:29:42,697 Starting Bokeh server version 1.1.0 (running on Tornado 6.0.2)
2019-04-13 12:29:42,702 Bokeh app running at: http://localhost:5006/test
2019-04-13 12:29:42,702 Starting Bokeh server with process id: 48939
2019-04-13 12:29:42,882 200 GET /test (::1) 10.75ms
2019-04-13 12:29:43,038 101 GET /test/ws?bokeh-protocol-version=1.0&bokeh-session-id=y55qNB03iq1851iMPS8ji75eCL9VvbmpEXcK4iklFr2x (::1) 0.84ms
2019-04-13 12:29:43,038 WebSocket connection opened
2019-04-13 12:29:43,038 ServerConnection created
2019-04-13 12:29:45,268 error handling message Message 'EVENT' (revision 1) content: '{"event_name":"button_click","event_values":{"model_id":"1002"}}': RuntimeError('Set changed size during iteration')
2019-04-13 12:30:48,285 WebSocket connection closed: code=None, reason=None

```


Discussion:
I think this and #8783 are the same issue. It's a simple fix to copy the set before iterating over it. 

Resolving patch:
diff --git a/bokeh/document/document.py b/bokeh/document/document.py
--- a/bokeh/document/document.py
+++ b/bokeh/document/document.py
@@ -334,8 +334,9 @@ def apply_json_event(self, json):
         if not isinstance(event, Event):
             log.warning('Could not decode event json: %s' % json)
         else:
-            for obj in self._subscribed_models[event.event_name]:
-                obj._trigger_event(event)
+            subscribed = self._subscribed_models[event.event_name].copy()
+            for model in subscribed:
+                model._trigger_event(event)
 
     def apply_json_patch(self, patch, setter=None):
         ''' Apply a JSON patch object and process any resulting events.


Test patch:
diff --git a/bokeh/document/tests/test_document.py b/bokeh/document/tests/test_document.py
--- a/bokeh/document/tests/test_document.py
+++ b/bokeh/document/tests/test_document.py
@@ -20,6 +20,7 @@
 import logging
 
 from copy import copy
+import json
 from mock import patch
 
 # External imports
@@ -922,6 +923,26 @@ def test_scatter(self):
         d.add_root(p1)
         assert len(d.roots) == 1
 
+    def test_event_handles_new_callbacks_in_event_callback(self):
+        from bokeh.models import Button
+        d = document.Document()
+        button1 = Button(label="1")
+        button2 = Button(label="2")
+        def clicked_1():
+            button2.on_click(clicked_2)
+            d.add_root(button2)
+        def clicked_2():
+            pass
+
+        button1.on_click(clicked_1)
+        d.add_root(button1)
+
+        event_json = json.dumps({"event_name":"button_click","event_values":{"model_id":button1.id}})
+        try:
+            d.apply_json_event(event_json)
+        except RuntimeError:
+            pytest.fail("apply_json_event probably did not copy models before modifying")
+
     # TODO test serialize/deserialize with list-and-dict-valued properties
 
     # TODO test replace_with_json