Skip to content

Commit cc9391d

Browse files
committed
move install_on_import to pyodide, add package.json schema for pyodideKernel
1 parent 4b239b0 commit cc9391d

20 files changed

+652
-518
lines changed

jupyterlite_pyodide_kernel/addons/_base.py

+83-64
Original file line numberDiff line numberDiff line change
@@ -5,90 +5,109 @@
55
66
https://github.com/jupyterlite/jupyterlite/issues/996
77
"""
8-
import json
98
from pathlib import Path
10-
from typing import Generator, Dict, Any
9+
import json
10+
from hashlib import sha256
11+
from typing import Dict, Any, List, Optional
1112
from jupyterlite_core.addons.base import BaseAddon
12-
from jupyterlite_core.constants import (
13-
JUPYTERLITE_IPYNB,
14-
JUPYTERLITE_JSON,
15-
UTF8,
16-
JUPYTERLITE_METADATA,
17-
JUPYTER_CONFIG_DATA,
18-
LITE_PLUGIN_SETTINGS,
19-
JSON_FMT,
13+
from jupyterlite_core.constants import LAB_EXTENSIONS, UTF8
14+
15+
from ..constants import (
16+
PKG_JSON_PYODIDE_KERNEL,
17+
PKG_JSON_WHEELDIR,
18+
PYODIDE_KERNEL_PLUGIN_ID,
19+
PYODIDE_KERNEL_NPM_NAME,
20+
PYPI_WHEELS,
2021
)
2122

22-
from ..constants import PYODIDE_KERNEL_PLUGIN_ID
23-
2423
__all__ = ["_BaseAddon"]
2524

2625

2726
class _BaseAddon(BaseAddon):
28-
def get_pyodide_settings(self, config_path: Path):
29-
"""Get the settings for the client-side Pyodide kernel."""
30-
return self.get_lite_plugin_settings(config_path, PYODIDE_KERNEL_PLUGIN_ID)
27+
@property
28+
def output_extensions(self) -> Path:
29+
"""where labextensions will go in the output folder
3130
32-
def set_pyodide_settings(self, config_path: Path, settings: Dict[str, Any]) -> None:
33-
"""Update the settings for the client-side Pyodide kernel."""
34-
return self.set_lite_plugin_settings(
35-
config_path, PYODIDE_KERNEL_PLUGIN_ID, settings
36-
)
31+
Candidate for hoisting to ``jupyterlite_core``
32+
"""
33+
return self.manager.output_dir / LAB_EXTENSIONS
3734

38-
def get_output_config_paths(self) -> Generator[Path, None, None]:
39-
"""Yield an iterator of all config paths that _might_ exist in the
40-
``output_dir``.
35+
def get_output_labextension_packages(self) -> List[Path]:
36+
"""All ``package.json`` files for labextensions in ``output_dir``.
4137
42-
This will likely move upstream.
38+
Candidate for hoisting to ``jupyterlite_core``
4339
"""
44-
for app in [None, *self.manager.apps]:
45-
app_dir = self.manager.output_dir / app if app else self.manager.output_dir
46-
for path_name in [JUPYTERLITE_JSON, JUPYTERLITE_IPYNB]:
47-
config_path = app_dir / path_name
48-
yield config_path
40+
return sorted(
41+
[
42+
*self.output_extensions.glob("*/package.json"),
43+
*self.output_extensions.glob("@*/*/package.json"),
44+
]
45+
)
4946

50-
def get_lite_plugin_settings(
51-
self, config_path: Path, plugin_id: str
52-
) -> Dict[str, Any]:
53-
"""Get the plugin settings from a config path.
47+
def check_index_urls(self, raw_urls: List[str], schema: Path):
48+
"""Validate URLs against a schema."""
49+
for raw_url in raw_urls:
50+
if not raw_url.startswith("./"):
51+
continue
5452

55-
The keys follow the JupyterLab settings naming convention, of module and
56-
identifier e.g.
53+
index_url = raw_url.split("?")[0].split("#")[0]
5754

58-
@jupyterlite/contents:plugin
55+
index_path = self.manager.output_dir / index_url
5956

60-
This will likely move upstream.
61-
"""
62-
if not config_path.exists():
63-
return {}
57+
if not index_path.exists():
58+
continue
6459

65-
config = json.loads(config_path.read_text(**UTF8))
60+
yield self.task(
61+
name=f"validate:{index_url}",
62+
doc=f"validate {index_url} against {schema}",
63+
file_dep=[index_path],
64+
actions=[(self.validate_one_json_file, [schema, index_path])],
65+
)
6666

67-
# if a notebook, look in the top-level metadata (which must exist)
68-
if config_path.name == JUPYTERLITE_IPYNB:
69-
config = config["metadata"].get(JUPYTERLITE_METADATA, {})
67+
@property
68+
def output_kernel_extension(self) -> Path:
69+
"""the location of the Pyodide kernel labextension static assets"""
70+
return self.output_extensions / PYODIDE_KERNEL_NPM_NAME
7071

71-
return (
72-
config.get(JUPYTER_CONFIG_DATA, {})
73-
.get(LITE_PLUGIN_SETTINGS, {})
74-
.get(plugin_id, {})
75-
)
72+
@property
73+
def schemas(self) -> Path:
74+
"""the path to the as-deployed schema in the labextension"""
75+
return self.output_kernel_extension / "static/schema"
7676

77-
def set_lite_plugin_settings(
78-
self, config_path: Path, plugin_id: str, settings: Dict[str, Any]
79-
) -> None:
80-
"""Overwrite the plugin settings for a single plugin in a config path.
77+
@property
78+
def output_wheels(self) -> Path:
79+
"""where wheels will go in the output folder"""
80+
return self.manager.output_dir / PYPI_WHEELS
8181

82-
This will likely move upstream.
83-
"""
84-
whole_file = config = json.loads(config_path.read_text(**UTF8))
85-
if config_path.name == JUPYTERLITE_IPYNB:
86-
config = whole_file["metadata"][JUPYTERLITE_METADATA]
82+
@property
83+
def wheel_cache(self) -> Path:
84+
"""where wheels will go in the cache folder"""
85+
return self.manager.cache_dir / "wheels"
86+
87+
def get_pyodide_settings(self, config_path: Path):
88+
"""Get the settings for the client-side Pyodide kernel."""
89+
return self.get_lite_plugin_settings(config_path, PYODIDE_KERNEL_PLUGIN_ID)
8790

88-
config.setdefault(JUPYTER_CONFIG_DATA, {}).setdefault(
89-
LITE_PLUGIN_SETTINGS, {}
90-
).update({plugin_id: settings})
91+
def set_pyodide_settings(self, config_path: Path, settings: Dict[str, Any]) -> None:
92+
"""Update the settings for the client-side Pyodide kernel."""
93+
return self.set_lite_plugin_settings(
94+
config_path, PYODIDE_KERNEL_PLUGIN_ID, settings
95+
)
9196

92-
config_path.write_text(json.dumps(whole_file, **JSON_FMT), **UTF8)
93-
self.log.debug("%s wrote settings in %s: %s", plugin_id, config_path, settings)
94-
self.maybe_timestamp(config_path)
97+
def get_index_urls(self, index_path: Path):
98+
"""Get output_dir relative URLs for an index file."""
99+
index_sha256 = sha256(index_path.read_bytes()).hexdigest()
100+
index_url = f"./{index_path.relative_to(self.manager.output_dir).as_posix()}"
101+
index_url_with_sha = f"{index_url}?sha256={index_sha256}"
102+
return index_url, index_url_with_sha
103+
104+
def get_package_wheel_index_url(
105+
self, pkg_json: Path, index_name: str
106+
) -> Optional[Path]:
107+
pkg_data = json.loads(pkg_json.read_text(**UTF8))
108+
wheel_dir = pkg_data.get(PKG_JSON_PYODIDE_KERNEL, {}).get(PKG_JSON_WHEELDIR)
109+
if wheel_dir:
110+
pkg_whl_index = pkg_json.parent / wheel_dir / index_name
111+
if pkg_whl_index.exists():
112+
return self.get_index_urls(pkg_whl_index)[1]
113+
return None

0 commit comments

Comments
 (0)