From edfbb6c4e7e11b7c272db1d6b28c60119a0ab9a2 Mon Sep 17 00:00:00 2001 From: Kevin Bates Date: Mon, 28 Nov 2022 09:17:17 -0800 Subject: [PATCH 1/4] Add initial support for ServerKernelManager --- .../services/kernels/kernelmanager.py | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/jupyter_server/services/kernels/kernelmanager.py b/jupyter_server/services/kernels/kernelmanager.py index 1f57d1ee77..8f15ce9329 100644 --- a/jupyter_server/services/kernels/kernelmanager.py +++ b/jupyter_server/services/kernels/kernelmanager.py @@ -7,10 +7,12 @@ # Distributed under the terms of the Modified BSD License. import asyncio import os +import warnings from collections import defaultdict from datetime import datetime, timedelta from functools import partial +from jupyter_client.ioloop.manager import AsyncIOLoopKernelManager from jupyter_client.multikernelmanager import ( AsyncMultiKernelManager, MultiKernelManager, @@ -36,7 +38,7 @@ from jupyter_server._tz import isoformat, utcnow from jupyter_server.prometheus.metrics import KERNEL_CURRENTLY_RUNNING_TOTAL -from jupyter_server.utils import ensure_async, to_os_path +from jupyter_server.utils import ensure_async, import_item, to_os_path class MappingKernelManager(MultiKernelManager): @@ -656,10 +658,39 @@ async def cull_kernel_if_idle(self, kernel_id): class AsyncMappingKernelManager(MappingKernelManager, AsyncMultiKernelManager): @default("kernel_manager_class") def _default_kernel_manager_class(self): - return "jupyter_client.ioloop.AsyncIOLoopKernelManager" + return "jupyter_server.services.kernels.kernelmanager.ServerKernelManager" + + @validate("kernel_manager_class") + def _validate_kernel_manager_class(self, proposal): + km_class_value = proposal.value + km_class = import_item(km_class_value) + if not issubclass(km_class, ServerKernelManager): + warnings.warn( + f"KernelManager class '{km_class}' is not a subclass of `ServerKernelManager`. Custom " + "KernelManager classes should derive from 'ServerKernelManager' beginning with jupyter-server 2.0 " + "or risk missing functionality. Continuing...", + FutureWarning, + stacklevel=3, + ) + return km_class_value def __init__(self, **kwargs): self.pinned_superclass = MultiKernelManager self._pending_kernel_tasks = {} self.pinned_superclass.__init__(self, **kwargs) self.last_kernel_activity = utcnow() + + +class ServerKernelManager(AsyncIOLoopKernelManager): + + # Define activity-related attributes: + execution_state = Unicode("initializing", help="The current execution state of the kernel") + reason = Unicode("", help="The reason for the last failure against the kernel") + last_activity = Instance(datetime, help="The last activity on the kernel") + + @default("last_activity") + def _default_last_activity(self): + return utcnow() + + async def start_kernel(self, **kw: Any) -> None: + await super().start_kernel(**kw) From 4057a1eba7a572742b25c1d413a177f06bcef350 Mon Sep 17 00:00:00 2001 From: Kevin Bates Date: Mon, 28 Nov 2022 10:47:30 -0800 Subject: [PATCH 2/4] add test to check warning has been emitted --- jupyter_server/services/kernels/kernelmanager.py | 2 +- tests/services/kernels/test_config.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/jupyter_server/services/kernels/kernelmanager.py b/jupyter_server/services/kernels/kernelmanager.py index 8f15ce9329..038dd46fe7 100644 --- a/jupyter_server/services/kernels/kernelmanager.py +++ b/jupyter_server/services/kernels/kernelmanager.py @@ -666,7 +666,7 @@ def _validate_kernel_manager_class(self, proposal): km_class = import_item(km_class_value) if not issubclass(km_class, ServerKernelManager): warnings.warn( - f"KernelManager class '{km_class}' is not a subclass of `ServerKernelManager`. Custom " + f"KernelManager class '{km_class}' is not a subclass of 'ServerKernelManager'. Custom " "KernelManager classes should derive from 'ServerKernelManager' beginning with jupyter-server 2.0 " "or risk missing functionality. Continuing...", FutureWarning, diff --git a/tests/services/kernels/test_config.py b/tests/services/kernels/test_config.py index 1ab4db3bcb..9e9fdd1141 100644 --- a/tests/services/kernels/test_config.py +++ b/tests/services/kernels/test_config.py @@ -1,3 +1,5 @@ +import warnings + import pytest from traitlets.config import Config @@ -21,3 +23,11 @@ def test_async_kernel_manager(jp_configurable_serverapp): ] app = jp_configurable_serverapp(argv=argv) assert isinstance(app.kernel_manager, AsyncMappingKernelManager) + + +def test_not_server_kernel_manager(jp_configurable_serverapp): + argv = [ + "--AsyncMappingKernelManager.kernel_manager_class=jupyter_client.ioloop.manager.AsyncIOLoopKernelManager" + ] + with pytest.warns(FutureWarning, match="is not a subclass of 'ServerKernelManager'"): + jp_configurable_serverapp(argv=argv) From 21fe5dff041ab644234a07d85784e8ed08419cdb Mon Sep 17 00:00:00 2001 From: Kevin Bates Date: Mon, 28 Nov 2022 11:25:34 -0800 Subject: [PATCH 3/4] remove unused import --- tests/services/kernels/test_config.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/services/kernels/test_config.py b/tests/services/kernels/test_config.py index 9e9fdd1141..1db2e11b1f 100644 --- a/tests/services/kernels/test_config.py +++ b/tests/services/kernels/test_config.py @@ -1,5 +1,3 @@ -import warnings - import pytest from traitlets.config import Config From 1a3c2f31a82c1df24fb91be08cd45d20c353418d Mon Sep 17 00:00:00 2001 From: Kevin Bates Date: Mon, 28 Nov 2022 15:36:23 -0800 Subject: [PATCH 4/4] Allow None for execution_state, remove unnecessary code --- jupyter_server/services/kernels/kernelmanager.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/jupyter_server/services/kernels/kernelmanager.py b/jupyter_server/services/kernels/kernelmanager.py index 038dd46fe7..a657f08562 100644 --- a/jupyter_server/services/kernels/kernelmanager.py +++ b/jupyter_server/services/kernels/kernelmanager.py @@ -684,13 +684,8 @@ def __init__(self, **kwargs): class ServerKernelManager(AsyncIOLoopKernelManager): # Define activity-related attributes: - execution_state = Unicode("initializing", help="The current execution state of the kernel") + execution_state = Unicode( + None, allow_none=True, help="The current execution state of the kernel" + ) reason = Unicode("", help="The reason for the last failure against the kernel") last_activity = Instance(datetime, help="The last activity on the kernel") - - @default("last_activity") - def _default_last_activity(self): - return utcnow() - - async def start_kernel(self, **kw: Any) -> None: - await super().start_kernel(**kw)