text: #-----------------------------------------------------------------------------
  # Copyright (c) 2012 - 2021, Anaconda, Inc., and Bokeh Contributors.
  # All rights reserved.
  #
  # The full license is in the file LICENSE.txt, distributed with this software.
  #-----------------------------------------------------------------------------
  
  #-----------------------------------------------------------------------------
  # Boilerplate
  #-----------------------------------------------------------------------------
  import pytest ; pytest
  
  #-----------------------------------------------------------------------------
  # Imports
  #-----------------------------------------------------------------------------
  
  # Standard library imports
  import logging
  
  # External imports
  from mock import patch
  
  # Bokeh imports
  from bokeh import __version__
  from bokeh.core.properties import Instance, Int, List, String
  from bokeh.document.document import Document
  from bokeh.events import Tap
  from bokeh.io import curdoc
  from bokeh.model import Model
  from bokeh.themes import Theme
  from bokeh.util.logconfig import basicConfig
  
  # Module under test
  import bokeh.embed.util as beu # isort:skip
  
  #-----------------------------------------------------------------------------
  # Setup
  #-----------------------------------------------------------------------------
  
  @pytest.fixture
  def test_plot() -> None:
      from bokeh.plotting import figure
      test_plot = figure()
      test_plot.circle([1, 2], [2, 3])
      return test_plot
  
  class SomeModel(Model):
      some = Int
  
  class OtherModel(Model):
      child = Instance(Model)
  
  # Taken from test_callback_manager.py
  class _GoodPropertyCallback:
      def __init__(self):
          self.last_name = None
          self.last_old = None
          self.last_new = None
  
      def __call__(self, name, old, new):
          self.method(name, old, new)
  
      def method(self, name, old, new):
          self.last_name = name
          self.last_old = old
          self.last_new = new
  
      def partially_good(self, name, old, new, newer):
          pass
  
      def just_fine(self, name, old, new, extra='default'):
          pass
  
  
  class _GoodEventCallback:
      def __init__(self):
          self.last_name = None
          self.last_old = None
          self.last_new = None
  
      def __call__(self, event):
          self.method(event)
  
      def method(self, event):
          self.event = event
  
      def partially_good(self, arg, event):
          pass
  
  # Taken from test_model
  class EmbedTestUtilModel(Model):
      a = Int(12)
      b = String("hello")
      c = List(Int, [1, 2, 3])
  
  #-----------------------------------------------------------------------------
  # General API
  #-----------------------------------------------------------------------------
  
  #-----------------------------------------------------------------------------
  # Dev API
  #-----------------------------------------------------------------------------
  
  
  class Test_FromCurdoc:
      def test_type(self) -> None:
          assert isinstance(beu.FromCurdoc, type)
  
  _ODFERR = "OutputDocumentFor expects a sequence of Models"
  
  
  class Test_OutputDocumentFor_general:
      def test_error_on_empty_list(self) -> None:
          with pytest.raises(ValueError) as e:
              with beu.OutputDocumentFor([]):
                  pass
          assert str(e.value).endswith(_ODFERR)
  
      def test_error_on_mixed_list(self) -> None:
          p = SomeModel()
          d = Document()
          orig_theme = d.theme
          with pytest.raises(ValueError) as e:
              with beu.OutputDocumentFor([p, d]):
                  pass
          assert str(e.value).endswith(_ODFERR)
          assert d.theme is orig_theme
  
      @pytest.mark.parametrize('v', [10, -0,3, "foo", True])
      def test_error_on_wrong_types(self, v) -> None:
          with pytest.raises(ValueError) as e:
              with beu.OutputDocumentFor(v):
                  pass
          assert str(e.value).endswith(_ODFERR)
  
      def test_with_doc_in_child_raises_error(self) -> None:
          doc = Document()
          p1 = SomeModel()
          p2 = OtherModel(child=SomeModel())
          doc.add_root(p2.child)
          assert p1.document is None
          assert p2.document is None
          assert p2.child.document is doc
          with pytest.raises(RuntimeError) as e:
              with beu.OutputDocumentFor([p1, p2]):
                  pass
              assert "already in a doc" in str(e.value)
  
      @patch('bokeh.document.document.check_integrity')
      def test_validates_document_by_default(self, check_integrity, test_plot) -> None:
          with beu.OutputDocumentFor([test_plot]):
              pass
          assert check_integrity.called
  
      @patch('bokeh.document.document.check_integrity')
      def test_doesnt_validate_doc_due_to_env_var(self, check_integrity, monkeypatch, test_plot) -> None:
          monkeypatch.setenv("BOKEH_VALIDATE_DOC", "false")
          with beu.OutputDocumentFor([test_plot]):
              pass
          assert not check_integrity.called
  
  
  class Test_OutputDocumentFor_default_apply_theme:
      def test_single_model_with_document(self) -> None:
          # should use existing doc in with-block
          p = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p)
          with beu.OutputDocumentFor([p]):
              assert p.document is d
              assert d.theme is orig_theme
          assert p.document is d
          assert d.theme is orig_theme
  
      def test_single_model_with_no_document(self) -> None:
          p = SomeModel()
          assert p.document is None
          with beu.OutputDocumentFor([p]):
              assert p.document is not None
          assert p.document is not None
  
      def test_list_of_model_with_no_documents(self) -> None:
          # should create new (permanent) doc for inputs
          p1 = SomeModel()
          p2 = SomeModel()
          assert p1.document is None
          assert p2.document is None
          with beu.OutputDocumentFor([p1, p2]):
              assert p1.document is not None
              assert p2.document is not None
              assert p1.document is p2.document
              new_doc = p1.document
              new_theme = p1.document.theme
          assert p1.document is new_doc
          assert p1.document is p2.document
          assert p1.document.theme is new_theme
  
      def test_list_of_model_same_as_roots(self) -> None:
          # should use existing doc in with-block
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p1)
          d.add_root(p2)
          with beu.OutputDocumentFor([p1, p2]):
              assert p1.document is d
              assert p2.document is d
              assert d.theme is orig_theme
          assert p1.document is d
          assert p2.document is d
          assert d.theme is orig_theme
  
      def test_list_of_model_same_as_roots_with_always_new(self) -> None:
          # should use new temp doc for everything inside with-block
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p1)
          d.add_root(p2)
          with beu.OutputDocumentFor([p1, p2], always_new=True):
              assert p1.document is not d
              assert p2.document is not d
              assert p1.document is p2.document
              assert p2.document.theme is orig_theme
          assert p1.document is d
          assert p2.document is d
          assert d.theme is orig_theme
  
      def test_list_of_model_subset_roots(self) -> None:
          # should use new temp doc for subset inside with-block
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p1)
          d.add_root(p2)
          with beu.OutputDocumentFor([p1]):
              assert p1.document is not d
              assert p2.document is d
              assert p1.document.theme is orig_theme
              assert p2.document.theme is orig_theme
          assert p1.document is d
          assert p2.document is d
          assert d.theme is orig_theme
  
      def test_list_of_models_different_docs(self) -> None:
          # should use new temp doc for eveything inside with-block
          d = Document()
          orig_theme = d.theme
          p1 = SomeModel()
          p2 = SomeModel()
          d.add_root(p2)
          assert p1.document is None
          assert p2.document is not None
          with beu.OutputDocumentFor([p1, p2]):
              assert p1.document is not None
              assert p2.document is not None
              assert p1.document is not d
              assert p2.document is not d
              assert p1.document == p2.document
              assert p1.document.theme is orig_theme
          assert p1.document is None
          assert p2.document is not None
          assert p2.document.theme is orig_theme
  
  
  class Test_OutputDocumentFor_custom_apply_theme:
      def test_single_model_with_document(self) -> None:
          # should use existing doc in with-block
          p = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p)
          with beu.OutputDocumentFor([p], apply_theme=Theme(json={})):
              assert p.document is d
              assert d.theme is not orig_theme
          assert p.document is d
          assert d.theme is orig_theme
  
      def test_single_model_with_no_document(self) -> None:
          p = SomeModel()
          assert p.document is None
          with beu.OutputDocumentFor([p], apply_theme=Theme(json={})):
              assert p.document is not None
              new_theme = p.document.theme
          assert p.document is not None
          assert p.document.theme is not new_theme
  
      def test_list_of_model_with_no_documents(self) -> None:
          # should create new (permanent) doc for inputs
          p1 = SomeModel()
          p2 = SomeModel()
          assert p1.document is None
          assert p2.document is None
          with beu.OutputDocumentFor([p1, p2], apply_theme=Theme(json={})):
              assert p1.document is not None
              assert p2.document is not None
              assert p1.document is p2.document
              new_doc = p1.document
              new_theme = p1.document.theme
          assert p1.document is new_doc
          assert p2.document is new_doc
          assert p1.document is p2.document
          # should restore to default theme after with-block
          assert p1.document.theme is not new_theme
  
      def test_list_of_model_same_as_roots(self) -> None:
          # should use existing doc in with-block
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p1)
          d.add_root(p2)
          with beu.OutputDocumentFor([p1, p2], apply_theme=Theme(json={})):
              assert p1.document is d
              assert p2.document is d
              assert d.theme is not orig_theme
          assert p1.document is d
          assert p2.document is d
          assert d.theme is orig_theme
  
      def test_list_of_model_same_as_roots_with_always_new(self) -> None:
          # should use new temp doc for everything inside with-block
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p1)
          d.add_root(p2)
          with beu.OutputDocumentFor([p1, p2], always_new=True, apply_theme=Theme(json={})):
              assert p1.document is not d
              assert p2.document is not d
              assert p1.document is p2.document
              assert p2.document.theme is not orig_theme
          assert p1.document is d
          assert p2.document is d
          assert d.theme is orig_theme
  
      def test_list_of_model_subset_roots(self) -> None:
          # should use new temp doc for subset inside with-block
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p1)
          d.add_root(p2)
          with beu.OutputDocumentFor([p1], apply_theme=Theme(json={})):
              assert p1.document is not d
              assert p2.document is d
              assert p1.document.theme is not orig_theme
              assert p2.document.theme is orig_theme
          assert p1.document is d
          assert p2.document is d
          assert d.theme is orig_theme
  
      def test_list_of_models_different_docs(self) -> None:
          # should use new temp doc for eveything inside with-block
          d = Document()
          orig_theme = d.theme
          p1 = SomeModel()
          p2 = SomeModel()
          d.add_root(p2)
          assert p1.document is None
          assert p2.document is not None
          with beu.OutputDocumentFor([p1, p2], apply_theme=Theme(json={})):
              assert p1.document is not None
              assert p2.document is not None
              assert p1.document is not d
              assert p2.document is not d
              assert p1.document == p2.document
              assert p1.document.theme is not orig_theme
          assert p1.document is None
          assert p2.document is not None
          assert p2.document.theme is orig_theme
  
  
  class Test_OutputDocumentFor_FromCurdoc_apply_theme:
      def setup_method(self):
          self.orig_theme = curdoc().theme
          curdoc().theme = Theme(json={})
  
      def teardown_method(self):
          curdoc().theme = self.orig_theme
  
      def test_single_model_with_document(self) -> None:
          # should use existing doc in with-block
          p = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p)
          with beu.OutputDocumentFor([p], apply_theme=beu.FromCurdoc):
              assert p.document is d
              assert d.theme is curdoc().theme
          assert p.document is d
          assert d.theme is orig_theme
  
      def test_single_model_with_no_document(self) -> None:
          p = SomeModel()
          assert p.document is None
          with beu.OutputDocumentFor([p], apply_theme=beu.FromCurdoc):
              assert p.document is not None
              assert p.document.theme is curdoc().theme
              new_doc = p.document
          assert p.document is new_doc
          assert p.document.theme is not curdoc().theme
  
      def test_list_of_model_with_no_documents(self) -> None:
          # should create new (permanent) doc for inputs
          p1 = SomeModel()
          p2 = SomeModel()
          assert p1.document is None
          assert p2.document is None
          with beu.OutputDocumentFor([p1, p2], apply_theme=beu.FromCurdoc):
              assert p1.document is not None
              assert p2.document is not None
              assert p1.document is p2.document
              new_doc = p1.document
              assert p1.document.theme is curdoc().theme
          assert p1.document is new_doc
          assert p2.document is new_doc
          assert p1.document is p2.document
          # should restore to default theme after with-block
          assert p1.document.theme is not curdoc().theme
  
      def test_list_of_model_same_as_roots(self) -> None:
          # should use existing doc in with-block
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p1)
          d.add_root(p2)
          with beu.OutputDocumentFor([p1, p2], apply_theme=beu.FromCurdoc):
              assert p1.document is d
              assert p2.document is d
              assert d.theme is curdoc().theme
          assert p1.document is d
          assert p2.document is d
          assert d.theme is orig_theme
  
      def test_list_of_model_same_as_roots_with_always_new(self) -> None:
          # should use new temp doc for everything inside with-block
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p1)
          d.add_root(p2)
          with beu.OutputDocumentFor([p1, p2], always_new=True, apply_theme=beu.FromCurdoc):
              assert p1.document is not d
              assert p2.document is not d
              assert p1.document is p2.document
              assert p2.document.theme is curdoc().theme
          assert p1.document is d
          assert p2.document is d
          assert d.theme is orig_theme
  
      def test_list_of_model_subset_roots(self) -> None:
          # should use new temp doc for subset inside with-block
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          orig_theme = d.theme
          d.add_root(p1)
          d.add_root(p2)
          with beu.OutputDocumentFor([p1], apply_theme=beu.FromCurdoc):
              assert p1.document is not d
              assert p2.document is d
              assert p1.document.theme is curdoc().theme
              assert p2.document.theme is orig_theme
          assert p1.document is d
          assert p2.document is d
          assert d.theme is orig_theme
  
      def test_list_of_models_different_docs(self) -> None:
          # should use new temp doc for eveything inside with-block
          d = Document()
          orig_theme = d.theme
          p1 = SomeModel()
          p2 = SomeModel()
          d.add_root(p2)
          assert p1.document is None
          assert p2.document is not None
          with beu.OutputDocumentFor([p1, p2], apply_theme=beu.FromCurdoc):
              assert p1.document is not None
              assert p2.document is not None
              assert p1.document is not d
              assert p2.document is not d
              assert p1.document == p2.document
              assert p1.document.theme is curdoc().theme
          assert p1.document is None
          assert p2.document is not None
          assert p2.document.theme is orig_theme
  
  
  class Test_standalone_docs_json_and_render_items:
      def test_passing_model(self) -> None:
          p1 = SomeModel()
          d = Document()
          d.add_root(p1)
          docs_json, render_items = beu.standalone_docs_json_and_render_items([p1])
          doc = list(docs_json.values())[0]
          assert doc['title'] == "Bokeh Application"
          assert doc['version'] == __version__
          assert len(doc['roots']['root_ids']) == 1
          assert len(doc['roots']['references']) == 1
          assert doc['roots']['references'] == [{'attributes': {}, 'id': str(p1.id), 'type': 'test_util__embed.SomeModel'}]
          assert len(render_items) == 1
  
      def test_passing_doc(self) -> None:
          p1 = SomeModel()
          d = Document()
          d.add_root(p1)
          docs_json, render_items = beu.standalone_docs_json_and_render_items([d])
          doc = list(docs_json.values())[0]
          assert doc['title'] == "Bokeh Application"
          assert doc['version'] == __version__
          assert len(doc['roots']['root_ids']) == 1
          assert len(doc['roots']['references']) == 1
          assert doc['roots']['references'] == [{'attributes': {}, 'id': str(p1.id), 'type': 'test_util__embed.SomeModel'}]
          assert len(render_items) == 1
  
      def test_exception_for_missing_doc(self) -> None:
          p1 = SomeModel()
          with pytest.raises(ValueError) as e:
              beu.standalone_docs_json_and_render_items([p1])
          assert str(e.value) == "A Bokeh Model must be part of a Document to render as standalone content"
  
      def test_log_warning_if_python_property_callback(self, caplog) -> None:
          d = Document()
          m1 = EmbedTestUtilModel()
          c1 = _GoodPropertyCallback()
          d.add_root(m1)
  
          m1.on_change('name', c1)
          assert len(m1._callbacks) != 0
  
          with caplog.at_level(logging.WARN):
              beu.standalone_docs_json_and_render_items(m1)
              assert len(caplog.records) == 1
              assert caplog.text != ''
  
      def test_log_warning_if_python_event_callback(self, caplog) -> None:
          d = Document()
          m1 = EmbedTestUtilModel()
          c1 = _GoodEventCallback()
          d.add_root(m1)
  
          m1.on_event(Tap, c1)
          assert len(m1._event_callbacks) != 0
  
          with caplog.at_level(logging.WARN):
              beu.standalone_docs_json_and_render_items(m1)
              assert len(caplog.records) == 1
              assert caplog.text != ''
  
      def test_suppress_warnings(self, caplog) -> None:
          d = Document()
          m1 = EmbedTestUtilModel()
          c1 = _GoodPropertyCallback()
          c2 = _GoodEventCallback()
          d.add_root(m1)
  
          m1.on_change('name', c1)
          assert len(m1._callbacks) != 0
  
          m1.on_event(Tap, c2)
          assert len(m1._event_callbacks) != 0
  
          with caplog.at_level(logging.WARN):
              beu.standalone_docs_json_and_render_items(m1, suppress_callback_warning=True)
              assert len(caplog.records) == 0
              assert caplog.text == ''
  
  
  class Test_standalone_docs_json:
      @patch('bokeh.embed.util.standalone_docs_json_and_render_items')
      def test_delgation(self, mock_sdjari) -> None:
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          d.add_root(p1)
          d.add_root(p2)
          # ignore error unpacking None mock result, just checking to see that
          # standalone_docs_json_and_render_items is called as expected
          try:
              beu.standalone_docs_json([p1, p2])
          except ValueError:
              pass
          mock_sdjari.assert_called_once_with([p1, p2])
  
      def test_output(self) -> None:
          p1 = SomeModel()
          p2 = SomeModel()
          d = Document()
          d.add_root(p1)
          d.add_root(p2)
          out = beu.standalone_docs_json([p1, p2])
          expected = beu.standalone_docs_json_and_render_items([p1, p2])[0]
          assert list(out.values()) ==list(expected.values())
  
  #-----------------------------------------------------------------------------
  # Private API
  #-----------------------------------------------------------------------------
  
  
  class Test__create_temp_doc:
      def test_no_docs(self) -> None:
          p1 = SomeModel()
          p2 = SomeModel()
          beu._create_temp_doc([p1, p2])
          assert isinstance(p1.document, Document)
          assert isinstance(p2.document, Document)
  
      def test_top_level_same_doc(self) -> None:
          d = Document()
          p1 = SomeModel()
          p2 = SomeModel()
          d.add_root(p1)
          d.add_root(p2)
          beu._create_temp_doc([p1, p2])
          assert isinstance(p1.document, Document)
          assert p1.document is not d
          assert isinstance(p2.document, Document)
          assert p2.document is not d
  
          assert p2.document == p1.document
  
      def test_top_level_different_doc(self) -> None:
          d1 = Document()
          d2 = Document()
          p1 = SomeModel()
          p2 = SomeModel()
          d1.add_root(p1)
          d2.add_root(p2)
          beu._create_temp_doc([p1, p2])
          assert isinstance(p1.document, Document)
          assert p1.document is not d1
          assert isinstance(p2.document, Document)
          assert p2.document is not d2
  
          assert p2.document == p1.document
  
      def test_child_docs(self) -> None:
          d = Document()
          p1 = SomeModel()
          p2 = OtherModel(child=SomeModel())
          d.add_root(p2.child)
          beu._create_temp_doc([p1, p2])
  
          assert isinstance(p1.document, Document)
          assert p1.document is not d
          assert isinstance(p2.document, Document)
          assert p2.document is not d
          assert isinstance(p2.child.document, Document)
          assert p2.child.document is not d
  
          assert p2.document == p1.document
          assert p2.document == p2.child.document
  
  
  class Test__dispose_temp_doc:
      def test_no_docs(self) -> None:
          p1 = SomeModel()
          p2 = SomeModel()
          beu._dispose_temp_doc([p1, p2])
          assert p1.document is None
          assert p2.document is None
  
      def test_with_docs(self) -> None:
          d1 = Document()
          d2 = Document()
          p1 = SomeModel()
          d1.add_root(p1)
          p2 = OtherModel(child=SomeModel())
          d2.add_root(p2.child)
          beu._create_temp_doc([p1, p2])
          beu._dispose_temp_doc([p1, p2])
          assert p1.document is d1
          assert p2.document is None
          assert p2.child.document is d2
  
      def test_with_temp_docs(self) -> None:
          p1 = SomeModel()
          p2 = SomeModel()
          beu._create_temp_doc([p1, p2])
          beu._dispose_temp_doc([p1, p2])
          assert p1.document is None
          assert p2.document is None
  
  class Test__set_temp_theme:
      def test_apply_None(self) -> None:
          d = Document()
          orig = d.theme
          beu._set_temp_theme(d, None)
          assert d._old_theme is orig
          assert d.theme is orig
  
      def test_apply_theme(self) -> None:
          t = Theme(json={})
          d = Document()
          orig = d.theme
          beu._set_temp_theme(d, t)
          assert d._old_theme is orig
          assert d.theme is t
  
      def test_apply_from_curdoc(self) -> None:
          t = Theme(json={})
          curdoc().theme = t
          d = Document()
          orig = d.theme
          beu._set_temp_theme(d, beu.FromCurdoc)
          assert d._old_theme is orig
          assert d.theme is t
  
  class Test__unset_temp_theme:
      def test_basic(self) -> None:
          t = Theme(json={})
          d = Document()
          d._old_theme = t
          beu._unset_temp_theme(d)
          assert d.theme is t
          assert not hasattr(d, "_old_theme")
  
      def test_no_old_theme(self) -> None:
          d = Document()
          orig = d.theme
          beu._unset_temp_theme(d)
          assert d.theme is orig
          assert not hasattr(d, "_old_theme")
  
  #-----------------------------------------------------------------------------
  # Code
  #-----------------------------------------------------------------------------
  
  # needed for caplog tests to function
  basicConfig()
  