From 89402b60f538f485bdf84870db2173292cadd8d9 Mon Sep 17 00:00:00 2001 From: Darshan808 Date: Tue, 7 Jan 2025 23:36:05 +0545 Subject: [PATCH 01/11] add-execution-timing-metadata --- jupyter_server_nbmodel/handlers.py | 12 ++++++++++-- src/executor.ts | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/jupyter_server_nbmodel/handlers.py b/jupyter_server_nbmodel/handlers.py index 2627f6e..7cbeb3e 100644 --- a/jupyter_server_nbmodel/handlers.py +++ b/jupyter_server_nbmodel/handlers.py @@ -8,6 +8,7 @@ from dataclasses import asdict, dataclass from functools import partial from http import HTTPStatus +from datetime import datetime, timezone import jupyter_server import jupyter_server.services @@ -123,7 +124,6 @@ async def _get_ycell( raise KeyError( msg, ) - return ycell @@ -219,6 +219,7 @@ async def _execute_snippet( The execution status and outputs. """ ycell = None + time_info = {} if metadata is not None: ycell = await _get_ycell(ydoc, metadata) if ycell is not None: @@ -227,7 +228,11 @@ async def _execute_snippet( del ycell["outputs"][:] ycell["execution_count"] = None ycell["execution_state"] = "running" - + if metadata["record_timing"]: + time_info = ycell["metadata"].get("execution",{}) + time_info["start_time"] = datetime.now(timezone.utc).isoformat()[:-6] + ycell["metadata"]["execution"] = time_info + outputs = [] # FIXME we don't check if the session is consistent (aka the kernel is linked to the document) @@ -247,6 +252,9 @@ async def _execute_snippet( with ycell.doc.transaction(): ycell["execution_count"] = reply_content.get("execution_count") ycell["execution_state"] = "idle" + if metadata["record_timing"]: + time_info["end_time"] = datetime.now(timezone.utc).isoformat()[:-6] + ycell["metadata"]["execution"] = time_info return { "status": reply_content["status"], diff --git a/src/executor.ts b/src/executor.ts index cd3c2ee..fabfaee 100644 --- a/src/executor.ts +++ b/src/executor.ts @@ -108,12 +108,13 @@ export class NotebookCellServerExecutor implements INotebookCellExecutor { const code = cell.model.sharedModel.getSource(); const cellId = cell.model.sharedModel.getId(); const documentId = notebook.sharedModel.getState('document_id'); + const { recordTiming } = notebookConfig const init = { method: 'POST', body: JSON.stringify({ code, - metadata: { cell_id: cellId, document_id: documentId } + metadata: { cell_id: cellId, document_id: documentId, record_timing: recordTiming } }) }; onCellExecutionScheduled({ cell }); From b5d1ad7adc86747866855b186efddf10fb6176a5 Mon Sep 17 00:00:00 2001 From: Darshan808 Date: Thu, 9 Jan 2025 16:14:21 +0545 Subject: [PATCH 02/11] add test for timing metadata --- conftest.py | 12 +++- jupyter_server_nbmodel/handlers.py | 10 ++-- jupyter_server_nbmodel/tests/test_handlers.py | 55 +++++++++++++++++++ 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/conftest.py b/conftest.py index 6461459..c11e331 100644 --- a/conftest.py +++ b/conftest.py @@ -1,12 +1,18 @@ import pytest -pytest_plugins = ("pytest_jupyter.jupyter_server",) +pytest_plugins = ("pytest_jupyter.jupyter_server","jupyter_server_ydoc.pytest_plugin") @pytest.fixture def jp_server_config(jp_server_config): return { "ServerApp": { - "jpserver_extensions": {"jupyter_server_nbmodel": True, "jupyter_server_ydoc": False} - } + "jpserver_extensions": { + "jupyter_server_ydoc": True, + "jupyter_server_nbmodel": True, + "jupyter_server_fileid": True, + }, + 'IdentityProvider': {'token': ''}, + "disable_check_xsrf": True, + }, } diff --git a/jupyter_server_nbmodel/handlers.py b/jupyter_server_nbmodel/handlers.py index 7cbeb3e..378f5b4 100644 --- a/jupyter_server_nbmodel/handlers.py +++ b/jupyter_server_nbmodel/handlers.py @@ -230,7 +230,7 @@ async def _execute_snippet( ycell["execution_state"] = "running" if metadata["record_timing"]: time_info = ycell["metadata"].get("execution",{}) - time_info["start_time"] = datetime.now(timezone.utc).isoformat()[:-6] + time_info["shell.execute_reply.started"] = datetime.now(timezone.utc).isoformat()[:-6] ycell["metadata"]["execution"] = time_info outputs = [] @@ -253,9 +253,11 @@ async def _execute_snippet( ycell["execution_count"] = reply_content.get("execution_count") ycell["execution_state"] = "idle" if metadata["record_timing"]: - time_info["end_time"] = datetime.now(timezone.utc).isoformat()[:-6] + time_info["shell.execute_reply"] = datetime.now(timezone.utc).isoformat()[:-6] ycell["metadata"]["execution"] = time_info - + if reply_content["status"]!="ok": + time_info["execution_state"] = "failed" + ycell["metadata"]["execution"] = time_info return { "status": reply_content["status"], "execution_count": reply_content.get("execution_count"), @@ -532,9 +534,7 @@ async def post(self, kernel_id: str) -> None: msg = f"Unknown kernel with id: {kernel_id}" get_logger().error(msg) raise tornado.web.HTTPError(status_code=HTTPStatus.NOT_FOUND, reason=msg) - uid = self._execution_stack.put(kernel_id, snippet, metadata) - self.set_status(HTTPStatus.ACCEPTED) self.set_header("Location", f"/api/kernels/{kernel_id}/requests/{uid}") self.finish("{}") diff --git a/jupyter_server_nbmodel/tests/test_handlers.py b/jupyter_server_nbmodel/tests/test_handlers.py index eade4e2..be5ad38 100644 --- a/jupyter_server_nbmodel/tests/test_handlers.py +++ b/jupyter_server_nbmodel/tests/test_handlers.py @@ -2,6 +2,7 @@ import datetime import json import re +import nbformat import pytest from jupyter_client.kernelspec import NATIVE_KERNEL_NAME @@ -148,6 +149,60 @@ async def test_post_erroneous_execute(jp_fetch, pending_kernel_is_ready, snippet await asyncio.sleep(1) +@pytest.mark.timeout(TEST_TIMEOUT) +async def test_execution_timing_metadata(jp_fetch, pending_kernel_is_ready, rtc_create_notebook, jp_serverapp): + snippet = "a = 1" + nb = nbformat.v4.new_notebook( + cells=[nbformat.v4.new_code_cell(source=snippet, execution_count=1)] + ) + nb_content = nbformat.writes(nb, version=4) + path, _ = await rtc_create_notebook("test.ipynb", nb_content, store=True) + collaboration = jp_serverapp.web_app.settings["jupyter_server_ydoc"] + fim = jp_serverapp.web_app.settings["file_id_manager"] + document = await collaboration.get_document( + path=path, content_type="notebook", file_format="json", copy=False + ) + doc = document.get() + document_id = f'json:notebook:{fim.get_id("test.ipynb")}' + cell_id = doc["cells"][0].get("id") + + r = await jp_fetch( + "api", "kernels", method="POST", body=json.dumps({"name": NATIVE_KERNEL_NAME}) + ) + kernel = json.loads(r.body.decode()) + await pending_kernel_is_ready(kernel["id"]) + + response = await wait_for_request( + jp_fetch, + "api", + "kernels", + kernel["id"], + "execute", + method="POST", + body=json.dumps({"code": snippet, "metadata":{"cell_id":cell_id,"document_id":document_id,"record_timing":True}}), + ) + assert response.code == 200 + cell_data = document.get()["cells"][0] + assert 'execution' in cell_data['metadata'], "'execution' does not exist in 'metadata'" + + # Assert that start and end time exist in 'execution' + execution = cell_data['metadata']['execution'] + assert 'shell.execute_reply.started' in execution, "'shell.execute_reply.started' does not exist in 'execution'" + assert 'shell.execute_reply' in execution, "'shell.execute_reply' does not exist in 'execution'" + + started_time = execution['shell.execute_reply.started'] + reply_time = execution['shell.execute_reply'] + + started_dt = datetime.datetime.fromisoformat(started_time) + reply_dt = datetime.datetime.fromisoformat(reply_time) + + # Assert that reply_time is greater than started_time + assert reply_dt > started_dt, "The reply time is not greater than the started time." + response2 = await jp_fetch("api", "kernels", kernel["id"], method="DELETE") + assert response2.code == 204 + await jp_serverapp.web_app.settings["jupyter_server_ydoc"].stop_extension() + del jp_serverapp.web_app.settings["file_id_manager"] + await asyncio.sleep(1) @pytest.mark.timeout(TEST_TIMEOUT) async def test_post_input_execute(jp_fetch, pending_kernel_is_ready): From 7b01c0835fcdf1a5a71a0acc914623a89914afb4 Mon Sep 17 00:00:00 2001 From: Darshan808 Date: Thu, 9 Jan 2025 16:25:15 +0545 Subject: [PATCH 03/11] add additional config --- conftest.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/conftest.py b/conftest.py index c11e331..7130bd5 100644 --- a/conftest.py +++ b/conftest.py @@ -4,7 +4,7 @@ @pytest.fixture -def jp_server_config(jp_server_config): +def jp_server_config(jp_root_dir,jp_server_config): return { "ServerApp": { "jpserver_extensions": { @@ -12,6 +12,13 @@ def jp_server_config(jp_server_config): "jupyter_server_nbmodel": True, "jupyter_server_fileid": True, }, + "SQLiteYStore": {"db_path": str(jp_root_dir.joinpath(".rtc_test.db"))}, + "BaseFileIdManager": { + "root_dir": str(jp_root_dir), + "db_path": str(jp_root_dir.joinpath(".fid_test.db")), + "db_journal_mode": "OFF", + }, + "YDocExtension": {"document_save_delay": 1}, 'IdentityProvider': {'token': ''}, "disable_check_xsrf": True, }, From f3e13de6db2b26a2912054885cce9b64c8707ceb Mon Sep 17 00:00:00 2001 From: Darshan808 Date: Thu, 9 Jan 2025 16:29:23 +0545 Subject: [PATCH 04/11] lint fix --- src/executor.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/executor.ts b/src/executor.ts index fabfaee..c1d5154 100644 --- a/src/executor.ts +++ b/src/executor.ts @@ -108,13 +108,17 @@ export class NotebookCellServerExecutor implements INotebookCellExecutor { const code = cell.model.sharedModel.getSource(); const cellId = cell.model.sharedModel.getId(); const documentId = notebook.sharedModel.getState('document_id'); - const { recordTiming } = notebookConfig + const { recordTiming } = notebookConfig; const init = { method: 'POST', body: JSON.stringify({ code, - metadata: { cell_id: cellId, document_id: documentId, record_timing: recordTiming } + metadata: { + cell_id: cellId, + document_id: documentId, + record_timing: recordTiming + } }) }; onCellExecutionScheduled({ cell }); From 891a44b3e6faffb198a67d3942b6418b791ba1df Mon Sep 17 00:00:00 2001 From: Darshan808 Date: Thu, 9 Jan 2025 16:50:20 +0545 Subject: [PATCH 05/11] add ydoc as test dependency --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d49d5ce..cd520e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ dynamic = ["version", "description", "authors", "urls", "keywords"] [project.optional-dependencies] lab = ["jupyterlab>=4.2.0", "jupyter-docprovider>=1.0.0b1", "jupyter-server-ydoc>=1.0.0b1"] -test = ["pytest~=8.2", "pytest-cov", "pytest-jupyter[server]>=0.6", "pytest-timeout"] +test = ["pytest~=8.2", "pytest-cov", "pytest-jupyter[server]>=0.6", "pytest-timeout", "jupyter-server-ydoc>=1.0.0b1"] lint = ["mdformat>0.7", "mdformat-gfm>=0.3.5", "ruff>=0.4.0"] typing = ["mypy>=0.990"] From bb08e9399d29d375b3a24754171cbb22aa1343ff Mon Sep 17 00:00:00 2001 From: Darshan808 Date: Thu, 9 Jan 2025 17:11:43 +0545 Subject: [PATCH 06/11] improve code --- conftest.py | 7 +++++-- jupyter_server_nbmodel/handlers.py | 9 +++++---- pyproject.toml | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/conftest.py b/conftest.py index 7130bd5..e8ec44a 100644 --- a/conftest.py +++ b/conftest.py @@ -1,6 +1,9 @@ import pytest -pytest_plugins = ("pytest_jupyter.jupyter_server","jupyter_server_ydoc.pytest_plugin") +pytest_plugins = ( + "pytest_jupyter.jupyter_server", + "jupyter_server_ydoc.pytest_plugin" + ) @pytest.fixture @@ -19,7 +22,7 @@ def jp_server_config(jp_root_dir,jp_server_config): "db_journal_mode": "OFF", }, "YDocExtension": {"document_save_delay": 1}, - 'IdentityProvider': {'token': ''}, + "IdentityProvider": {"token": ""}, "disable_check_xsrf": True, }, } diff --git a/jupyter_server_nbmodel/handlers.py b/jupyter_server_nbmodel/handlers.py index 378f5b4..5ad9ea0 100644 --- a/jupyter_server_nbmodel/handlers.py +++ b/jupyter_server_nbmodel/handlers.py @@ -253,11 +253,12 @@ async def _execute_snippet( ycell["execution_count"] = reply_content.get("execution_count") ycell["execution_state"] = "idle" if metadata["record_timing"]: - time_info["shell.execute_reply"] = datetime.now(timezone.utc).isoformat()[:-6] + end_time = datetime.now(timezone.utc).isoformat()[:-6] + if reply_content["status"] == "ok": + time_info["shell.execute_reply"] = end_time + else: + time_info["execution_failed"] = end_time ycell["metadata"]["execution"] = time_info - if reply_content["status"]!="ok": - time_info["execution_state"] = "failed" - ycell["metadata"]["execution"] = time_info return { "status": reply_content["status"], "execution_count": reply_content.get("execution_count"), diff --git a/pyproject.toml b/pyproject.toml index cd520e7..fb30612 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ dynamic = ["version", "description", "authors", "urls", "keywords"] [project.optional-dependencies] lab = ["jupyterlab>=4.2.0", "jupyter-docprovider>=1.0.0b1", "jupyter-server-ydoc>=1.0.0b1"] -test = ["pytest~=8.2", "pytest-cov", "pytest-jupyter[server]>=0.6", "pytest-timeout", "jupyter-server-ydoc>=1.0.0b1"] +test = ["pytest~=8.2", "pytest-cov", "pytest-jupyter[server]>=0.6", "pytest-timeout", "jupyter-server-ydoc[test]>=1.0.0b1"] lint = ["mdformat>0.7", "mdformat-gfm>=0.3.5", "ruff>=0.4.0"] typing = ["mypy>=0.990"] From e56150169e8e47051d4749814d4a34f26d6bd5b7 Mon Sep 17 00:00:00 2001 From: Darshan808 Date: Thu, 9 Jan 2025 17:41:21 +0545 Subject: [PATCH 07/11] drop stop_extension() and del fim --- jupyter_server_nbmodel/tests/test_handlers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/jupyter_server_nbmodel/tests/test_handlers.py b/jupyter_server_nbmodel/tests/test_handlers.py index be5ad38..0728116 100644 --- a/jupyter_server_nbmodel/tests/test_handlers.py +++ b/jupyter_server_nbmodel/tests/test_handlers.py @@ -200,8 +200,6 @@ async def test_execution_timing_metadata(jp_fetch, pending_kernel_is_ready, rtc_ assert reply_dt > started_dt, "The reply time is not greater than the started time." response2 = await jp_fetch("api", "kernels", kernel["id"], method="DELETE") assert response2.code == 204 - await jp_serverapp.web_app.settings["jupyter_server_ydoc"].stop_extension() - del jp_serverapp.web_app.settings["file_id_manager"] await asyncio.sleep(1) @pytest.mark.timeout(TEST_TIMEOUT) From 7c2ac6b13f4dd89026ec949a3254ab7579a8f574 Mon Sep 17 00:00:00 2001 From: Darshan808 Date: Thu, 9 Jan 2025 18:11:26 +0545 Subject: [PATCH 08/11] add jupyter-server-fileid in test-dependencies --- jupyter_server_nbmodel/tests/test_handlers.py | 2 ++ pyproject.toml | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/jupyter_server_nbmodel/tests/test_handlers.py b/jupyter_server_nbmodel/tests/test_handlers.py index 0728116..be5ad38 100644 --- a/jupyter_server_nbmodel/tests/test_handlers.py +++ b/jupyter_server_nbmodel/tests/test_handlers.py @@ -200,6 +200,8 @@ async def test_execution_timing_metadata(jp_fetch, pending_kernel_is_ready, rtc_ assert reply_dt > started_dt, "The reply time is not greater than the started time." response2 = await jp_fetch("api", "kernels", kernel["id"], method="DELETE") assert response2.code == 204 + await jp_serverapp.web_app.settings["jupyter_server_ydoc"].stop_extension() + del jp_serverapp.web_app.settings["file_id_manager"] await asyncio.sleep(1) @pytest.mark.timeout(TEST_TIMEOUT) diff --git a/pyproject.toml b/pyproject.toml index fb30612..b0920be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,14 @@ dynamic = ["version", "description", "authors", "urls", "keywords"] [project.optional-dependencies] lab = ["jupyterlab>=4.2.0", "jupyter-docprovider>=1.0.0b1", "jupyter-server-ydoc>=1.0.0b1"] -test = ["pytest~=8.2", "pytest-cov", "pytest-jupyter[server]>=0.6", "pytest-timeout", "jupyter-server-ydoc[test]>=1.0.0b1"] +test = [ + "pytest~=8.2", + "pytest-cov", + "pytest-jupyter[server]>=0.6", + "pytest-timeout", + "jupyter-server-ydoc[test]>=1.0.0b1", + "jupyter-server-fileid" +] lint = ["mdformat>0.7", "mdformat-gfm>=0.3.5", "ruff>=0.4.0"] typing = ["mypy>=0.990"] From 04dec3fea2d926ef1aea2a6767ef34ecb2bfdb8d Mon Sep 17 00:00:00 2001 From: Darshan808 Date: Thu, 9 Jan 2025 19:32:09 +0545 Subject: [PATCH 09/11] resolve CI stall --- jupyter_server_nbmodel/tests/test_handlers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/jupyter_server_nbmodel/tests/test_handlers.py b/jupyter_server_nbmodel/tests/test_handlers.py index be5ad38..0728116 100644 --- a/jupyter_server_nbmodel/tests/test_handlers.py +++ b/jupyter_server_nbmodel/tests/test_handlers.py @@ -200,8 +200,6 @@ async def test_execution_timing_metadata(jp_fetch, pending_kernel_is_ready, rtc_ assert reply_dt > started_dt, "The reply time is not greater than the started time." response2 = await jp_fetch("api", "kernels", kernel["id"], method="DELETE") assert response2.code == 204 - await jp_serverapp.web_app.settings["jupyter_server_ydoc"].stop_extension() - del jp_serverapp.web_app.settings["file_id_manager"] await asyncio.sleep(1) @pytest.mark.timeout(TEST_TIMEOUT) From cc36797ea219d5585db77641f0c31775adf20242 Mon Sep 17 00:00:00 2001 From: Darshan808 Date: Fri, 10 Jan 2025 08:10:12 +0545 Subject: [PATCH 10/11] add additional plugins --- conftest.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/conftest.py b/conftest.py index e8ec44a..4f3d571 100644 --- a/conftest.py +++ b/conftest.py @@ -1,28 +1,31 @@ import pytest -pytest_plugins = ( +pytest_plugins = [ "pytest_jupyter.jupyter_server", + "jupyter_server.pytest_plugin", + "jupyter_server_fileid.pytest_plugin", "jupyter_server_ydoc.pytest_plugin" - ) - +] @pytest.fixture def jp_server_config(jp_root_dir,jp_server_config): return { - "ServerApp": { - "jpserver_extensions": { - "jupyter_server_ydoc": True, - "jupyter_server_nbmodel": True, - "jupyter_server_fileid": True, + 'ServerApp': { + 'jpserver_extensions': { + 'jupyter_server_ydoc': True, + 'jupyter_server_fileid': True, + 'jupyter_server_nbmodel': True }, + 'token': '', + 'password': '', + 'disable_check_xsrf': True}, "SQLiteYStore": {"db_path": str(jp_root_dir.joinpath(".rtc_test.db"))}, "BaseFileIdManager": { "root_dir": str(jp_root_dir), "db_path": str(jp_root_dir.joinpath(".fid_test.db")), "db_journal_mode": "OFF", }, - "YDocExtension": {"document_save_delay": 1}, - "IdentityProvider": {"token": ""}, - "disable_check_xsrf": True, - }, - } + 'YDocExtension': { + 'document_save_delay': 1 + } + } \ No newline at end of file From 358ea0da0108e9d16cb7966370a6a8fe954c3488 Mon Sep 17 00:00:00 2001 From: krassowski <5832902+krassowski@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:15:19 +0000 Subject: [PATCH 11/11] Attempt to fix the test issues, cleanup --- conftest.py | 28 ++++++++++--------- jupyter_server_nbmodel/extension.py | 1 + jupyter_server_nbmodel/handlers.py | 8 +++--- jupyter_server_nbmodel/tests/test_handlers.py | 23 ++++++++++----- pyproject.toml | 4 +++ 5 files changed, 40 insertions(+), 24 deletions(-) diff --git a/conftest.py b/conftest.py index 4f3d571..ee29be3 100644 --- a/conftest.py +++ b/conftest.py @@ -7,25 +7,27 @@ "jupyter_server_ydoc.pytest_plugin" ] + @pytest.fixture -def jp_server_config(jp_root_dir,jp_server_config): +def jp_server_config(jp_root_dir, jp_server_config): return { 'ServerApp': { 'jpserver_extensions': { 'jupyter_server_ydoc': True, 'jupyter_server_fileid': True, - 'jupyter_server_nbmodel': True + 'jupyter_server_nbmodel': True, }, 'token': '', 'password': '', - 'disable_check_xsrf': True}, - "SQLiteYStore": {"db_path": str(jp_root_dir.joinpath(".rtc_test.db"))}, - "BaseFileIdManager": { - "root_dir": str(jp_root_dir), - "db_path": str(jp_root_dir.joinpath(".fid_test.db")), - "db_journal_mode": "OFF", - }, - 'YDocExtension': { - 'document_save_delay': 1 - } - } \ No newline at end of file + 'disable_check_xsrf': True + }, + "SQLiteYStore": {"db_path": str(jp_root_dir.joinpath(".rtc_test.db"))}, + "BaseFileIdManager": { + "root_dir": str(jp_root_dir), + "db_path": str(jp_root_dir.joinpath(".fid_test.db")), + "db_journal_mode": "OFF", + }, + "YDocExtension": { + "document_save_delay": 1 + } + } diff --git a/jupyter_server_nbmodel/extension.py b/jupyter_server_nbmodel/extension.py index ad945ff..34dd205 100644 --- a/jupyter_server_nbmodel/extension.py +++ b/jupyter_server_nbmodel/extension.py @@ -56,4 +56,5 @@ def initialize_handlers(self): async def stop_extension(self): if hasattr(self, "__execution_stack"): get_logger().info("Disposing the execution stackā€¦") + await self.__execution_stack.dispose() await asyncio.wait_for(self.__execution_stack.dispose(), timeout=3) diff --git a/jupyter_server_nbmodel/handlers.py b/jupyter_server_nbmodel/handlers.py index 5ad9ea0..3d36a88 100644 --- a/jupyter_server_nbmodel/handlers.py +++ b/jupyter_server_nbmodel/handlers.py @@ -228,11 +228,11 @@ async def _execute_snippet( del ycell["outputs"][:] ycell["execution_count"] = None ycell["execution_state"] = "running" - if metadata["record_timing"]: - time_info = ycell["metadata"].get("execution",{}) + if metadata.get("record_timing", False): + time_info = ycell["metadata"].get("execution", {}) time_info["shell.execute_reply.started"] = datetime.now(timezone.utc).isoformat()[:-6] ycell["metadata"]["execution"] = time_info - + outputs = [] # FIXME we don't check if the session is consistent (aka the kernel is linked to the document) @@ -252,7 +252,7 @@ async def _execute_snippet( with ycell.doc.transaction(): ycell["execution_count"] = reply_content.get("execution_count") ycell["execution_state"] = "idle" - if metadata["record_timing"]: + if metadata and metadata.get("record_timing", False): end_time = datetime.now(timezone.utc).isoformat()[:-6] if reply_content["status"] == "ok": time_info["shell.execute_reply"] = end_time diff --git a/jupyter_server_nbmodel/tests/test_handlers.py b/jupyter_server_nbmodel/tests/test_handlers.py index 0728116..b60d6ce 100644 --- a/jupyter_server_nbmodel/tests/test_handlers.py +++ b/jupyter_server_nbmodel/tests/test_handlers.py @@ -149,8 +149,9 @@ async def test_post_erroneous_execute(jp_fetch, pending_kernel_is_ready, snippet await asyncio.sleep(1) + @pytest.mark.timeout(TEST_TIMEOUT) -async def test_execution_timing_metadata(jp_fetch, pending_kernel_is_ready, rtc_create_notebook, jp_serverapp): +async def test_execution_timing_metadata(jp_root_dir, jp_fetch, pending_kernel_is_ready, rtc_create_notebook, jp_serverapp): snippet = "a = 1" nb = nbformat.v4.new_notebook( cells=[nbformat.v4.new_code_cell(source=snippet, execution_count=1)] @@ -159,12 +160,8 @@ async def test_execution_timing_metadata(jp_fetch, pending_kernel_is_ready, rtc_ path, _ = await rtc_create_notebook("test.ipynb", nb_content, store=True) collaboration = jp_serverapp.web_app.settings["jupyter_server_ydoc"] fim = jp_serverapp.web_app.settings["file_id_manager"] - document = await collaboration.get_document( - path=path, content_type="notebook", file_format="json", copy=False - ) - doc = document.get() document_id = f'json:notebook:{fim.get_id("test.ipynb")}' - cell_id = doc["cells"][0].get("id") + cell_id = nb["cells"][0].get("id") r = await jp_fetch( "api", "kernels", method="POST", body=json.dumps({"name": NATIVE_KERNEL_NAME}) @@ -179,9 +176,20 @@ async def test_execution_timing_metadata(jp_fetch, pending_kernel_is_ready, rtc_ kernel["id"], "execute", method="POST", - body=json.dumps({"code": snippet, "metadata":{"cell_id":cell_id,"document_id":document_id,"record_timing":True}}), + body=json.dumps({ + "code": snippet, + "metadata": { + "cell_id": cell_id, + "document_id": document_id, + "record_timing": True + } + }), ) assert response.code == 200 + + document = await collaboration.get_document( + path=path, content_type="notebook", file_format="json", copy=False + ) cell_data = document.get()["cells"][0] assert 'execution' in cell_data['metadata'], "'execution' does not exist in 'metadata'" @@ -202,6 +210,7 @@ async def test_execution_timing_metadata(jp_fetch, pending_kernel_is_ready, rtc_ assert response2.code == 204 await asyncio.sleep(1) + @pytest.mark.timeout(TEST_TIMEOUT) async def test_post_input_execute(jp_fetch, pending_kernel_is_ready): # Start the first kernel diff --git a/pyproject.toml b/pyproject.toml index b0920be..d68f908 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,10 +81,14 @@ build_dir = "jupyter_server_nbmodel/labextension" [tool.pytest.ini_options] filterwarnings = [ "error", + "ignore:Unclosed context