Skip to content

Commit f4180f5

Browse files
Frédéric Collonvalfcollonvalgithub-actions[bot]
authored
Add first implementation for list and get variables (#12)
* Fix linter issue * Add first iteration for list and get variables * Please pre-commit * Test forcing mimetype * Automatic application of license header --------- Co-authored-by: Frédéric Collonval <[email protected]> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 7c6cd38 commit f4180f5

File tree

15 files changed

+357
-28
lines changed

15 files changed

+357
-28
lines changed

.github/workflows/fix-license-header.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
concurrency:
77
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
88
cancel-in-progress: true
9-
9+
1010
jobs:
1111
header-license-fix:
1212
runs-on: ubuntu-latest

.licenserc.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ header:
1414
- '**/.*'
1515
- 'LICENSE'
1616

17-
comment: on-failure
17+
comment: on-failure

Makefile

100755100644
File mode changed.

README.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Jupyter Kernel Client allows you to connect via WebSocket and HTTP to Jupyter Ke
1717

1818
To install the library, run the following command.
1919

20-
```bash
20+
```sh
2121
pip install jupyter_kernel_client
2222
```
2323

@@ -31,13 +31,13 @@ pip install jupyter-server ipykernel
3131

3232
### Kernel Client
3333

34-
2. Start a Jupyter Server.
34+
1. Start a Jupyter Server.
3535

3636
```sh
3737
jupyter server --port 8888 --IdentityProvider.token MY_TOKEN
3838
```
3939

40-
3. Launch `python` in a terminal and execute the following snippet (update the server_url and token).
40+
2. Launch `python` in a terminal and execute the following snippet (update the server_url and token).
4141

4242
```py
4343
import os
@@ -77,13 +77,16 @@ pip install jupyter-kernel-client[konsole]
7777
2. Start a Jupyter Server.
7878

7979
```sh
80-
jupyter server --port 8888 --token MY_TOKEN
80+
jupyter server --port 8888 --IdentityProvider.token MY_TOKEN
8181
```
8282

8383
3. Start the konsole and execute code.
8484

85-
```bash
86-
$ jupyter konsole --url http://localhost:8888 --IdentityProvider.token MY_TOKEN
85+
```sh
86+
jupyter konsole --url http://localhost:8888 --token MY_TOKEN
87+
```
88+
89+
```sh
8790
[KonsoleApp] KernelHttpManager created a new kernel: ...
8891
Jupyter Kernel console 0.2.0
8992

@@ -94,22 +97,22 @@ IPython 8.30.0 -- An enhanced Interactive Python. Type '?' for help.
9497
In [1]: print("hello")
9598
hello
9699

97-
In [2]:
100+
In [2]:
98101
```
99102

100103
## Uninstall
101104

102105
To remove the library, execute:
103106

104-
```bash
107+
```sh
105108
pip uninstall jupyter_kernel_client
106109
```
107110

108111
## Contributing
109112

110113
### Development install
111114

112-
```bash
115+
```sh
113116
# Clone the repo to your local environment
114117
# Change directory to the jupyter_kernel_client directory
115118
# Install package in development mode - will automatically enable
@@ -121,19 +124,19 @@ pip install -e ".[konsole,test,lint,typing]"
121124

122125
Install dependencies:
123126

124-
```bash
127+
```sh
125128
pip install -e ".[test]"
126129
```
127130

128131
To run the python tests, use:
129132

130-
```bash
133+
```sh
131134
pytest
132135
```
133136

134137
### Development uninstall
135138

136-
```bash
139+
```sh
137140
pip uninstall jupyter_kernel_client
138141
```
139142

jupyter_kernel_client/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
from .client import KernelClient
88
from .konsoleapp import KonsoleApp
99
from .manager import KernelHttpManager
10+
from .snippets import SNIPPETS_REGISTRY, LanguageSnippets
1011
from .wsclient import KernelWebSocketClient
1112

12-
1313
__all__ = [
14+
"SNIPPETS_REGISTRY",
1415
"KernelClient",
1516
"KernelHttpManager",
1617
"KernelWebSocketClient",
1718
"KonsoleApp",
19+
"LanguageSnippets",
1820
]

jupyter_kernel_client/__version__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@
44

55
"""Jupyter Kernel Client through websocket."""
66

7-
87
__version__ = "0.3.1"

jupyter_kernel_client/client.py

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from .constants import REQUEST_TIMEOUT
1616
from .manager import KernelHttpManager
17+
from .snippets import SNIPPETS_REGISTRY
1718
from .utils import UTC
1819

1920
logger = logging.getLogger("jupyter_kernel_client")
@@ -149,7 +150,7 @@ class KernelClient(LoggingConfigurable):
149150
Client user name; default to environment variable USER
150151
kernel_id: str | None
151152
ID of the kernel to connect to
152-
"""
153+
""" # noqa E501
153154

154155
kernel_manager_class = Type(
155156
default_value=KernelHttpManager,
@@ -187,6 +188,18 @@ def id(self) -> str | None:
187188
"""Kernel ID"""
188189
return self._manager.kernel["id"] if self._manager.kernel else None
189190

191+
@property
192+
def kernel_info(self) -> dict[str, t.Any] | None:
193+
"""Kernel information.
194+
195+
This is the dictionary returned by the kernel for a kernel_info_request.
196+
197+
Returns:
198+
The kernel information
199+
"""
200+
if self._manager.kernel:
201+
return self._manager.client.kernel_info_interactive(timeout=REQUEST_TIMEOUT)
202+
190203
@property
191204
def last_activity(self) -> datetime.datetime | None:
192205
"""Kernel process last activity.
@@ -337,7 +350,7 @@ def restart(self, timeout: float = REQUEST_TIMEOUT) -> None:
337350
"""Restarts a kernel."""
338351
return self._manager.restart_kernel(timeout=timeout)
339352

340-
def __enter__(self) -> "KernelClient":
353+
def __enter__(self) -> KernelClient:
341354
self.start()
342355
return self
343356

@@ -383,3 +396,91 @@ def stop(
383396
shutdown = self._own_kernel if shutdown_kernel is None else shutdown_kernel
384397
if shutdown:
385398
self._manager.shutdown_kernel(now=shutdown_now, timeout=timeout)
399+
400+
#
401+
# Variables related methods
402+
#
403+
def get_variable(self, name: str, mimetype: str | None = None) -> dict[str, t.Any]:
404+
"""Get a kernel variable.
405+
406+
Args:
407+
name: Variable name
408+
mimetype: optional, type of variable value serialization; default ``None``,
409+
i.e. returns all known serialization.
410+
411+
Returns:
412+
A dictionary for which keys are mimetype and values the variable value
413+
serialized in that mimetype.
414+
Even if a mimetype is specified, the dictionary may not contain it if
415+
the kernel introspection failed to get the variable in the specified format.
416+
Raises:
417+
ValueError: If the kernel programming language is not supported
418+
RuntimeError: If the kernel introspection failed
419+
"""
420+
kernel_language = (self.kernel_info or {}).get("language_info", {}).get("name")
421+
if kernel_language not in SNIPPETS_REGISTRY.available_languages:
422+
raise ValueError(f"""Code snippet for language {kernel_language} are not available.
423+
You can set them yourself using:
424+
425+
from jupyter_kernel_client import SNIPPETS_REGISTRY, LanguageSnippets
426+
SNIPPETS_REGISTRY.register("my-language", LanguageSnippets(list_variables="", get_variable=""))
427+
""")
428+
429+
snippet = SNIPPETS_REGISTRY.get_get_variable(kernel_language)
430+
results = self.execute(snippet.format(name=name, mimetype=mimetype), silent=True)
431+
432+
self.log.debug("Kernel variables: %s", results)
433+
434+
if results["status"] == "ok" and results["outputs"]:
435+
if mimetype is None:
436+
return results["outputs"][0]["data"]
437+
else:
438+
has_mimetype = mimetype in results["outputs"][0]["data"]
439+
return {mimetype: results["outputs"][0]["data"][mimetype]} if has_mimetype else {}
440+
else:
441+
raise RuntimeError(f"Failed to get variable {name} with type {mimetype}.")
442+
443+
def list_variables(self) -> list[dict[str, t.Any]]:
444+
"""List the kernel global variables.
445+
446+
A variable is defined by a dictionary with the schema:
447+
{
448+
"type": "object",
449+
"properties": {
450+
"name": {"title": "Variable name", "type": "string"},
451+
"type": {"title": "Variable type", "type": "string"},
452+
"size": {"title": "Variable size in bytes.", "type": "number"}
453+
},
454+
"required": ["name", "type"]
455+
}
456+
457+
Returns:
458+
The list of global variables.
459+
Raises:
460+
ValueError: If the kernel programming language is not supported
461+
RuntimeError: If the kernel introspection failed
462+
"""
463+
kernel_language = (self.kernel_info or {}).get("language_info", {}).get("name")
464+
if kernel_language not in SNIPPETS_REGISTRY.available_languages:
465+
raise ValueError(f"""Code snippet for language {kernel_language} are not available.
466+
You can set them yourself using:
467+
468+
from jupyter_kernel_client import SNIPPETS_REGISTRY, LanguageSnippets
469+
SNIPPETS_REGISTRY.register("my-language", LanguageSnippets(list_variables="", get_variable=""))
470+
""")
471+
472+
snippet = SNIPPETS_REGISTRY.get_list_variables(kernel_language)
473+
results = self.execute(snippet, silent=True)
474+
475+
self.log.debug("Kernel variables: %s", results)
476+
477+
if (
478+
results["status"] == "ok"
479+
and results["outputs"]
480+
and "application/json" in results["outputs"][0]["data"]
481+
):
482+
return sorted(
483+
results["outputs"][0]["data"]["application/json"], key=lambda v: v["name"]
484+
)
485+
else:
486+
raise RuntimeError("Failed to list variables.")

jupyter_kernel_client/konsoleapp.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from .manager import KernelHttpManager
1717
from .shell import WSTerminalInteractiveShell
1818

19-
2019
# -----------------------------------------------------------------------------
2120
# Globals
2221
# -----------------------------------------------------------------------------
@@ -27,7 +26,7 @@
2726
2827
# Start a console connected to a distant Jupyter Server with a new python kernel.
2928
jupyter konsole --url https://my.jupyter-server.xzy --token <server_token>
30-
"""
29+
""" # noqa E501
3130

3231
# -----------------------------------------------------------------------------
3332
# Flags and Aliases
@@ -102,7 +101,7 @@ class KonsoleApp(JupyterApp):
102101
"""
103102
examples = _examples
104103

105-
classes = [WSTerminalInteractiveShell]
104+
classes = [WSTerminalInteractiveShell] # noqa RUF012
106105
flags = Dict(flags)
107106
aliases = Dict(aliases)
108107

jupyter_kernel_client/shell.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ async def handle_external_iopub(self, loop=None):
3434
await asyncio.sleep(0.5)
3535

3636
def show_banner(self):
37-
print(
37+
print( # noqa T201
3838
self.banner.format(
3939
version=__version__, kernel_banner=self.kernel_info.get("banner", "")
4040
),
@@ -57,7 +57,7 @@ def __init__(self):
5757
self._executing = False
5858

5959
def show_banner(self):
60-
return "You must install `jupyter_console` to use the console:\n\n\tpip install jupyter-console\n"
60+
return "You must install `jupyter_console` to use the console:\n\n\tpip install jupyter-console\n" # noqa E501
6161

6262
def mainloop(self) -> None:
6363
raise ModuleNotFoundError("jupyter_console")

0 commit comments

Comments
 (0)