forked from jupyterlite/xeus-python-kernel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathenv_build_addon.py
161 lines (130 loc) · 5.33 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
"""a JupyterLite addon for creating the env for xeus-python"""
import json
import os
from pathlib import Path
import requests
import shutil
from subprocess import check_call, run, DEVNULL
from tempfile import TemporaryDirectory
from urllib.parse import urlparse
import yaml
from traitlets import List, Unicode
from empack.pack import pack_env, DEFAULT_CONFIG_PATH
from empack.file_patterns import PkgFileFilter, pkg_file_filter_from_yaml
from jupyterlite_core.constants import (
SHARE_LABEXTENSIONS,
LAB_EXTENSIONS,
JUPYTERLITE_JSON,
UTF8,
FEDERATED_EXTENSIONS,
)
from jupyterlite_core.addons.federated_extensions import (
FederatedExtensionAddon,
ENV_EXTENSIONS,
)
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(
None,
config=True,
allow_none=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(XeusPythonEnv, self).__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(ENV_EXTENSIONS):
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"])