From 036466431718ed646210271129cde3616c0cab6d Mon Sep 17 00:00:00 2001 From: Zach Sailer Date: Mon, 29 Sep 2025 10:28:21 -0700 Subject: [PATCH 1/2] Make ServerKernelManager client traits configurable Add config=True to client_class and client_factory traits in ServerKernelManager to enable configuration of kernel client classes through configuration files and command line arguments. This enables custom kernel clients to be used for intercepting and routing messages, which is required for server-side document handling. Changes: - Override client_class and client_factory traits with config=True - Use correct default values matching parent class behavior - Add comprehensive unit tests for configuration via Config objects - Test default values, string configuration, and factory configuration - Import AsyncKernelClient directly instead of using import_item All tests pass and maintain backward compatibility while enabling the configurable functionality needed for jupyter-server-documents project. --- .../services/kernels/kernelmanager.py | 27 +++++++++++++++ tests/services/kernels/test_config.py | 34 ++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/jupyter_server/services/kernels/kernelmanager.py b/jupyter_server/services/kernels/kernelmanager.py index 8f4e8277f9..04dd1b8515 100644 --- a/jupyter_server/services/kernels/kernelmanager.py +++ b/jupyter_server/services/kernels/kernelmanager.py @@ -18,6 +18,7 @@ from datetime import datetime, timedelta from functools import partial, wraps +from jupyter_client.asynchronous.client import AsyncKernelClient from jupyter_client.ioloop.manager import AsyncIOLoopKernelManager from jupyter_client.multikernelmanager import AsyncMultiKernelManager, MultiKernelManager from jupyter_client.session import Session @@ -37,11 +38,13 @@ Any, Bool, Dict, + DottedObjectName, Float, Instance, Integer, List, TraitError, + Type, Unicode, default, validate, @@ -845,6 +848,30 @@ async def wrapped_method(self, *args, **kwargs): class ServerKernelManager(AsyncIOLoopKernelManager): """A server-specific kernel manager.""" + # Override parent traits to make them configurable + client_class = DottedObjectName( + "jupyter_client.asynchronous.AsyncKernelClient", + config=True, + help="""The kernel client class to use for communicating with kernels. + + This should be a subclass of KernelClient, and it should accept the + following arguments: + - kernel_manager + - blocking + - loop + """ + ) + + client_factory = Type( + default_value=AsyncKernelClient, + klass="jupyter_client.client.KernelClient", + config=True, + help="""The kernel client factory class to use for creating client instances. + + This should be a subclass of KernelClient. + """ + ) + # Define activity-related attributes: execution_state = Unicode( None, allow_none=True, help="The current execution state of the kernel" diff --git a/tests/services/kernels/test_config.py b/tests/services/kernels/test_config.py index 1db2e11b1f..153416e33f 100644 --- a/tests/services/kernels/test_config.py +++ b/tests/services/kernels/test_config.py @@ -1,7 +1,11 @@ import pytest from traitlets.config import Config -from jupyter_server.services.kernels.kernelmanager import AsyncMappingKernelManager +from jupyter_client.asynchronous.client import AsyncKernelClient +from jupyter_client.blocking.client import BlockingKernelClient +from jupyter_client.client import KernelClient +from jupyter_server.services.kernels.kernelmanager import AsyncMappingKernelManager, ServerKernelManager +from jupyter_server.utils import import_item @pytest.fixture @@ -29,3 +33,31 @@ def test_not_server_kernel_manager(jp_configurable_serverapp): ] with pytest.warns(FutureWarning, match="is not a subclass of 'ServerKernelManager'"): jp_configurable_serverapp(argv=argv) + + +def test_server_kernel_manager_client_traits_via_config(): + """Test that ServerKernelManager client traits can be configured via Config object.""" + config = Config() + config.ServerKernelManager.client_class = "jupyter_client.blocking.client.BlockingKernelClient" + config.ServerKernelManager.client_factory = BlockingKernelClient + + km = ServerKernelManager(config=config) + assert km.client_class == "jupyter_client.blocking.client.BlockingKernelClient" + assert km.client_factory == BlockingKernelClient + + +def test_server_kernel_manager_client_traits_default_values(): + """Test that ServerKernelManager client traits have correct default values.""" + km = ServerKernelManager() + assert km.client_class == "jupyter_client.asynchronous.AsyncKernelClient" + # Default client_factory should be the AsyncKernelClient class + assert km.client_factory == AsyncKernelClient + + +def test_server_kernel_manager_client_class_string_configuration(): + """Test that client_class can be configured with different string values.""" + config = Config() + config.ServerKernelManager.client_class = "jupyter_client.client.KernelClient" + + km = ServerKernelManager(config=config) + assert km.client_class == "jupyter_client.client.KernelClient" From fe19bf3b78d8324cd40b0a86d7c1604439568999 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:29:22 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- jupyter_server/services/kernels/kernelmanager.py | 4 ++-- tests/services/kernels/test_config.py | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/jupyter_server/services/kernels/kernelmanager.py b/jupyter_server/services/kernels/kernelmanager.py index 04dd1b8515..ee80799c21 100644 --- a/jupyter_server/services/kernels/kernelmanager.py +++ b/jupyter_server/services/kernels/kernelmanager.py @@ -859,7 +859,7 @@ class ServerKernelManager(AsyncIOLoopKernelManager): - kernel_manager - blocking - loop - """ + """, ) client_factory = Type( @@ -869,7 +869,7 @@ class ServerKernelManager(AsyncIOLoopKernelManager): help="""The kernel client factory class to use for creating client instances. This should be a subclass of KernelClient. - """ + """, ) # Define activity-related attributes: diff --git a/tests/services/kernels/test_config.py b/tests/services/kernels/test_config.py index 153416e33f..657f8c8ea1 100644 --- a/tests/services/kernels/test_config.py +++ b/tests/services/kernels/test_config.py @@ -1,10 +1,13 @@ import pytest -from traitlets.config import Config - from jupyter_client.asynchronous.client import AsyncKernelClient from jupyter_client.blocking.client import BlockingKernelClient from jupyter_client.client import KernelClient -from jupyter_server.services.kernels.kernelmanager import AsyncMappingKernelManager, ServerKernelManager +from traitlets.config import Config + +from jupyter_server.services.kernels.kernelmanager import ( + AsyncMappingKernelManager, + ServerKernelManager, +) from jupyter_server.utils import import_item