diff --git a/_multibuild b/_multibuild
new file mode 100644
index 0000000..49fb912
--- /dev/null
+++ b/_multibuild
@@ -0,0 +1,3 @@
+
+ test
+
diff --git a/bokeh-2.4.3.tar.gz b/bokeh-2.4.3.tar.gz
deleted file mode 100644
index 7b4970c..0000000
--- a/bokeh-2.4.3.tar.gz
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:ef33801161af379665ab7a34684f2209861e3aefd5c803a21fbbb99d94874b03
-size 17722836
diff --git a/bokeh-3.0.1-gh.tar.gz b/bokeh-3.0.1-gh.tar.gz
new file mode 100644
index 0000000..a8e4da0
--- /dev/null
+++ b/bokeh-3.0.1-gh.tar.gz
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bd32fd760906b18becc6a1b320175a9b8d6c24acf94d91393004037a159265fc
+size 37179517
diff --git a/bokeh-3.0.1.tar.gz b/bokeh-3.0.1.tar.gz
new file mode 100644
index 0000000..881434b
--- /dev/null
+++ b/bokeh-3.0.1.tar.gz
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa044829f66f08e9aeb74e33d538bc719270406124ea128bd7616e09e1561815
+size 15511827
diff --git a/bokeh-pr12218-Pillow9.2.patch b/bokeh-pr12218-Pillow9.2.patch
deleted file mode 100644
index 8baf72a..0000000
--- a/bokeh-pr12218-Pillow9.2.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From b86d5458cbf6d371013a8ced744b262460005e23 Mon Sep 17 00:00:00 2001
-From: Mateusz Paprocki
-Date: Wed, 6 Jul 2022 14:59:48 +0200
-Subject: [PATCH] Robustify a unit test of Image.transform()
-
----
- tests/unit/bokeh/core/property/test_visual.py | 14 +++++++++-----
- 1 file changed, 9 insertions(+), 5 deletions(-)
-
-diff --git a/tests/unit/bokeh/core/property/test_visual.py b/tests/unit/bokeh/core/property/test_visual.py
-index 3f293642b56..37620d35c1d 100644
---- a/tests/unit/bokeh/core/property/test_visual.py
-+++ b/tests/unit/bokeh/core/property/test_visual.py
-@@ -21,6 +21,7 @@
- import datetime
- from io import BytesIO
- from pathlib import Path
-+from typing import Literal
-
- # External imports
- import numpy as np
-@@ -284,12 +285,15 @@ def test_transform_numpy(self) -> None:
- assert prop.transform(data) == expected
-
- @pytest.mark.parametrize('typ', ('png', 'gif', 'tiff'))
-- def test_transform_PIL(self, typ) -> None:
-+ def test_transform_PIL(self, typ: Literal["png", "gif", "tiff"]) -> None:
- image = PIL.Image.new("RGBA", size=(50, 50), color=(155, 0, 0))
-- out = BytesIO()
-- image.save(out, typ)
-- value = PIL.Image.open(out)
-- expected = "data:image/%s;base64," % typ + base64.b64encode(out.getvalue()).decode('ascii')
-+ out0 = BytesIO()
-+ image.save(out0, typ)
-+
-+ value = PIL.Image.open(out0)
-+ out1 = BytesIO()
-+ value.save(out1, typ)
-+ expected = "data:image/%s;base64," % typ + base64.b64encode(out1.getvalue()).decode('ascii')
-
- prop = bcpv.Image()
- assert prop.transform(value) == expected
diff --git a/bokeh-remove-mock.patch b/bokeh-remove-mock.patch
new file mode 100644
index 0000000..bf9f135
--- /dev/null
+++ b/bokeh-remove-mock.patch
@@ -0,0 +1,497 @@
+diff --git a/tests/unit/bokeh/application/test_application.py b/tests/unit/bokeh/application/test_application.py
+index 7fa815eff..c1de92ff6 100644
+--- a/tests/unit/bokeh/application/test_application.py
++++ b/tests/unit/bokeh/application/test_application.py
+@@ -20,7 +20,7 @@ import pytest ; pytest
+ import logging
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.application.handlers import CodeHandler, FunctionHandler, Handler
+diff --git a/tests/unit/bokeh/client/test_session__client.py b/tests/unit/bokeh/client/test_session__client.py
+index 2157d0bea..c1f2bf261 100644
+--- a/tests/unit/bokeh/client/test_session__client.py
++++ b/tests/unit/bokeh/client/test_session__client.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Module under test
+ import bokeh.client.session as bcs # isort:skip
+diff --git a/tests/unit/bokeh/command/test_subcommand.py b/tests/unit/bokeh/command/test_subcommand.py
+index 507d14541..a6442566c 100644
+--- a/tests/unit/bokeh/command/test_subcommand.py
++++ b/tests/unit/bokeh/command/test_subcommand.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import MagicMock
++from unittest.mock import MagicMock
+
+ # Module under test
+ import bokeh.command.subcommand as sc # isort:skip
+diff --git a/tests/unit/bokeh/command/test_util__command.py b/tests/unit/bokeh/command/test_util__command.py
+index ec6c72991..baa8365a3 100644
+--- a/tests/unit/bokeh/command/test_util__command.py
++++ b/tests/unit/bokeh/command/test_util__command.py
+@@ -21,7 +21,7 @@ import os
+ import tempfile
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from tests.support.util.types import Capture
+diff --git a/tests/unit/bokeh/core/property/test_bases.py b/tests/unit/bokeh/core/property/test_bases.py
+index 2e65191a1..cc69110f6 100644
+--- a/tests/unit/bokeh/core/property/test_bases.py
++++ b/tests/unit/bokeh/core/property/test_bases.py
+@@ -22,7 +22,7 @@ from typing import Any
+ # External imports
+ import numpy as np
+ import pandas as pd
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.core.has_props import HasProps
+diff --git a/tests/unit/bokeh/core/property/test_descriptors.py b/tests/unit/bokeh/core/property/test_descriptors.py
+index 2b4676064..00c279b03 100644
+--- a/tests/unit/bokeh/core/property/test_descriptors.py
++++ b/tests/unit/bokeh/core/property/test_descriptors.py
+@@ -20,7 +20,7 @@ import pytest ; pytest
+ import typing as tp
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.core.properties import Int, List, Nullable
+diff --git a/tests/unit/bokeh/core/property/test_wrappers__property.py b/tests/unit/bokeh/core/property/test_wrappers__property.py
+index 2202244cc..4551be8cc 100644
+--- a/tests/unit/bokeh/core/property/test_wrappers__property.py
++++ b/tests/unit/bokeh/core/property/test_wrappers__property.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.core.properties import (
+diff --git a/tests/unit/bokeh/core/test_validation.py b/tests/unit/bokeh/core/test_validation.py
+index 8f2634161..02971b25b 100644
+--- a/tests/unit/bokeh/core/test_validation.py
++++ b/tests/unit/bokeh/core/test_validation.py
+@@ -20,7 +20,7 @@ import pytest ; pytest
+ from typing import Any, cast
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.core.properties import Int
+diff --git a/tests/unit/bokeh/document/test_callbacks__document.py b/tests/unit/bokeh/document/test_callbacks__document.py
+index a42798c9d..2790d470c 100644
+--- a/tests/unit/bokeh/document/test_callbacks__document.py
++++ b/tests/unit/bokeh/document/test_callbacks__document.py
+@@ -22,7 +22,7 @@ import logging
+ from typing import Any
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.core.enums import HoldPolicy, HoldPolicyType
+diff --git a/tests/unit/bokeh/document/test_document.py b/tests/unit/bokeh/document/test_document.py
+index 63072acc7..0cf36ebe9 100644
+--- a/tests/unit/bokeh/document/test_document.py
++++ b/tests/unit/bokeh/document/test_document.py
+@@ -21,7 +21,7 @@ import weakref
+ from typing import Any
+
+ # External imports
+-from mock import patch
++from unittest.mock import patch
+
+ # Bokeh imports
+ from bokeh.core.enums import HoldPolicy
+diff --git a/tests/unit/bokeh/document/test_events__document.py b/tests/unit/bokeh/document/test_events__document.py
+index 346a4290b..6ad971126 100644
+--- a/tests/unit/bokeh/document/test_events__document.py
++++ b/tests/unit/bokeh/document/test_events__document.py
+@@ -18,7 +18,7 @@ import pytest ; pytest
+
+ # External imports
+ import pandas as pd
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.core.properties import Any, ColumnData, Instance
+diff --git a/tests/unit/bokeh/document/test_models.py b/tests/unit/bokeh/document/test_models.py
+index 8f21cc135..da87c39b5 100644
+--- a/tests/unit/bokeh/document/test_models.py
++++ b/tests/unit/bokeh/document/test_models.py
+@@ -20,7 +20,7 @@ import pytest ; pytest
+ import gc
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.core.types import ID
+diff --git a/tests/unit/bokeh/embed/test_standalone.py b/tests/unit/bokeh/embed/test_standalone.py
+index d8a7e9468..6d27d4476 100644
+--- a/tests/unit/bokeh/embed/test_standalone.py
++++ b/tests/unit/bokeh/embed/test_standalone.py
+@@ -26,7 +26,7 @@ from typing import Any
+ import bs4
+ import numpy as np
+ from jinja2 import Template
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+ from selenium.webdriver.common.by import By
+ from selenium.webdriver.remote.webdriver import WebDriver
+
+diff --git a/tests/unit/bokeh/embed/test_util__embed.py b/tests/unit/bokeh/embed/test_util__embed.py
+index 9027bfac9..ee24a1629 100644
+--- a/tests/unit/bokeh/embed/test_util__embed.py
++++ b/tests/unit/bokeh/embed/test_util__embed.py
+@@ -20,7 +20,7 @@ import pytest ; pytest
+ import logging
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh import __version__
+diff --git a/tests/unit/bokeh/io/test_notebook__io.py b/tests/unit/bokeh/io/test_notebook__io.py
+index 6b28b4c2d..9a7dc84c4 100644
+--- a/tests/unit/bokeh/io/test_notebook__io.py
++++ b/tests/unit/bokeh/io/test_notebook__io.py
+@@ -21,7 +21,7 @@ import json
+ from typing import Any
+
+ # External imports
+-from mock import MagicMock, PropertyMock, patch
++from unittest.mock import MagicMock, PropertyMock, patch
+
+ # Bokeh imports
+ from bokeh.document.document import Document
+diff --git a/tests/unit/bokeh/io/test_output.py b/tests/unit/bokeh/io/test_output.py
+index df2d939a5..107052b9c 100644
+--- a/tests/unit/bokeh/io/test_output.py
++++ b/tests/unit/bokeh/io/test_output.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.io.state import curstate
+diff --git a/tests/unit/bokeh/io/test_saving.py b/tests/unit/bokeh/io/test_saving.py
+index f657a89c3..1709d44c8 100644
+--- a/tests/unit/bokeh/io/test_saving.py
++++ b/tests/unit/bokeh/io/test_saving.py
+@@ -20,7 +20,7 @@ import pytest ; pytest
+ from pathlib import Path
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.core.templates import FILE
+diff --git a/tests/unit/bokeh/io/test_showing.py b/tests/unit/bokeh/io/test_showing.py
+index 6c49a6d07..1e615b8c2 100644
+--- a/tests/unit/bokeh/io/test_showing.py
++++ b/tests/unit/bokeh/io/test_showing.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import MagicMock, Mock, patch
++from unittest.mock import MagicMock, Mock, patch
+
+ # Bokeh imports
+ from bokeh.application.application import Application
+diff --git a/tests/unit/bokeh/io/test_state.py b/tests/unit/bokeh/io/test_state.py
+index 395811acc..d12f9fa74 100644
+--- a/tests/unit/bokeh/io/test_state.py
++++ b/tests/unit/bokeh/io/test_state.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.document import Document
+diff --git a/tests/unit/bokeh/io/test_util__io.py b/tests/unit/bokeh/io/test_util__io.py
+index 3a4be5244..d18323294 100644
+--- a/tests/unit/bokeh/io/test_util__io.py
++++ b/tests/unit/bokeh/io/test_util__io.py
+@@ -20,7 +20,7 @@ import pytest ; pytest
+ import os
+
+ # External imports
+-from mock import (
++from unittest.mock import (
+ MagicMock,
+ Mock,
+ PropertyMock,
+diff --git a/tests/unit/bokeh/models/test_annotations.py b/tests/unit/bokeh/models/test_annotations.py
+index f1eef4443..57a64ede4 100644
+--- a/tests/unit/bokeh/models/test_annotations.py
++++ b/tests/unit/bokeh/models/test_annotations.py
+@@ -20,7 +20,7 @@ import pytest ; pytest
+ from datetime import datetime
+
+ # External imports
+-import mock
++from unittest import mock
+
+ # Bokeh imports
+ from bokeh.core.properties import field, value
+diff --git a/tests/unit/bokeh/models/test_mappers.py b/tests/unit/bokeh/models/test_mappers.py
+index 1273f4911..130b77a31 100644
+--- a/tests/unit/bokeh/models/test_mappers.py
++++ b/tests/unit/bokeh/models/test_mappers.py
+@@ -18,7 +18,7 @@ import pytest ; pytest
+
+ # External imports
+ import pandas as pd
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.core.validation import check_integrity, process_validation_issues
+diff --git a/tests/unit/bokeh/models/test_plots.py b/tests/unit/bokeh/models/test_plots.py
+index 8c1a66c17..7f4024cf7 100644
+--- a/tests/unit/bokeh/models/test_plots.py
++++ b/tests/unit/bokeh/models/test_plots.py
+@@ -20,9 +20,9 @@ import pytest ; pytest
+ from math import isnan
+
+ # External imports
+-import mock
++from unittest import mock
++from unittest.mock import MagicMock, patch
+ import xyzservices.providers as xyz
+-from mock import MagicMock, patch
+
+ # Bokeh imports
+ from bokeh.core.validation import check_integrity, process_validation_issues
+diff --git a/tests/unit/bokeh/models/test_ranges.py b/tests/unit/bokeh/models/test_ranges.py
+index 7c71a8453..3bf9c745b 100644
+--- a/tests/unit/bokeh/models/test_ranges.py
++++ b/tests/unit/bokeh/models/test_ranges.py
+@@ -21,7 +21,7 @@ import datetime as dt
+ from math import isnan
+
+ # External imports
+-import mock
++from unittest import mock
+
+ # Bokeh imports
+ from bokeh.core.validation import check_integrity, process_validation_issues
+diff --git a/tests/unit/bokeh/plotting/test__decorators.py b/tests/unit/bokeh/plotting/test__decorators.py
+index 5ee77137d..ea45289d7 100644
+--- a/tests/unit/bokeh/plotting/test__decorators.py
++++ b/tests/unit/bokeh/plotting/test__decorators.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import mock
++from unittest import mock
+
+ # Bokeh imports
+ from bokeh.models import CDSView, Marker
+diff --git a/tests/unit/bokeh/plotting/test__renderer.py b/tests/unit/bokeh/plotting/test__renderer.py
+index 271cf196f..3337c550a 100644
+--- a/tests/unit/bokeh/plotting/test__renderer.py
++++ b/tests/unit/bokeh/plotting/test__renderer.py
+@@ -19,7 +19,7 @@ import pytest ; pytest
+ # Bokeh imports
+ from bokeh.models import Circle
+
+-#from mock import mock
++#from unittest import mock
+
+ #from bokeh.plotting import figure
+
+diff --git a/tests/unit/bokeh/server/test_server__server.py b/tests/unit/bokeh/server/test_server__server.py
+index a195fe4dc..5ced4affc 100644
+--- a/tests/unit/bokeh/server/test_server__server.py
++++ b/tests/unit/bokeh/server/test_server__server.py
+@@ -26,7 +26,7 @@ import time
+ from datetime import timedelta
+
+ # External imports
+-import mock
++from unittest import mock
+ import tornado
+ from _util_server import (
+ http_get,
+diff --git a/tests/unit/bokeh/server/test_session__server.py b/tests/unit/bokeh/server/test_session__server.py
+index 42804ce41..f37156342 100644
+--- a/tests/unit/bokeh/server/test_session__server.py
++++ b/tests/unit/bokeh/server/test_session__server.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-import mock
++from unittest import mock
+
+ # Bokeh imports
+ from bokeh.document import Document
+diff --git a/tests/unit/bokeh/test___main__.py b/tests/unit/bokeh/test___main__.py
+index 380409c30..539bb0b9b 100644
+--- a/tests/unit/bokeh/test___main__.py
++++ b/tests/unit/bokeh/test___main__.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from tests.support.util.api import verify_all
+diff --git a/tests/unit/bokeh/test_client_server.py b/tests/unit/bokeh/test_client_server.py
+index 5ca089f6e..41c497281 100644
+--- a/tests/unit/bokeh/test_client_server.py
++++ b/tests/unit/bokeh/test_client_server.py
+@@ -23,7 +23,7 @@ import sys
+
+ # External imports
+ from flaky import flaky
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+ from tornado.httpclient import HTTPError
+
+ # Bokeh imports
+diff --git a/tests/unit/bokeh/test_layouts.py b/tests/unit/bokeh/test_layouts.py
+index c5e620f2f..f07940bac 100644
+--- a/tests/unit/bokeh/test_layouts.py
++++ b/tests/unit/bokeh/test_layouts.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-import mock
++from unittest import mock
+
+ # Bokeh imports
+ from bokeh.core.validation import check_integrity, process_validation_issues
+diff --git a/tests/unit/bokeh/util/test_browser.py b/tests/unit/bokeh/util/test_browser.py
+index a9bc18773..171aba69f 100644
+--- a/tests/unit/bokeh/util/test_browser.py
++++ b/tests/unit/bokeh/util/test_browser.py
+@@ -22,7 +22,7 @@ import sys
+ import webbrowser
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Bokeh imports
+ from tests.support.util.env import envset
+diff --git a/tests/unit/bokeh/util/test_compiler.py b/tests/unit/bokeh/util/test_compiler.py
+index 03a7020ed..f8d2f0a87 100644
+--- a/tests/unit/bokeh/util/test_compiler.py
++++ b/tests/unit/bokeh/util/test_compiler.py
+@@ -21,7 +21,7 @@ import json
+ import os
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Module under test
+ import bokeh.util.compiler as buc # isort:skip
+diff --git a/tests/unit/bokeh/util/test_deprecation.py b/tests/unit/bokeh/util/test_deprecation.py
+index 7945d1bb4..a77c16d22 100644
+--- a/tests/unit/bokeh/util/test_deprecation.py
++++ b/tests/unit/bokeh/util/test_deprecation.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Module under test
+ import bokeh.util.deprecation as dep # isort:skip
+diff --git a/tests/unit/bokeh/util/test_package.py b/tests/unit/bokeh/util/test_package.py
+index 73afb7944..8010eb1db 100644
+--- a/tests/unit/bokeh/util/test_package.py
++++ b/tests/unit/bokeh/util/test_package.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import MagicMock, patch
++from unittest.mock import MagicMock, patch
+
+ # Module under test
+ import bokeh.util.package as bup # isort:skip
+diff --git a/tests/unit/bokeh/util/test_sampledata__util.py b/tests/unit/bokeh/util/test_sampledata__util.py
+index 0a7892a4f..4fb9ebf16 100644
+--- a/tests/unit/bokeh/util/test_sampledata__util.py
++++ b/tests/unit/bokeh/util/test_sampledata__util.py
+@@ -17,7 +17,7 @@ import pytest ; pytest
+ #-----------------------------------------------------------------------------
+
+ # External imports
+-from mock import call, patch
++from unittest.mock import call, patch
+
+ # Module under test
+ import bokeh.util.sampledata as bus # isort:skip
+diff --git a/tests/unit/bokeh/util/test_token.py b/tests/unit/bokeh/util/test_token.py
+index cd94e10b5..9fe23377b 100644
+--- a/tests/unit/bokeh/util/test_token.py
++++ b/tests/unit/bokeh/util/test_token.py
+@@ -24,7 +24,7 @@ import json
+ import random
+
+ # External imports
+-from mock import MagicMock, Mock, patch
++from unittest.mock import MagicMock, Mock, patch
+
+ # Bokeh imports
+ from bokeh.util.token import (
+diff --git a/tests/unit/bokeh/util/test_version.py b/tests/unit/bokeh/util/test_version.py
+index 48667d036..bafe1ba95 100644
+--- a/tests/unit/bokeh/util/test_version.py
++++ b/tests/unit/bokeh/util/test_version.py
+@@ -20,7 +20,7 @@ import pytest ; pytest
+ import re
+
+ # External imports
+-import mock
++from unittest import mock
+
+ # Module under test
+ import bokeh.util.version as buv # isort:skip
diff --git a/bokeh-sampledata.tar.xz b/bokeh-sampledata.tar.xz
new file mode 100644
index 0000000..a987df2
--- /dev/null
+++ b/bokeh-sampledata.tar.xz
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:36baef0896d65ca7b3ea9cd8bfbbf64490b2bcb4ce966f5e04761f853b85b46f
+size 9495500
diff --git a/bokeh-sys-executable.patch b/bokeh-sys-executable.patch
new file mode 100644
index 0000000..f9508e7
--- /dev/null
+++ b/bokeh-sys-executable.patch
@@ -0,0 +1,26 @@
+diff --git a/tests/support/plugins/bokeh_server.py b/tests/support/plugins/bokeh_server.py
+index c37f31bfa..50d786a4b 100644
+--- a/tests/support/plugins/bokeh_server.py
++++ b/tests/support/plugins/bokeh_server.py
+@@ -55,7 +55,7 @@ __all__ = (
+ def bokeh_server(request: pytest.FixtureRequest, log_file: IO[str]) -> str:
+ bokeh_port: int = request.config.option.bokeh_port
+
+- cmd = ["python", "-m", "bokeh", "serve"]
++ cmd = [sys.executable, "-m", "bokeh", "serve"]
+ argv = [f"--port={bokeh_port}"]
+ bokeh_server_url = f"http://localhost:{bokeh_port}"
+
+diff --git a/tests/test_examples.py b/tests/test_examples.py
+index 7072f2f88..1dfb1a4e8 100644
+--- a/tests/test_examples.py
++++ b/tests/test_examples.py
+@@ -268,7 +268,7 @@ with open(filename, 'rb') as example:
+ exec(compile(example.read(), filename, 'exec'))
+ """
+
+- cmd = ["python", "-c", code]
++ cmd = [sys.executable, "-c", code]
+ cwd = dirname(example.path)
+
+ env = os.environ.copy()
diff --git a/conftest.py b/conftest.py
deleted file mode 100644
index d3de2cc..0000000
--- a/conftest.py
+++ /dev/null
@@ -1,51 +0,0 @@
-#-----------------------------------------------------------------------------
-# Copyright (c) 2012 - 2022, Anaconda, Inc., and Bokeh Contributors.
-# All rights reserved.
-#
-# The full license is in the file LICENSE.txt, distributed with this software.
-#-----------------------------------------------------------------------------
-
-pytest_plugins = (
- "bokeh._testing.plugins.ipython",
- "bokeh._testing.plugins.managed_server_loop",
- "bokeh._testing.plugins.networkx",
- "bokeh._testing.plugins.pandas",
-)
-
-# Standard library imports
-from inspect import iscoroutinefunction
-from typing import List
-
-# External imports
-import _pytest
-import pytest
-
-
-def pytest_collection_modifyitems(items: List[_pytest.nodes.Item]) -> None:
- for item in items:
- if iscoroutinefunction(item.obj):
- item.add_marker(pytest.mark.asyncio)
-
-# Unfortunately these seem to all need to be centrally defined at the top level
-def pytest_addoption(parser: _pytest.config.argparsing.Parser) -> None:
-
- # plugins/selenium
- parser.addoption(
- "--driver", choices=('chrome', 'firefox', 'safari'), default='chrome', help='webdriver implementation')
-
- # plugins/bokeh_server
- parser.addoption(
- "--bokeh-port", dest="bokeh_port", type=int, default=5006, help="port on which Bokeh server resides"
- )
-
- # plugins/jupyter_notebook
- parser.addoption(
- "--notebook-port", type=int, default=6007, help="port on which Jupyter Notebook server resides"
- )
-
- parser.addoption(
- "--examples-log-file", dest="log_file", metavar="path", action="store", default='examples.log', help="where to write the complete log"
- )
- parser.addoption(
- "--no-js", action="store_true", default=False,
- help="only run python code and skip js")
diff --git a/python-bokeh.changes b/python-bokeh.changes
index 6cd54bd..15cacf7 100644
--- a/python-bokeh.changes
+++ b/python-bokeh.changes
@@ -1,3 +1,35 @@
+-------------------------------------------------------------------
+Fri Nov 4 15:23:00 UTC 2022 - Ben Greiner
+
+- Update to version 3.0.1
+ * point release that addresses an issue with runtime dependencies
+- Add patches for test suite:
+ * bokeh-remove-mock.patch gh#bokeh/bokeh#12561
+ * bokeh-sys-executable.patch gh#bokeh/bokeh#12563
+
+-------------------------------------------------------------------
+Mon Oct 31 11:39:10 UTC 2022 - Ben Greiner
+
+- Update to version 3.0.0
+ * Bokeh Version 3.0.0 is a major milestone of Bokeh project.
+ ## Major changes
+ * Support for legacy web browser (e.g. IE, non-ES6 compliant) was
+ removed
+ * Official support for Python 3.7 was removed and for 3.10 added
+ * Layout and CSS interoperability was redesigned and greatly
+ improved
+ * Serialization protocol was redesigned and vastly improved
+ * CSS was modularized and all UI components are now Web
+ components
+ * Django integration was moved to
+ https://github.com/bokeh/bokeh-django
+ ## Minor changes
+ * Support added for favicon.ico files
+ * Initial import times reduced
+ * Support added for new selection policy NodesAndAdjacentNodes
+ * Plot.remove_tools() function added
+- Drop bokeh-pr12218-Pillow9.2.patch
+
-------------------------------------------------------------------
Thu Jul 7 17:32:06 UTC 2022 - Ben Greiner
diff --git a/python-bokeh.spec b/python-bokeh.spec
index a80d681..84a6cfa 100644
--- a/python-bokeh.spec
+++ b/python-bokeh.spec
@@ -1,5 +1,5 @@
#
-# spec file for package python-bokeh
+# spec file
#
# Copyright (c) 2022 SUSE LLC
#
@@ -16,55 +16,88 @@
#
-%define skip_python2 1
-%bcond_without tests
-Name: python-bokeh
-Version: 2.4.3
+%global flavor @BUILD_FLAVOR@%{nil}
+%if "%{flavor}" == "test"
+%define psuffix -test
+%bcond_without test
+%else
+%define psuffix %{nil}
+%bcond_with test
+%endif
+
+# too many flaky timeouts on obs servers
+%bcond_with testexamples
+
+Name: python-bokeh%{psuffix}
+Version: 3.0.1
Release: 0
Summary: Statistical interactive HTML plots for Python
License: BSD-3-Clause
-URL: https://github.com/bokeh/bokeh/
+URL: https://bokeh.org/
+# Source-URL: https://github.com/bokeh/bokeh/
+# for the precompiled JS files
Source0: https://files.pythonhosted.org/packages/source/b/bokeh/bokeh-%{version}.tar.gz
-Source1: https://raw.githubusercontent.com/bokeh/bokeh/%{version}/conftest.py
-#PATCH-FIX-UPSTREAM bokeh-pr12218-Pillow9.2.patch gh#bokeh/bokeh#12218
-Patch1: https://github.com/bokeh/bokeh/pull/12218.patch#/bokeh-pr12218-Pillow9.2.patch
+# for the tests
+Source1: https://github.com/bokeh/bokeh/archive/refs/tags/%{version}.tar.gz#/bokeh-%{version}-gh.tar.gz
+# Sampledata: `rm -rf .bokeh && HOME=$PWD bokeh sampledata && tar cJf bokeh-sampledata.tar.xz .bokeh`
+Source99: bokeh-sampledata.tar.xz
+# PATCH-FIX-UPSTREAM bokeh-remove-mock.patch gh#bokeh/bokeh#12561
+Patch1: bokeh-remove-mock.patch
+# PATCH-FIX-UPSTREAM bokeh-sys-executable.patch gh#bokeh/bokeh#12563
+Patch2: bokeh-sys-executable.patch
BuildRequires: %{python_module Jinja2 >= 2.9}
BuildRequires: %{python_module Pillow >= 7.1.0}
BuildRequires: %{python_module PyYAML >= 3.10}
-BuildRequires: %{python_module base >= 3.7}
+BuildRequires: %{python_module base >= 3.8}
+BuildRequires: %{python_module colorama}
+BuildRequires: %{python_module contourpy >= 1}
BuildRequires: %{python_module numpy >= 1.11.3}
BuildRequires: %{python_module packaging >= 16.8}
-BuildRequires: %{python_module python-dateutil >= 2.1}
+BuildRequires: %{python_module pandas >= 1.2}
+BuildRequires: %{python_module pip}
+BuildRequires: %{python_module setuptools-git-versioning}
BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module tornado >= 5.1}
-BuildRequires: %{python_module typing_extensions >= 3.7.4}
+BuildRequires: %{python_module wheel}
+BuildRequires: %{python_module xyzservices >= 2021.9.1 }
BuildRequires: fdupes
BuildRequires: python-rpm-macros
-BuildConflicts: python-buildservice-tweak
Requires: python-Jinja2 >= 2.9
Requires: python-Pillow >= 7.1.0
Requires: python-PyYAML >= 3.10
+Requires: python-base >= 3.8
+Requires: python-contourpy >= 1
Requires: python-numpy >= 1.11.3
Requires: python-packaging >= 16.8
-Requires: python-python-dateutil >= 2.1
+Requires: python-pandas >= 1.2
Requires: python-tornado >= 5.1
-Requires: python-typing_extensions >= 3.7.4
+Requires: python-xyzservices >= 2021.9.1
Requires(post): update-alternatives
Requires(postun):update-alternatives
-Recommends: python-python-dateutil
BuildArch: noarch
# SECTION test requirements
-%if %{with tests}
+%if %{with test}
BuildRequires: %{python_module beautifulsoup4}
+BuildRequires: %{python_module bokeh = %{version}}
+BuildRequires: %{python_module colorcet}
BuildRequires: %{python_module flaky}
-BuildRequires: %{python_module nbconvert}
+BuildRequires: %{python_module icalendar}
+BuildRequires: %{python_module json5}
+BuildRequires: %{python_module nbconvert >= 5.4}
BuildRequires: %{python_module networkx}
+BuildRequires: %{python_module pandas-datareader}
+BuildRequires: %{python_module pandas}
BuildRequires: %{python_module pydot}
-BuildRequires: %{python_module pytest-asyncio}
+BuildRequires: %{python_module pyshp}
+BuildRequires: %{python_module pytest-asyncio >= 0.18.1}
+BuildRequires: %{python_module pytest-xdist}
BuildRequires: %{python_module pytest}
-BuildRequires: %{python_module requests}
+BuildRequires: %{python_module requests >= 1.2.3}
+BuildRequires: %{python_module requests-unixsocket}
+BuildRequires: %{python_module scikit-learn}
BuildRequires: %{python_module selenium}
-BuildRequires: nodejs >= 14
+BuildRequires: %{python_module tornado}
+BuildRequires: npm19
%endif
# /SECTION
%python_subpackages
@@ -76,48 +109,68 @@ graphics in the style of D3.js, and favors delivering this capability
with interactivity over large or streaming datasets.
%prep
-%autosetup -p1 -n bokeh-%{version}
-# add conftest.py for pytest fixtures
-cp %{SOURCE1} .
-# remove external mock in favor of unittest.mock
-find tests -name '*.py' -exec \
- sed -i {} \
- -e 's/^import mock/from unittest import mock/' \
- -e 's/^from mock import mock/from unittest import mock/' \
- -e 's/^from mock import/from unittest.mock import/' \
- ';'
+%if !%{with test}
+%setup -q -n bokeh-%{version}
+%else
+%setup -q -n bokeh-%{version} -T -b1 -a99
+%patch1 -p1
+%patch2 -p1
+%endif
+%if !%{with test}
%build
-%python_build
+# Needs the JS files prepackaged in src/bokeh/server/static/, marked by PKG-INFO in sdist
+%pyproject_wheel
%install
-%python_install
+%pyproject_install
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%python_clone -a %{buildroot}%{_bindir}/bokeh
-# Remove hidden files
+# Remove hidden files for git repos
%python_expand rm %{buildroot}%{$python_sitelib}/bokeh/server/static/.keep
+%endif
-# Remove test and script files
-%python_expand rm -rf %{buildroot}%{$python_sitelib}/scripts/
-%python_expand rm -rf %{buildroot}%{$python_sitelib}/tests/
-
-%if %{with tests}
+%if %{with test}
%check
-# GUI based selenium with chromedriver setup within OBS is hard/impossible
-deselectmarker="selenium"
-# sampledata: no download
-deselectmarker+=" or sampledata"
# npm can't build inside obs without network
-deselectname="test_ext_commands"
-# testfile not packaged in sdist
+deselectname="test_bokehjs or test_ext_commands"
+%if %{with testexamples}
+# loads external font
+deselectname+=" or (test_examples and font-awesome)"
+# no squarify
+deselectname+=" or (test_examples and treemap)"
+%else
+# too many flaky timeouts on obs servers
+deselectname+=" or test_examples"
+%endif
+# testfile not available
deselectname+=" or test_with_INLINE_resources"
deselectname+=" or test_with_CDN_resources"
# does not expect pytest-$binsuffix
deselectname+=" or test_detect_current_filename"
-# no browser installed
-deselectname+=" or test_webdriver"
-%pytest -m "not ($deselectmarker)" -k "not ($deselectname)"
+# cannot open socket / address already in use / no pattern detected
+deselectname+=" or (test_server and test_address)"
+deselectname+=" or (test_serve and printed)"
+deselectname+=" or test__ioloop_not_forcibly_stopped"
+# not json5 serializable
+deselectname+=" or test_defaults"
+# flaky timeouts
+deselectname+=" or (test_deprecation and (test_since or test_message))"
+# test can't list modules correctly in test environment
+deselectname+=" or (codebase and combined)"
+# extraneous fields
+deselectname+=" or test_serialization_data_models"
+# linting and code structure irrelevant for rpm package
+deselectname+=" or test_flake8 or test_isort or test_eslint or test_code_quality or test_no_request_host"
+# no driver (chromedriver only x86_64)
+deselectname+=" or Test_webdriver_control"
+# fails when tested with pytest-xdist
+deselectname+=" or (TestModelCls and test_get_class)"
+deselectname+=" or test_external_js_and_css_resource_ordering"
+# for finding the sampledata (packaged in Source99)
+export HOME=$PWD
+%pytest -v -m "not selenium" -k "not ($deselectname)" --no-js -n auto
%endif
%post
@@ -126,11 +179,13 @@ deselectname+=" or test_webdriver"
%postun
%python_uninstall_alternative bokeh
+%if !%{with test}
%files %{python_files}
%license LICENSE.txt
-%doc CHANGELOG README.md
+%doc README.md
%python_alternative %{_bindir}/bokeh
%{python_sitelib}/bokeh/
-%{python_sitelib}/bokeh-%{version}-py%{python_version}.egg-info
+%{python_sitelib}/bokeh-%{version}*-info
+%endif
%changelog