jupyterpack 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +2 -0
  2. package/lib/document/widgetFactory.js +4 -1
  3. package/lib/pythonServer/common/generatedPythonFiles.d.ts +2 -0
  4. package/lib/pythonServer/common/generatedPythonFiles.js +72 -0
  5. package/lib/pythonServer/dash/dashServer.d.ts +24 -0
  6. package/lib/pythonServer/dash/dashServer.js +39 -0
  7. package/lib/pythonServer/dash/generatedPythonFiles.d.ts +2 -0
  8. package/lib/pythonServer/dash/generatedPythonFiles.js +31 -0
  9. package/lib/pythonServer/index.d.ts +5 -0
  10. package/lib/pythonServer/index.js +9 -0
  11. package/lib/pythonServer/kernelExecutor.d.ts +66 -0
  12. package/lib/pythonServer/kernelExecutor.js +133 -0
  13. package/lib/pythonServer/streamlit/generatedPythonFiles.d.ts +2 -0
  14. package/lib/pythonServer/streamlit/generatedPythonFiles.js +147 -0
  15. package/lib/pythonServer/streamlit/streamlitServer.d.ts +33 -0
  16. package/lib/pythonServer/streamlit/streamlitServer.js +55 -0
  17. package/lib/pythonServer/tornado/generatedPythonFiles.d.ts +3 -0
  18. package/lib/pythonServer/tornado/generatedPythonFiles.js +456 -0
  19. package/lib/pythonServer/tornado/tornadoServer.d.ts +32 -0
  20. package/lib/pythonServer/tornado/tornadoServer.js +51 -0
  21. package/lib/pythonWidget/pythonWidget.d.ts +1 -0
  22. package/lib/pythonWidget/pythonWidget.js +9 -3
  23. package/lib/pythonWidget/pythonWidgetModel.d.ts +12 -3
  24. package/lib/pythonWidget/pythonWidgetModel.js +32 -10
  25. package/lib/swConnection/index.js +2 -2
  26. package/lib/{pythonWidget/connectionManager.d.ts → swConnection/mainConnectionManager.d.ts} +10 -0
  27. package/lib/swConnection/mainConnectionManager.js +93 -0
  28. package/lib/swConnection/sw.js +1 -1
  29. package/lib/swConnection/swCommManager.d.ts +11 -0
  30. package/lib/swConnection/{comm_manager.js → swCommManager.js} +5 -0
  31. package/lib/tools.d.ts +4 -0
  32. package/lib/tools.js +58 -0
  33. package/lib/type.d.ts +37 -2
  34. package/lib/type.js +2 -0
  35. package/lib/websocket/websocket.d.ts +0 -0
  36. package/lib/websocket/websocket.js +152 -0
  37. package/package.json +8 -5
  38. package/src/document/widgetFactory.ts +4 -1
  39. package/src/global.d.ts +4 -0
  40. package/src/pythonServer/common/generatedPythonFiles.ts +73 -0
  41. package/src/pythonServer/dash/dashServer.ts +57 -0
  42. package/src/pythonServer/dash/generatedPythonFiles.ts +32 -0
  43. package/src/pythonServer/index.ts +18 -0
  44. package/src/pythonServer/kernelExecutor.ts +229 -0
  45. package/src/pythonServer/streamlit/generatedPythonFiles.ts +148 -0
  46. package/src/pythonServer/streamlit/streamlitServer.ts +87 -0
  47. package/src/pythonServer/tornado/generatedPythonFiles.ts +457 -0
  48. package/src/pythonServer/tornado/tornadoServer.ts +80 -0
  49. package/src/pythonWidget/pythonWidget.ts +20 -3
  50. package/src/pythonWidget/pythonWidgetModel.ts +53 -19
  51. package/src/swConnection/index.ts +5 -2
  52. package/src/swConnection/mainConnectionManager.ts +121 -0
  53. package/src/swConnection/sw.ts +1 -1
  54. package/src/swConnection/{comm_manager.ts → swCommManager.ts} +6 -0
  55. package/src/tools.ts +69 -0
  56. package/src/type.ts +47 -3
  57. package/src/websocket/websocket.ts +216 -0
  58. package/lib/pythonWidget/connectionManager.js +0 -27
  59. package/lib/pythonWidget/kernelExecutor.d.ts +0 -27
  60. package/lib/pythonWidget/kernelExecutor.js +0 -104
  61. package/lib/swConnection/comm_manager.d.ts +0 -6
  62. package/lib/swConnection/connection_manager.d.ts +0 -18
  63. package/lib/swConnection/connection_manager.js +0 -27
  64. package/src/pythonWidget/connectionManager.ts +0 -43
  65. package/src/pythonWidget/kernelExecutor.ts +0 -140
  66. package/src/swConnection/connection_manager.ts +0 -43
@@ -0,0 +1,73 @@
1
+ // Auto-generated TypeScript file from Python files
2
+
3
+ export const patch = `
4
+ import pyodide_http
5
+ import collections
6
+
7
+ if not hasattr(collections, "MutableSet"):
8
+ import collections.abc
9
+
10
+ collections.MutableSet = collections.abc.MutableSet
11
+
12
+ pyodide_http.patch_all()`;
13
+ export const tools = `
14
+ import importlib.util
15
+ import sys
16
+ from types import ModuleType
17
+
18
+ import tempfile
19
+ from pathlib import Path
20
+ from typing import List
21
+ import os
22
+
23
+ os.environ.setdefault("JUPYTERPACK_BASE_URL", "{{base_url}}")
24
+
25
+
26
+ def __jupyterpack_import_from_path(module_name: str, path: str) -> ModuleType:
27
+ """
28
+ Import a Python module from a given file path.
29
+ Always reloads (does not use sys.modules cache).
30
+ """
31
+ # Remove from sys.modules if already loaded
32
+ if module_name in sys.modules:
33
+ del sys.modules[module_name]
34
+
35
+ spec = importlib.util.spec_from_file_location(module_name, path)
36
+ if spec is None or spec.loader is None:
37
+ raise ImportError(f"Cannot import module {module_name} from {path}")
38
+
39
+ module = importlib.util.module_from_spec(spec)
40
+ sys.modules[module_name] = module
41
+ spec.loader.exec_module(module)
42
+ return module
43
+
44
+
45
+ def __jupyterpack_create_mock_module(
46
+ module_names: List[str], mock_content: str, patch_parent=True
47
+ ):
48
+ tmpdir = tempfile.TemporaryDirectory()
49
+ package_dir = Path(tmpdir.name) / "__jupyterpack_mock_module"
50
+ package_dir.mkdir()
51
+ (package_dir / "__init__.py").write_text(mock_content)
52
+
53
+ sys.path.insert(0, tmpdir.name)
54
+ mock_module = importlib.import_module("__jupyterpack_mock_module")
55
+ sys.path.pop(0)
56
+ for module_name in module_names:
57
+ if patch_parent:
58
+ parts = module_name.split(".")
59
+ for i in range(1, len(parts) + 1):
60
+ subpath = ".".join(parts[:i])
61
+ sys.modules[subpath] = mock_module
62
+
63
+ for i in range(1, len(parts)):
64
+ parent_name = ".".join(parts[:i])
65
+ child_name = ".".join(parts[: i + 1])
66
+ parent_mod = sys.modules[parent_name]
67
+ child_mod = sys.modules[child_name]
68
+ setattr(parent_mod, parts[i], child_mod)
69
+ else:
70
+ sys.modules[module_name] = mock_module
71
+
72
+ return tmpdir
73
+ `;
@@ -0,0 +1,57 @@
1
+ import { stringOrNone } from '../../tools';
2
+ import { IDict, JupyterPackFramework } from '../../type';
3
+ import { patch } from '../common/generatedPythonFiles';
4
+ import { KernelExecutor } from '../kernelExecutor';
5
+ import { bootstrap, dashLoader } from './generatedPythonFiles';
6
+
7
+ export class DashServer extends KernelExecutor {
8
+ async init(options: {
9
+ initCode?: string;
10
+ instanceId: string;
11
+ kernelClientId: string;
12
+ }) {
13
+ await super.init(options);
14
+ const { initCode, instanceId, kernelClientId } = options;
15
+
16
+ const baseURL = this.buildBaseURL({
17
+ instanceId,
18
+ kernelClientId,
19
+ framework: JupyterPackFramework.DASH
20
+ });
21
+ await this.executeCode({ code: patch });
22
+ await this.executeCode({
23
+ code: bootstrap.replaceAll('{{base_url}}', baseURL)
24
+ });
25
+ if (initCode) {
26
+ await this.executeCode({ code: initCode });
27
+ }
28
+ await this.executeCode({ code: dashLoader });
29
+ }
30
+
31
+ getResponseFunctionFactory(options: {
32
+ urlPath: string;
33
+ method: string;
34
+ headers: IDict;
35
+ params?: string;
36
+ content?: string;
37
+ }) {
38
+ const { method, urlPath, headers, params, content } = options;
39
+ const code = `${this.DASH_GET_RESPONSE_FUNCTION}("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
40
+ return code;
41
+ }
42
+
43
+ async disposePythonServer(): Promise<void> {
44
+ //no-op
45
+ }
46
+
47
+ async openWebsocket(options: {
48
+ instanceId: string;
49
+ kernelId: string;
50
+ wsUrl: string;
51
+ protocol?: string;
52
+ }): Promise<void> {
53
+ //no-op
54
+ }
55
+
56
+ private DASH_GET_RESPONSE_FUNCTION = '__jupyterpack_dash_get_response';
57
+ }
@@ -0,0 +1,32 @@
1
+ // Auto-generated TypeScript file from Python files
2
+
3
+ export const bootstrap = `
4
+ import os
5
+
6
+ os.environ["DASH_URL_BASE_PATHNAME"] = "{{base_url}}"
7
+ `;
8
+ export const dashLoader = `
9
+ import httpx, json, base64
10
+ __jupyterpack_dash_transport = httpx.WSGITransport(app=app.server) # noqa
11
+
12
+
13
+ def __jupyterpack_dash_get_response(method, url, headers, content=None, params=None):
14
+ decoded_content = None
15
+ if content is not None:
16
+ decoded_content = base64.b64decode(content)
17
+ # decoded_content = content.decode()
18
+ with httpx.Client(
19
+ transport=__jupyterpack_dash_transport, base_url="http://testserver"
20
+ ) as client:
21
+ r = client.request(
22
+ method, url, headers=headers, content=decoded_content, params=params
23
+ )
24
+ reply_headers = json.dumps(dict(r.headers)).encode("utf-8")
25
+ response = {
26
+ "headers": base64.b64encode(reply_headers).decode("ascii"),
27
+ "content": base64.b64encode(r.content).decode("ascii"),
28
+ "status_code": r.status_code,
29
+ }
30
+ json_str = json.dumps(response)
31
+ return json_str
32
+ `;
@@ -0,0 +1,18 @@
1
+ import { IKernelExecutor, JupyterPackFramework } from '../type';
2
+ import { DashServer } from './dash/dashServer';
3
+ import { KernelExecutor } from './kernelExecutor';
4
+ import { StreamlitServer } from './streamlit/streamlitServer';
5
+ import { TornadoServer } from './tornado/tornadoServer';
6
+
7
+ type KernelExecutorConstructor = new (
8
+ options: KernelExecutor.IOptions
9
+ ) => IKernelExecutor;
10
+
11
+ export const PYTHON_SERVER = new Map<
12
+ JupyterPackFramework,
13
+ KernelExecutorConstructor
14
+ >([
15
+ [JupyterPackFramework.DASH, DashServer],
16
+ [JupyterPackFramework.STREAMLIT, StreamlitServer],
17
+ [JupyterPackFramework.TORNADO, TornadoServer]
18
+ ]);
@@ -0,0 +1,229 @@
1
+ import { PageConfig, URLExt } from '@jupyterlab/coreutils';
2
+ import { KernelMessage, Session } from '@jupyterlab/services';
3
+ import stripAnsi from 'strip-ansi';
4
+ import {
5
+ arrayBufferToBase64,
6
+ base64ToArrayBuffer,
7
+ base64ToString,
8
+ isBinaryContentType
9
+ } from '../tools';
10
+ import { IDict, IKernelExecutor, JupyterPackFramework } from '../type';
11
+ import websocketPatch from '../websocket/websocket.js?raw';
12
+ import { patch } from './common/generatedPythonFiles';
13
+
14
+ export abstract class KernelExecutor implements IKernelExecutor {
15
+ constructor(options: KernelExecutor.IOptions) {
16
+ this._sessionConnection = options.sessionConnection;
17
+ this._wsPatch = websocketPatch.replaceAll('"use strict";', '');
18
+ }
19
+
20
+ get isDisposed(): boolean {
21
+ return this._isDisposed;
22
+ }
23
+
24
+ abstract disposePythonServer(): Promise<void>;
25
+
26
+ abstract getResponseFunctionFactory(options: {
27
+ urlPath: string;
28
+ method: string;
29
+ headers: IDict;
30
+ params?: string;
31
+ content?: string;
32
+ }): string;
33
+
34
+ async init(options: {
35
+ entryPath?: string;
36
+ initCode?: string;
37
+ instanceId: string;
38
+ kernelClientId: string;
39
+ }): Promise<void> {
40
+ await this.executeCode({ code: patch });
41
+ }
42
+
43
+ openWebsocketFunctionFactory(options: {
44
+ instanceId: string;
45
+ kernelId: string;
46
+ wsUrl: string;
47
+ protocol?: string;
48
+ }): string | undefined {
49
+ return undefined;
50
+ }
51
+
52
+ sendWebsocketMessageFunctionFactory(options: {
53
+ instanceId: string;
54
+ kernelId: string;
55
+ wsUrl: string;
56
+ message: string;
57
+ }): string | undefined {
58
+ return undefined;
59
+ }
60
+
61
+ async openWebsocket(options: {
62
+ instanceId: string;
63
+ kernelId: string;
64
+ wsUrl: string;
65
+ protocol?: string;
66
+ }): Promise<void> {
67
+ const code = this.openWebsocketFunctionFactory(options);
68
+ if (code) {
69
+ await this.executeCode({ code });
70
+ }
71
+ }
72
+
73
+ async sendWebsocketMessage(options: {
74
+ instanceId: string;
75
+ kernelId: string;
76
+ wsUrl: string;
77
+ message: string;
78
+ }): Promise<void> {
79
+ const code = this.sendWebsocketMessageFunctionFactory(options);
80
+ if (code) {
81
+ await this.executeCode({ code });
82
+ }
83
+ }
84
+
85
+ async getResponse(options: {
86
+ method: string;
87
+ urlPath: string;
88
+ headers: IDict;
89
+ requestBody?: ArrayBuffer;
90
+ params?: string;
91
+ }): Promise<IDict> {
92
+ const { method, urlPath, requestBody, params, headers } = options;
93
+ const content = requestBody ? arrayBufferToBase64(requestBody) : undefined;
94
+ const code = this.getResponseFunctionFactory({
95
+ method,
96
+ urlPath,
97
+ headers,
98
+ params,
99
+ content
100
+ });
101
+ const raw = await this.executeCode({ code }, true);
102
+ if (!raw) {
103
+ throw new Error(`Missing response for ${urlPath}`);
104
+ }
105
+ const jsonStr = raw.replaceAll("'", '');
106
+ const obj: {
107
+ headers: string;
108
+ status_code: number;
109
+ content: string;
110
+ } = JSON.parse(jsonStr);
111
+ const responseHeaders: IDict<string> = JSON.parse(atob(obj.headers));
112
+ const contentType: string | undefined =
113
+ responseHeaders?.['Content-Type'] ?? responseHeaders?.['content-type'];
114
+ let responseContent: string | Uint8Array;
115
+
116
+ if (isBinaryContentType(contentType)) {
117
+ responseContent = base64ToArrayBuffer(obj.content);
118
+ } else {
119
+ responseContent = base64ToString(obj.content);
120
+ }
121
+
122
+ if (contentType && contentType.toLowerCase() === 'text/html') {
123
+ responseContent = (responseContent as string).replace(
124
+ '<head>',
125
+ `<head>\n<script>\n${this._wsPatch}\n</script>\n`
126
+ );
127
+ }
128
+
129
+ const decodedObj = {
130
+ status_code: obj.status_code,
131
+ headers: responseHeaders,
132
+ content: responseContent
133
+ };
134
+
135
+ return decodedObj;
136
+ }
137
+ async executeCode(
138
+ code: KernelMessage.IExecuteRequestMsg['content'],
139
+ waitForResult?: boolean
140
+ ): Promise<string | null> {
141
+ const kernel = this._sessionConnection?.kernel;
142
+ if (!kernel) {
143
+ throw new Error('Session has no kernel.');
144
+ }
145
+ return new Promise<string | null>((resolve, reject) => {
146
+ const future = kernel.requestExecute(code, false, undefined);
147
+ let executeResult = '';
148
+ future.onIOPub = (msg: KernelMessage.IIOPubMessage): void => {
149
+ const msgType = msg.header.msg_type;
150
+
151
+ switch (msgType) {
152
+ case 'execute_result': {
153
+ if (waitForResult) {
154
+ const content = (msg as KernelMessage.IExecuteResultMsg).content
155
+ .data['text/plain'] as string;
156
+ executeResult += content;
157
+ resolve(executeResult);
158
+ }
159
+ break;
160
+ }
161
+ case 'stream': {
162
+ const content = (msg as KernelMessage.IStreamMsg).content;
163
+ if (content.name === 'stderr') {
164
+ console.error('Kernel stream', content.text);
165
+ } else {
166
+ console.log('Kernel stream', content.text);
167
+ }
168
+ break;
169
+ }
170
+ case 'error': {
171
+ console.error(
172
+ 'Kernel operation failed',
173
+ code.code,
174
+ (msg.content as any).traceback
175
+ .map((it: string) => stripAnsi(it))
176
+ .join('\n')
177
+ );
178
+
179
+ reject(msg.content);
180
+ break;
181
+ }
182
+ default:
183
+ break;
184
+ }
185
+ };
186
+ if (!waitForResult) {
187
+ resolve(null);
188
+ // future.dispose() # TODO
189
+ }
190
+ });
191
+ }
192
+
193
+ dispose(): void {
194
+ if (this._isDisposed) {
195
+ return;
196
+ }
197
+ this._isDisposed = true;
198
+ this._sessionConnection.dispose();
199
+ }
200
+
201
+ protected buildBaseURL(options: {
202
+ instanceId: string;
203
+ kernelClientId: string;
204
+ framework: JupyterPackFramework;
205
+ }) {
206
+ const { instanceId, kernelClientId, framework } = options;
207
+ const labBaseUrl = PageConfig.getOption('baseUrl');
208
+ const baseURL = URLExt.join(
209
+ labBaseUrl,
210
+ 'extensions/jupyterpack/static',
211
+ instanceId,
212
+ framework,
213
+ kernelClientId,
214
+ '/'
215
+ );
216
+
217
+ return baseURL;
218
+ }
219
+
220
+ private _isDisposed: boolean = false;
221
+ private _sessionConnection: Session.ISessionConnection;
222
+ private _wsPatch: string;
223
+ }
224
+
225
+ export namespace KernelExecutor {
226
+ export interface IOptions {
227
+ sessionConnection: Session.ISessionConnection;
228
+ }
229
+ }
@@ -0,0 +1,148 @@
1
+ // Auto-generated TypeScript file from Python files
2
+
3
+ export const bootstrap = `
4
+ import os
5
+
6
+ import threading
7
+ import streamlit.watcher.path_watcher
8
+ import contextlib
9
+ import streamlit.elements.spinner
10
+
11
+ os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python"
12
+
13
+
14
+ class MockedThread(threading.Thread):
15
+ def start(self):
16
+ threading.current_thread = lambda: self
17
+ try:
18
+ self.run()
19
+ except Exception as e:
20
+ raise e
21
+
22
+
23
+ threading.Thread = MockedThread
24
+
25
+
26
+ class WatcherMock:
27
+ def __init__(
28
+ self,
29
+ path,
30
+ callback,
31
+ glob_pattern: str | None = None,
32
+ allow_nonexistent: bool = False,
33
+ ) -> None:
34
+ pass
35
+
36
+ def close(self) -> None:
37
+ pass
38
+
39
+
40
+ streamlit.watcher.path_watcher.watchdog_available = False
41
+ streamlit.watcher.path_watcher.EventBasedPathWatcher = WatcherMock
42
+ streamlit.watcher.path_watcher._is_watchdog_available = lambda: False
43
+ streamlit.watcher.path_watcher.get_path_watcher_class = lambda x: WatcherMock
44
+
45
+
46
+ class MockThreading:
47
+ class Timer:
48
+ def __init__(self, delay, cb):
49
+ cb()
50
+
51
+ def start(self):
52
+ pass
53
+
54
+ Lock = contextlib.nullcontext
55
+
56
+
57
+ streamlit.elements.spinner.threading = MockThreading
58
+ `;
59
+ export const streamlitLoader = `
60
+ import json
61
+ from streamlit import config
62
+ import streamlit.web.server.server as st_server
63
+ from streamlit.runtime.runtime import Runtime
64
+
65
+ try:
66
+ # Check if __jupyterpack_streamlit_instance defined from previous run
67
+ __jupyterpack_streamlit_instance
68
+ except NameError:
69
+ __jupyterpack_streamlit_instance = {
70
+ "tornado_bridge": None,
71
+ "streamlit_server": None,
72
+ }
73
+
74
+
75
+ def __jupyterpack_create_streamlit_app(base_url, script_path):
76
+ if Runtime._instance is not None:
77
+ Runtime._instance.stop()
78
+ Runtime._instance = None
79
+ config.set_option("server.baseUrlPath", base_url)
80
+
81
+ config.set_option("server.port", 6789)
82
+ config.set_option("server.enableCORS", False)
83
+ config.set_option("server.enableXsrfProtection", False)
84
+
85
+ streamlit_server = st_server.Server(script_path, True)
86
+ return streamlit_server
87
+
88
+
89
+ def __jupyterpack_streamlit_dispose():
90
+ global __jupyterpack_streamlit_instance
91
+ streamlit_server = __jupyterpack_streamlit_instance.get("streamlit_server", None)
92
+ if streamlit_server:
93
+ streamlit_server._runtime.stop()
94
+
95
+ __jupyterpack_streamlit_instance = {
96
+ "tornado_bridge": None,
97
+ "streamlit_server": None,
98
+ }
99
+ del streamlit_server
100
+
101
+
102
+ async def __jupyterpack_streamlit_open_ws(
103
+ instance_id: str, kernel_client_id: str, ws_url: str, protocols_str: str | None
104
+ ):
105
+ tornado_bridge = __jupyterpack_streamlit_instance["tornado_bridge"]
106
+ if tornado_bridge is None:
107
+ raise Exception("Missing tornado instance")
108
+ await tornado_bridge.open_ws(instance_id, kernel_client_id, ws_url, protocols_str)
109
+
110
+
111
+ async def __jupyterpack_streamlit_receive_ws_message(
112
+ instance_id: str, kernel_client_id: str, ws_url: str, payload_message: str
113
+ ):
114
+ tornado_bridge = __jupyterpack_streamlit_instance["tornado_bridge"]
115
+ if tornado_bridge is None:
116
+ raise Exception("Missing tornado instance")
117
+ await tornado_bridge.receive_ws_message_from_js(
118
+ instance_id, kernel_client_id, ws_url, payload_message
119
+ )
120
+
121
+
122
+ async def __jupyterpack_streamlit_get_response(
123
+ method, url, headers, content=None, params=None
124
+ ):
125
+ global __jupyterpack_streamlit_instance
126
+ if not __jupyterpack_streamlit_instance["streamlit_server"]:
127
+ streamlit_server = __jupyterpack_create_streamlit_app(
128
+ "{{base_url}}", "{{script_path}}"
129
+ ) # noqa
130
+ app = streamlit_server._create_app()
131
+ await streamlit_server._runtime.start()
132
+ __jupyterpack_streamlit_instance["streamlit_server"] = streamlit_server
133
+ __jupyterpack_streamlit_instance["tornado_bridge"] = TornadoBridge(
134
+ app, "{{base_url}}"
135
+ )
136
+
137
+ tornado_bridge = __jupyterpack_streamlit_instance["tornado_bridge"]
138
+ req_dict = {
139
+ "method": method,
140
+ "url": url,
141
+ "headers": list(headers.items()),
142
+ "body": content,
143
+ }
144
+
145
+ response = await tornado_bridge.fetch(req_dict)
146
+ json_str = json.dumps(response)
147
+ return json_str
148
+ `;
@@ -0,0 +1,87 @@
1
+ import { stringOrNone } from '../../tools';
2
+ import { IDict, JupyterPackFramework } from '../../type';
3
+ import { patch, tools } from '../common/generatedPythonFiles';
4
+ import { KernelExecutor } from '../kernelExecutor';
5
+ import {
6
+ bootstrap as tornadoBootstrap,
7
+ tornadoBridge
8
+ } from '../tornado/generatedPythonFiles';
9
+ import { bootstrap, streamlitLoader } from './generatedPythonFiles';
10
+
11
+ export class StreamlitServer extends KernelExecutor {
12
+ async init(options: {
13
+ entryPath?: string;
14
+ initCode?: string;
15
+ instanceId: string;
16
+ kernelClientId: string;
17
+ }) {
18
+ await super.init(options);
19
+ const { instanceId, kernelClientId, entryPath } = options;
20
+ if (!entryPath) {
21
+ throw new Error(
22
+ 'Missing streamlit entry path, please check your SPK file'
23
+ );
24
+ }
25
+ const baseURL = this.buildBaseURL({
26
+ instanceId,
27
+ kernelClientId,
28
+ framework: JupyterPackFramework.STREAMLIT
29
+ });
30
+ await this.executeCode({ code: patch });
31
+ await this.executeCode({ code: tools.replaceAll('{{base_url}}', baseURL) });
32
+ await this.executeCode({ code: tornadoBootstrap });
33
+ await this.executeCode({ code: tornadoBridge });
34
+ await this.executeCode({ code: bootstrap });
35
+
36
+ const stCode = streamlitLoader
37
+ .replaceAll('{{base_url}}', baseURL)
38
+ .replaceAll('{{script_path}}', entryPath);
39
+ await this.executeCode({ code: stCode });
40
+ }
41
+
42
+ getResponseFunctionFactory(options: {
43
+ urlPath: string;
44
+ method: string;
45
+ headers: IDict;
46
+ params?: string;
47
+ content?: string;
48
+ }) {
49
+ const { method, urlPath, headers, params, content } = options;
50
+ const code = `await ${this._GET_RESPONSE_FUNCTION}("${method}", "${urlPath}", headers=${JSON.stringify(headers)} , content=${stringOrNone(content)}, params=${stringOrNone(params)})`;
51
+ return code;
52
+ }
53
+
54
+ openWebsocketFunctionFactory(options: {
55
+ instanceId: string;
56
+ kernelId: string;
57
+ wsUrl: string;
58
+ protocol?: string;
59
+ }): string {
60
+ const { instanceId, kernelId, wsUrl, protocol } = options;
61
+
62
+ const code = `await ${this._OPEN_WEBSOCKET_FUNCTION}("${instanceId}", "${kernelId}", "${wsUrl}", ${stringOrNone(protocol)})`;
63
+ return code;
64
+ }
65
+
66
+ sendWebsocketMessageFunctionFactory(options: {
67
+ instanceId: string;
68
+ kernelId: string;
69
+ wsUrl: string;
70
+ message: string;
71
+ }): string {
72
+ const { instanceId, kernelId, wsUrl, message } = options;
73
+ const code = `await ${this._SEND_WEBSOCKET_FUNCTION}("${instanceId}", "${kernelId}", "${wsUrl}", '''${message}''')`;
74
+ return code;
75
+ }
76
+
77
+ async disposePythonServer(): Promise<void> {
78
+ await this.executeCode({
79
+ code: '__jupyterpack_streamlit_dispose()'
80
+ });
81
+ }
82
+
83
+ private _GET_RESPONSE_FUNCTION = '__jupyterpack_streamlit_get_response';
84
+ private _OPEN_WEBSOCKET_FUNCTION = '__jupyterpack_streamlit_open_ws';
85
+ private _SEND_WEBSOCKET_FUNCTION =
86
+ '__jupyterpack_streamlit_receive_ws_message';
87
+ }