Skip to content

Commit bd3b38c

Browse files
authored
Merge pull request #9 from datalayer/enh/multiple
Multiple enhancements
2 parents 77cdb6f + a6ecb77 commit bd3b38c

File tree

9 files changed

+599
-72
lines changed

9 files changed

+599
-72
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,4 @@ dmypy.json
101101
# OSX files
102102
.DS_Store
103103
.jupyter_ystore.db
104+
.vscode

README.md

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,83 @@ that the server extension is enabled:
3434
jupyter server extension list
3535
```
3636

37+
## How does it works
38+
39+
### Generic case
40+
41+
Execution of a Python code snippet: `print("hello")`
42+
43+
```mermaid
44+
sequenceDiagram
45+
Frontend->>+Server: POST /api/kernels/<id>/execute
46+
Server->>+ExecutionStack: Create asyncio.Task
47+
ExecutionStack->>Kernel: Execute request msg
48+
activate Kernel
49+
ExecutionStack-->>Server: Task uid
50+
Server-->>-Frontend: Returns task uid
51+
loop Running
52+
Kernel->>Shared Document: Add output
53+
Shared Document->>Frontend: Document update
54+
end
55+
loop While status is 202
56+
Frontend->>+Server: GET /api/kernels/<id>/requests/<uid>
57+
Server->>ExecutionStack: Get task result
58+
ExecutionStack-->>Server: null
59+
Server-->>-Frontend: Request status 202
60+
end
61+
Kernel-->>ExecutionStack: Execution reply
62+
deactivate Kernel
63+
Frontend->>+Server: GET /api/kernels/<id>/requests/<uid>
64+
Server->>ExecutionStack: Get task result
65+
ExecutionStack-->>Server: Result
66+
Server-->>-Frontend: Status 200 & result
67+
```
68+
69+
### With input case
70+
71+
72+
Execution of a Python code snippet: `input("Age:")`
73+
74+
```mermaid
75+
sequenceDiagram
76+
Frontend->>+Server: POST /api/kernels/<id>/execute
77+
Server->>+ExecutionStack: Create asyncio.Task
78+
ExecutionStack->>Kernel: Execute request msg
79+
activate Kernel
80+
ExecutionStack-->>Server: Task uid
81+
Server-->>-Frontend: Returns task uid
82+
loop Running
83+
Kernel->>Shared Document: Add output
84+
Shared Document->>Frontend: Document update
85+
end
86+
loop While status is 202
87+
Frontend->>+Server: GET /api/kernels/<id>/requests/<uid>
88+
Server->>ExecutionStack: Get task result
89+
ExecutionStack-->>Server: null
90+
Server-->>-Frontend: Request status 202
91+
end
92+
Kernel->>ExecutionStack: Set pending input
93+
Frontend->>+Server: GET /api/kernels/<id>/requests/<uid>
94+
Server->>ExecutionStack: Get task result
95+
ExecutionStack-->>Server: Pending input
96+
Server-->>-Frontend: Status 300 & Pending input
97+
Frontend->>+Server: POST /api/kernels/<id>/input
98+
Server->>Kernel: Send input msg
99+
Server-->>-Frontend:
100+
loop While status is 202
101+
Frontend->>+Server: GET /api/kernels/<id>/requests/<uid>
102+
Server->>ExecutionStack: Get task result
103+
ExecutionStack-->>Server: null
104+
Server-->>-Frontend: Request status 202
105+
end
106+
Kernel-->>ExecutionStack: Execution reply
107+
deactivate Kernel
108+
Frontend->>+Server: GET /api/kernels/<id>/requests/<uid>
109+
Server->>ExecutionStack: Get task result
110+
ExecutionStack-->>Server: Result
111+
Server-->>-Frontend: Status 200 & result
112+
```
113+
37114
## Contributing
38115

39116
### Development install
@@ -46,7 +123,6 @@ jupyter server extension list
46123
pip install -e .
47124
```
48125

49-
50126
You can watch the source directory and run your Jupyter Server-based application at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension. For example,
51127
when running JupyterLab:
52128

@@ -61,6 +137,25 @@ server directly:
61137
jupyter server --autoreload
62138
```
63139

140+
### Manual testing
141+
142+
```bash
143+
# Terminal 1.
144+
jupyter server --port 8888 --autoreload --ServerApp.disable_check_xsrf=True --IdentityProvider.token= --ServerApp.port_retries=0
145+
146+
# Terminal 2.
147+
KERNEL=$(curl -X POST http://localhost:8888/api/kernels)
148+
echo $KERNEL
149+
KERNEL_ID=$(echo $KERNEL | jq --raw-output '.id')
150+
echo $KERNEL_ID
151+
REQUEST=$(curl --include http://localhost:8888/api/kernels/$KERNEL_ID/execute -d "{ \"code\": \"print('1+1')\" }")
152+
RESULT=$(echo $REQUEST | grep -i ^Location: | cut -d' ' -f2 | tr -d '\r')
153+
echo $RESULT
154+
155+
curl http://localhost:8888$RESULT
156+
{"status": "ok", "execution_count": 1, "outputs": "[{\"output_type\": \"stream\", \"name\": \"stdout\", \"text\": \"1+1\\n\"}]"}
157+
```
158+
64159
### Running Tests
65160

66161
Install dependencies:
@@ -78,7 +173,7 @@ pytest
78173
pytest jupyter_server_nbmodel/tests/test_handlers.py
79174

80175
# To run a specific test
81-
pytest jupyter_server_nbmodel/tests/test_handlers.py -k "test_get"
176+
pytest jupyter_server_nbmodel/tests/test_handlers.py -k "test_post_execute"
82177
```
83178

84179
### Development uninstall

conftest.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
pytest_plugins = ["jupyter_server.pytest_plugin"]
44

5+
56
@pytest.fixture
67
def jp_server_config(jp_server_config):
7-
return {"ServerApp": {"jpserver_extensions": {"jupyter_server_nbmodel": True, "jupyter_server_ydoc": True}}}
8+
return {
9+
"ServerApp": {
10+
"jpserver_extensions": {"jupyter_server_nbmodel": True, "jupyter_server_ydoc": True}
11+
}
12+
}

jupyter_server_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
c.YDocExtension.server_side_execution = True
1+
c.YDocExtension.server_side_execution = True # noqa F821

jupyter_server_nbmodel/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
"""A Jupyter Server extension to execute code cell from the server."""
2+
23
from .extension import Extension
4+
35
__version__ = "0.1.0"
46

57

68
def _jupyter_server_extension_points():
7-
return [{
8-
"module": "jupyter_server_nbmodel",
9-
"app": Extension
10-
}]
9+
return [{"module": "jupyter_server_nbmodel", "app": Extension}]

jupyter_server_nbmodel/extension.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,51 @@
33
from jupyter_server.extension.application import ExtensionApp
44
from jupyter_server.services.kernels.handlers import _kernel_id_regex
55

6-
from .handlers import ExecuteHandler
6+
from .handlers import ExecuteHandler, ExecutionStack, InputHandler, RequestHandler
77
from .log import get_logger
88

99
RTC_EXTENSIONAPP_NAME = "jupyter_server_ydoc"
1010

11+
STOP_TIMEOUT = 3
12+
13+
_request_id_regex = r"(?P<request_id>\w+-\w+-\w+-\w+-\w+)"
14+
1115

1216
class Extension(ExtensionApp):
1317
name = "jupyter_server_nbmodel"
1418

1519
def initialize_handlers(self):
1620
rtc_extension = None
17-
rtc_extensions = self.serverapp.extension_manager.extension_apps.get(RTC_EXTENSIONAPP_NAME, set())
21+
rtc_extensions = self.serverapp.extension_manager.extension_apps.get(
22+
RTC_EXTENSIONAPP_NAME, set()
23+
)
1824
n_extensions = len(rtc_extensions)
1925
if n_extensions:
2026
if n_extensions > 1:
2127
get_logger().warning("%i collaboration extensions found.", n_extensions)
2228
rtc_extension = next(iter(rtc_extensions))
23-
self.handlers.extend([
24-
(f"/api/kernels/{_kernel_id_regex}/execute", ExecuteHandler, { "ydoc_extension": rtc_extension })
25-
])
29+
30+
self.__tasks = ExecutionStack()
31+
32+
self.handlers.extend(
33+
[
34+
(
35+
f"/api/kernels/{_kernel_id_regex}/execute",
36+
ExecuteHandler,
37+
{"ydoc_extension": rtc_extension, "execution_stack": self.__tasks},
38+
),
39+
(
40+
f"/api/kernels/{_kernel_id_regex}/input",
41+
InputHandler,
42+
),
43+
(
44+
f"/api/kernels/{_kernel_id_regex}/requests/{_request_id_regex}",
45+
RequestHandler,
46+
{"execution_stack": self.__tasks},
47+
),
48+
]
49+
)
50+
51+
async def stop_extension(self):
52+
if hasattr(self, "__tasks"):
53+
del self.__tasks

0 commit comments

Comments
 (0)