This repository has been archived by the owner on Feb 14, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 19
/
env_build_addon.py
145 lines (117 loc) · 5 KB
/
env_build_addon.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
"""a JupyterLite addon for creating the env for xeus-python"""
import json
import os
from pathlib import Path
from tempfile import TemporaryDirectory
from jupyterlite_core.addons.federated_extensions import FederatedExtensionAddon
from jupyterlite_core.constants import (
FEDERATED_EXTENSIONS,
JUPYTERLITE_JSON,
LAB_EXTENSIONS,
SHARE_LABEXTENSIONS,
UTF8,
)
from traitlets import List, Unicode
from .build import XEUS_PYTHON_VERSION, build_and_pack_emscripten_env
JUPYTERLITE_XEUS_PYTHON = "@jupyterlite/xeus-python-kernel"
class PackagesList(List):
def from_string(self, s):
return s.split(",")
class XeusPythonEnv(FederatedExtensionAddon):
__all__ = ["post_build"]
xeus_python_version = Unicode(XEUS_PYTHON_VERSION).tag(
config=True, description="The xeus-python version to use"
)
empack_config = Unicode(
"",
config=True,
description="The path or URL to the empack config file",
)
pin_packages = PackagesList([]).tag(
description="This property is not supposed to be used, unless you know what you're doing.",
)
packages = PackagesList([]).tag(
config=True,
description="A comma-separated list of packages to install in the xeus-python env",
)
environment_file = Unicode(
"environment.yml",
config=True,
description='The path to the environment file. Defaults to "environment.yml"',
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.cwd = TemporaryDirectory()
def post_build(self, manager):
"""yield a doit task to create the emscripten-32 env and grab anything we need from it"""
# Install the jupyterlite-xeus-python ourselves
for pkg_json in self.env_extensions(self.labextensions_path):
pkg_data = json.loads(pkg_json.read_text(**UTF8))
if pkg_data.get("name") == JUPYTERLITE_XEUS_PYTHON:
yield from self.safe_copy_extension(pkg_json)
env_prefix = build_and_pack_emscripten_env(
xeus_python_version=self.xeus_python_version,
packages=[*self.packages, *self.pin_packages],
environment_file=Path(self.manager.lite_dir) / self.environment_file,
empack_config=self.empack_config,
output_path=self.cwd.name,
log=self.log,
)
# Find the federated extensions in the emscripten-env and install them
for pkg_json in self.env_extensions(env_prefix / SHARE_LABEXTENSIONS):
yield from self.safe_copy_extension(pkg_json)
# TODO Currently we're shamelessly overwriting the
# python_data.{js,data} into the jupyterlite-xeus-python labextension.
# We should really find a nicer way.
# (make jupyterlite-xeus-python extension somewhat configurable?)
dest = self.output_extensions / "@jupyterlite" / "xeus-python-kernel" / "static"
# copy *.tar.gz for all side packages
for item in Path(self.cwd.name).iterdir():
if item.suffix == ".gz":
file = item.name
yield dict(
name=f"xeus:copy:{file}",
actions=[(self.copy_one, [item, dest / file])],
)
for file in [
"empack_env_meta.json",
"xpython_wasm.js",
"xpython_wasm.wasm",
]:
yield dict(
name=f"xeus:copy:{file}",
actions=[(self.copy_one, [Path(self.cwd.name) / file, dest / file])],
)
jupyterlite_json = manager.output_dir / JUPYTERLITE_JSON
lab_extensions_root = manager.output_dir / LAB_EXTENSIONS
lab_extensions = self.env_extensions(lab_extensions_root)
yield dict(
name="patch:xeus",
doc=f"ensure {JUPYTERLITE_JSON} includes the federated_extensions",
file_dep=[*lab_extensions, jupyterlite_json],
actions=[(self.patch_jupyterlite_json, [jupyterlite_json])],
)
def safe_copy_extension(self, pkg_json):
"""Copy a labextension, and overwrite it
if it's already in the output
"""
pkg_path = pkg_json.parent
stem = json.loads(pkg_json.read_text(**UTF8))["name"]
dest = self.output_extensions / stem
file_dep = [
p for p in pkg_path.rglob("*") if not (p.is_dir() or self.is_ignored_sourcemap(p.name))
]
yield dict(
name=f"xeus:copy:ext:{stem}",
file_dep=file_dep,
actions=[(self.copy_one, [pkg_path, dest])],
)
def dedupe_federated_extensions(self, config):
if FEDERATED_EXTENSIONS not in config:
return
named = {}
# Making sure to dedupe extensions by keeping the most recent ones
for ext in config[FEDERATED_EXTENSIONS]:
if os.path.exists(self.output_extensions / ext["name"] / ext["load"]):
named[ext["name"]] = ext
config[FEDERATED_EXTENSIONS] = sorted(named.values(), key=lambda x: x["name"])