Skip to content

Commit

Permalink
move install_on_import to pyodide, add package.json schema for pyodid…
Browse files Browse the repository at this point in the history
…eKernel
  • Loading branch information
bollwyvl committed Mar 11, 2023
1 parent 4b239b0 commit c370e55
Show file tree
Hide file tree
Showing 20 changed files with 651 additions and 517 deletions.
147 changes: 83 additions & 64 deletions jupyterlite_pyodide_kernel/addons/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,90 +5,109 @@
https://github.com/jupyterlite/jupyterlite/issues/996
"""
import json
from pathlib import Path
from typing import Generator, Dict, Any
import json
from hashlib import sha256
from typing import Dict, Any, List, Optional
from jupyterlite_core.addons.base import BaseAddon
from jupyterlite_core.constants import (
JUPYTERLITE_IPYNB,
JUPYTERLITE_JSON,
UTF8,
JUPYTERLITE_METADATA,
JUPYTER_CONFIG_DATA,
LITE_PLUGIN_SETTINGS,
JSON_FMT,
from jupyterlite_core.constants import LAB_EXTENSIONS, UTF8

from ..constants import (
PKG_JSON_PYODIDE_KERNEL,
PKG_JSON_WHEELDIR,
PYODIDE_KERNEL_PLUGIN_ID,
PYODIDE_KERNEL_NPM_NAME,
PYPI_WHEELS,
)

from ..constants import PYODIDE_KERNEL_PLUGIN_ID

__all__ = ["_BaseAddon"]


class _BaseAddon(BaseAddon):
def get_pyodide_settings(self, config_path: Path):
"""Get the settings for the client-side Pyodide kernel."""
return self.get_lite_plugin_settings(config_path, PYODIDE_KERNEL_PLUGIN_ID)
@property
def output_extensions(self) -> Path:
"""where labextensions will go in the output folder
def set_pyodide_settings(self, config_path: Path, settings: Dict[str, Any]) -> None:
"""Update the settings for the client-side Pyodide kernel."""
return self.set_lite_plugin_settings(
config_path, PYODIDE_KERNEL_PLUGIN_ID, settings
)
Candidate for hoisting to ``jupyterlite_core``
"""
return self.manager.output_dir / LAB_EXTENSIONS

def get_output_config_paths(self) -> Generator[Path, None, None]:
"""Yield an iterator of all config paths that _might_ exist in the
``output_dir``.
def get_output_labextension_packages(self) -> List[Path]:
"""All ``package.json`` files for labextensions in ``output_dir``.
This will likely move upstream.
Candidate for hoisting to ``jupyterlite_core``
"""
for app in [None, *self.manager.apps]:
app_dir = self.manager.output_dir / app if app else self.manager.output_dir
for path_name in [JUPYTERLITE_JSON, JUPYTERLITE_IPYNB]:
config_path = app_dir / path_name
yield config_path
return sorted(
[
*self.output_extensions.glob("*/package.json"),
*self.output_extensions.glob("@*/*/package.json"),
]
)

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

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

@jupyterlite/contents:plugin
index_path = self.manager.output_dir / index_url

This will likely move upstream.
"""
if not config_path.exists():
return {}
if not index_path.exists():
continue

config = json.loads(config_path.read_text(**UTF8))
yield self.task(
name=f"validate:{index_url}",
doc=f"validate {index_url} against {schema}",
file_dep=[index_path],
actions=[(self.validate_one_json_file, [schema, index_path])],
)

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

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

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

This will likely move upstream.
"""
whole_file = config = json.loads(config_path.read_text(**UTF8))
if config_path.name == JUPYTERLITE_IPYNB:
config = whole_file["metadata"][JUPYTERLITE_METADATA]
@property
def wheel_cache(self) -> Path:
"""where wheels will go in the cache folder"""
return self.manager.cache_dir / "wheels"

def get_pyodide_settings(self, config_path: Path):
"""Get the settings for the client-side Pyodide kernel."""
return self.get_lite_plugin_settings(config_path, PYODIDE_KERNEL_PLUGIN_ID)

config.setdefault(JUPYTER_CONFIG_DATA, {}).setdefault(
LITE_PLUGIN_SETTINGS, {}
).update({plugin_id: settings})
def set_pyodide_settings(self, config_path: Path, settings: Dict[str, Any]) -> None:
"""Update the settings for the client-side Pyodide kernel."""
return self.set_lite_plugin_settings(
config_path, PYODIDE_KERNEL_PLUGIN_ID, settings
)

config_path.write_text(json.dumps(whole_file, **JSON_FMT), **UTF8)
self.log.debug("%s wrote settings in %s: %s", plugin_id, config_path, settings)
self.maybe_timestamp(config_path)
def get_index_urls(self, index_path: Path):
"""Get output_dir relative URLs for an index file."""
index_sha256 = sha256(index_path.read_bytes()).hexdigest()
index_url = f"./{index_path.relative_to(self.manager.output_dir).as_posix()}"
index_url_with_sha = f"{index_url}?sha256={index_sha256}"
return index_url, index_url_with_sha

def get_package_wheel_index_url(
self, pkg_json: Path, index_name: str
) -> Optional[Path]:
pkg_data = json.loads(pkg_json.read_text(**UTF8))
wheel_dir = pkg_data.get(PKG_JSON_PYODIDE_KERNEL, {}).get(PKG_JSON_WHEELDIR)
if wheel_dir:
pkg_whl_index = pkg_json.parent / wheel_dir / index_name
if pkg_whl_index.exists():
return self.get_index_urls(pkg_whl_index)[1]
return None
Loading

0 comments on commit c370e55

Please sign in to comment.