Skip to content
This repository was archived by the owner on Feb 14, 2024. It is now read-only.

Commit 78fbd57

Browse files
authored
Add support for environment.yml file (#96)
* Add support for environment.yml file * Remove unused file * Make file name configurable * Run tests from the tests directory (to prevent finding the root environment.yml file) * Add tests * Fix tests * Update tests * Fix tests * Update README * Update docs * Try fixing docs
1 parent cc2d16d commit 78fbd57

File tree

11 files changed

+160
-57
lines changed

11 files changed

+160
-57
lines changed

.github/workflows/build.yml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ jobs:
144144
run: pip install jupyterlite-xeus-python.tar.gz
145145

146146
- name: Run tests
147-
run: pytest -rP tests/test_xeus_python_env.py
147+
run: pytest -rP test_xeus_python_env.py
148+
working-directory: tests
148149

149150
python-tests-mamba:
150151
needs: build
@@ -170,7 +171,8 @@ jobs:
170171
run: pip install jupyterlite-xeus-python.tar.gz
171172

172173
- name: Run tests
173-
run: pytest -rP tests/test_xeus_python_env.py
174+
run: pytest -rP test_xeus_python_env.py
175+
working-directory: tests
174176

175177
python-tests-micromamba:
176178
needs: build
@@ -195,7 +197,8 @@ jobs:
195197
run: pip install jupyterlite-xeus-python.tar.gz
196198

197199
- name: Run tests
198-
run: pytest -rP tests/test_xeus_python_env.py
200+
run: pytest -rP test_xeus_python_env.py
201+
working-directory: tests
199202

200203
python-tests-conda:
201204
needs: build
@@ -220,4 +223,5 @@ jobs:
220223
run: pip install jupyterlite-xeus-python.tar.gz
221224

222225
- name: Run tests
223-
run: pytest -rP tests/test_xeus_python_env.py
226+
run: pytest -rP test_xeus_python_env.py
227+
working-directory: tests

.readthedocs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ build:
66
python: "mambaforge-4.10"
77

88
conda:
9-
environment: docs/environment.yml
9+
environment: docs/build-environment.yml

README.md

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,39 @@ jupyter lite build
3131

3232
## Pre-installed packages
3333

34-
xeus-python allows you to pre-install packages in the Python runtime. You can pre-install packages by passing the `XeusPythonEnv.packages` CLI option to `jupyter lite build`.
35-
This will automatically install any labextension that it founds, for example installing ipyleaflet will make ipyleaflet work without the need to manually install the jupyter-leaflet labextension.
34+
xeus-python allows you to pre-install packages in the Python runtime. You can pre-install packages by adding an `environment.yml` file in the JupyterLite build directory, this file will be found automatically by xeus-python which will pre-build the environment when running `jupyter lite build`.
3635

37-
For example, say you want to install `NumPy`, `Matplotlib` and `ipyleaflet`, it can be done with the following command:
36+
Furthermore, this automatically installs any labextension that it founds, for example installing ipyleaflet will make ipyleaflet work without the need to manually install the jupyter-leaflet labextension.
37+
38+
Say you want to install `NumPy`, `Matplotlib` and `ipyleaflet`, it can be done by creating the `environment.yml` file with the following content:
39+
40+
```yml
41+
name: xeus-python-kernel
42+
channels:
43+
- https://repo.mamba.pm/emscripten-forge
44+
- https://repo.mamba.pm/conda-forge
45+
dependencies:
46+
- numpy
47+
- matplotlib
48+
- ipycanvas
49+
```
50+
51+
Then you only need to build JupyterLite:
3852
3953
```bash
40-
jupyter lite build --XeusPythonEnv.packages=numpy,matplotlib,ipyleaflet
54+
jupyter lite build
4155
```
4256

43-
The same can be achieved through a `jupyterlite_config.json` file:
57+
You can also pick another name for that environment file (*e.g.* `custom.yml`), by doing so, you will need to specify that name to xeus-python:
4458

45-
```json
46-
{
47-
"XeusPythonEnv": {
48-
"packages": ["numpy", "matplotlib", "ipyleaflet"]
49-
}
50-
}
59+
```bash
60+
jupyter lite build --XeusPythonEnv.environment_file=custom.yml
5161
```
5262

63+
#### About pip dependencies
64+
65+
It is common to provide `pip` dependencies in a conda environment file, this is currently **not supported** by xeus-python.
66+
5367
## Contributing
5468

5569
### Development install

docs/build-environment.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: xeus-python-kernel-docs
2+
3+
channels:
4+
- conda-forge
5+
6+
dependencies:
7+
- mamba
8+
- pydata-sphinx-theme
9+
- pip
10+
- pip:
11+
- jupyterlite==0.1.0b16
12+
- jupyterlite-sphinx
13+
- jupyterlite-xeus-python==0.6.0

docs/conf.py

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

1616
html_theme = "pydata_sphinx_theme"
1717

18-
jupyterlite_config = "jupyterlite_config.json"
1918
jupyterlite_dir = "."

docs/configuration.rst

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,37 @@ Configuration
66
Pre-installed packages
77
----------------------
88

9-
xeus-python allows you to pre-install packages in the Python runtime. You can pre-install packages by passing the ``XeusPythonEnv.packages`` CLI option to ``jupyter lite build``.
9+
``xeus-python`` allows you to pre-install packages in the Python runtime. You can pre-install packages by adding an ``environment.yml`` file in the JupyterLite build directory, this file will be found automatically by xeus-python which will pre-build the environment when running `jupyter lite build`.
1010

11-
.. note::
12-
This will automatically install any labextension that it founds, for example installing ipyleaflet will make ipyleaflet work without the need to manually install the jupyter-leaflet labextension.
11+
Furthermore, this automatically installs any labextension that it founds, for example installing ipyleaflet will make ipyleaflet work without the need to manually install the jupyter-leaflet labextension.
1312

14-
For example, say you want to install ``NumPy``, ``Matplotlib`` and ``ipyleaflet``, it can be done with the following command:
13+
Say you want to install ``NumPy``, ``Matplotlib`` and ``ipyleaflet``, it can be done by creating the ``environment.yml`` file with the following content:
1514

1615
.. code::
1716
18-
jupyter lite build --XeusPythonEnv.packages=numpy,matplotlib,ipyleaflet
17+
name: xeus-python-kernel
18+
channels:
19+
- https://repo.mamba.pm/emscripten-forge
20+
- https://repo.mamba.pm/conda-forge
21+
dependencies:
22+
- numpy
23+
- matplotlib
24+
- ipycanvas
1925
20-
The same can be achieved through a ``jupyterlite_config.json`` file:
26+
Then you only need to build JupyterLite:
2127

2228
.. code::
2329
24-
{
25-
"XeusPythonEnv": {
26-
"packages": ["numpy", "matplotlib", "ipyleaflet"]
27-
}
28-
}
30+
jupyter lite build
31+
32+
You can also pick another name for that environment file (*e.g.* `custom.yml`), by doing so, you will need to specify that name to xeus-python:
33+
34+
.. code::
35+
36+
jupyter lite build --XeusPythonEnv.environment_file=custom.yml
37+
38+
.. note::
39+
It is common to provide `pip` dependencies in a conda environment file, this is currently **not supported** by xeus-python.
2940

3041
Then those packages are usable directly:
3142

docs/environment.yml

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
name: xeus-python-kernel-docs
2-
32
channels:
4-
- conda-forge
5-
3+
- https://repo.mamba.pm/emscripten-forge
4+
- https://repo.mamba.pm/conda-forge
65
dependencies:
7-
- mamba
8-
- pydata-sphinx-theme
9-
- pip
10-
- pip:
11-
- jupyterlite==0.1.0b16
12-
- jupyterlite-sphinx
13-
- jupyterlite-xeus-python==0.6.0
6+
- numpy
7+
- matplotlib
8+
- ipycanvas

docs/jupyterlite_config.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

jupyterlite_xeus_python/env_build_addon.py

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,11 @@ class XeusPythonEnv(FederatedExtensionAddon):
9191
description="A comma-separated list of packages to install in the xeus-python env",
9292
)
9393

94-
@property
95-
def specs(self):
96-
"""The package specs to install in the environment."""
97-
return [
98-
f"python={PYTHON_VERSION}",
99-
"xeus-python"
100-
if not self.xeus_python_version
101-
else f"xeus-python={self.xeus_python_version}",
102-
*self.packages,
103-
]
94+
environment_file = Unicode(
95+
"environment.yml",
96+
config=True,
97+
description="The path to the environment file. Defaults to \"environment.yml\"",
98+
)
10499

105100
@property
106101
def prefix_path(self):
@@ -113,6 +108,14 @@ def __init__(self, *args, **kwargs):
113108
self.cwd = TemporaryDirectory()
114109
self.root_prefix = "/tmp/xeus-python-kernel"
115110
self.env_name = "xeus-python-kernel"
111+
self.channels = CHANNELS
112+
self.specs = [
113+
f"python={PYTHON_VERSION}",
114+
"xeus-python"
115+
if not self.xeus_python_version
116+
else f"xeus-python={self.xeus_python_version}",
117+
*self.packages,
118+
]
116119

117120
# Cleanup tmp dir in case it's not empty
118121
shutil.rmtree(Path(self.root_prefix) / "envs", ignore_errors=True)
@@ -128,8 +131,41 @@ def post_build(self, manager):
128131
if pkg_data.get("name") == JUPYTERLITE_XEUS_PYTHON:
129132
yield from self.safe_copy_extension(pkg_json)
130133

131-
# Bail early if there is no extra package to install
132-
if not self.packages and not self.xeus_python_version:
134+
bail_early = True
135+
if self.packages or self.xeus_python_version:
136+
bail_early = False
137+
138+
# Process environment.yml file
139+
if (Path(self.manager.lite_dir) / self.environment_file).exists():
140+
bail_early = False
141+
142+
with open(Path(self.manager.lite_dir) / self.environment_file) as f:
143+
env_data = yaml.safe_load(f)
144+
145+
if env_data.get("name") is not None:
146+
self.env_name = env_data["name"]
147+
148+
if env_data.get("channels") is not None:
149+
channels = env_data["channels"]
150+
151+
for channel in channels:
152+
if channel not in self.channels:
153+
self.channels.append(channel)
154+
155+
if env_data.get("dependencies") is not None:
156+
dependencies = env_data["dependencies"]
157+
158+
for dependency in dependencies:
159+
if isinstance(dependency, str) and dependency not in self.specs:
160+
self.specs.append(dependency)
161+
elif isinstance(dependency, dict) and dependency.get("pip") is not None:
162+
raise RuntimeError(
163+
"""Cannot install pip dependencies in the xeus-python Emscripten environment (yet?).
164+
"""
165+
)
166+
167+
# Bail early if there is nothing to do
168+
if bail_early:
133169
return []
134170

135171
# Create emscripten env with the given packages
@@ -207,13 +243,13 @@ def create_env(self):
207243
env_name=self.env_name,
208244
base_prefix=self.root_prefix,
209245
specs=self.specs,
210-
channels=CHANNELS,
246+
channels=self.channels,
211247
target_platform=PLATFORM,
212248
)
213249
return
214250

215251
channels = []
216-
for channel in CHANNELS:
252+
for channel in self.channels:
217253
channels.extend(["-c", channel])
218254

219255
if MAMBA_AVAILABLE:

tests/environment-1.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: xeus-python-kernel-1
2+
channels:
3+
- https://repo.mamba.pm/emscripten-forge
4+
- https://repo.mamba.pm/conda-forge
5+
dependencies:
6+
- numpy
7+
- matplotlib
8+
- ipycanvas

tests/test_xeus_python_env.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,31 @@ def test_python_env():
2929
# Check empack output
3030
assert os.path.isfile(Path(addon.cwd.name) / "python_data.js")
3131
assert os.path.isfile(Path(addon.cwd.name) / "python_data.data")
32+
33+
os.remove(Path(addon.cwd.name) / "python_data.js")
34+
os.remove(Path(addon.cwd.name) / "python_data.data")
35+
36+
37+
def test_python_env_from_file_1():
38+
app = LiteStatusApp(log_level="DEBUG")
39+
app.initialize()
40+
manager = app.lite_manager
41+
42+
addon = XeusPythonEnv(manager)
43+
addon.environment_file = "environment-1.yml"
44+
45+
for step in addon.post_build(manager):
46+
pass
47+
48+
# Check env
49+
assert os.path.isdir("/tmp/xeus-python-kernel/envs/xeus-python-kernel-1")
50+
51+
assert os.path.isfile("/tmp/xeus-python-kernel/envs/xeus-python-kernel-1/bin/xpython_wasm.js")
52+
assert os.path.isfile("/tmp/xeus-python-kernel/envs/xeus-python-kernel-1/bin/xpython_wasm.wasm")
53+
54+
# Check empack output
55+
assert os.path.isfile(Path(addon.cwd.name) / "python_data.js")
56+
assert os.path.isfile(Path(addon.cwd.name) / "python_data.data")
57+
58+
os.remove(Path(addon.cwd.name) / "python_data.js")
59+
os.remove(Path(addon.cwd.name) / "python_data.data")

0 commit comments

Comments
 (0)