Skip to content

Commit 56c2728

Browse files
committed
add script to install libs in pyodide
1 parent 55357f1 commit 56c2728

File tree

4 files changed

+103
-5
lines changed

4 files changed

+103
-5
lines changed

.github/workflows/wasm_test.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,22 @@ jobs:
2121
with:
2222
python-version: "3.12"
2323

24-
- name: Install dependencies (pyodide)
24+
- name: Install libraries
2525
run: |
26-
python -m pip install pyodide-build
26+
python -m pip install pyodide-build jupytext
2727
pyodide xbuildenv install
2828
pyodide venv .venv-pyodide
2929
30-
- name: Debug
30+
- name: Convert lectures to python files
31+
run: python testing/generate_py_files.py
32+
33+
- name: Pip and Python (venv)
3134
run: |
3235
source .venv-pyodide/bin/activate
3336
which pip
3437
which python
3538
36-
- name: Try numba install
39+
- name: Import libraries in pyodide
3740
run: |
3841
source .venv-pyodide/bin/activate
39-
pip install numba
42+
python testing/install_pyodide_libs.py

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ lectures/_build
33
.ipynb_checkpoints/
44
.virtual_documents/
55
_build/*
6+
testing/py_files/*

testing/generate_py_files.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import os
2+
import glob
3+
import shutil
4+
5+
PARENT_DIR = os.path.dirname(os.path.abspath(__file__))
6+
ROOT_DIR = os.path.dirname(PARENT_DIR)
7+
8+
IN_DIR = os.path.join(ROOT_DIR, 'lectures')
9+
OUT_DIR = os.path.join(PARENT_DIR, 'py_files')
10+
11+
def main():
12+
shutil.copytree(IN_DIR, OUT_DIR, dirs_exist_ok=True)
13+
cwd = os.getcwd()
14+
os.chdir(OUT_DIR)
15+
cmd = "jupytext --to py *.md"
16+
os.system(cmd)
17+
lectures = list(glob.glob(OUT_DIR + '/*.md'))
18+
for file in lectures:
19+
os.remove(file)
20+
os.chdir(cwd)
21+
22+
if __name__ == '__main__':
23+
main()

testing/install_pyodide_libs.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import os
2+
import glob
3+
import ast
4+
import importlib.util
5+
import micropip
6+
import asyncio
7+
8+
PARENT_DIR = os.path.dirname(os.path.abspath(__file__))
9+
ROOT_DIR = os.path.dirname(PARENT_DIR)
10+
11+
PY_FILES_DIR = os.path.join(PARENT_DIR, 'py_files')
12+
SKIP_FILES = [
13+
'short_path.py',
14+
'inflation_history.py'
15+
]
16+
17+
def get_imported_libraries(file_path):
18+
"""Extracts all imported libraries from a Python file."""
19+
with open(file_path, "r", encoding="utf-8") as file:
20+
tree = ast.parse(file.read(), filename=file_path)
21+
22+
imports = set()
23+
24+
for node in ast.walk(tree):
25+
if isinstance(node, ast.Import):
26+
for alias in node.names:
27+
imports.add(alias.name.split('.')[0]) # Get the top-level package
28+
elif isinstance(node, ast.ImportFrom):
29+
if node.module:
30+
imports.add(node.module.split('.')[0])
31+
32+
return sorted(imports)
33+
34+
35+
def is_library_installed(library_name):
36+
"""Checks if a given library is installed."""
37+
return importlib.util.find_spec(library_name) is not None
38+
39+
40+
async def install_missing_libraries(file_path, previously_installed):
41+
"""Finds missing libraries and installs them using micropip."""
42+
for skip_file in SKIP_FILES:
43+
if file_path.endswith(skip_file):
44+
return
45+
print(f"Installing missing libraries for file: {file_path}")
46+
imported_libraries = get_imported_libraries(file_path)
47+
48+
missing_libraries = []
49+
for lib in imported_libraries:
50+
if lib not in previously_installed and not is_library_installed(lib):
51+
missing_libraries.append(lib)
52+
53+
if len(missing_libraries) == 0:
54+
print(f"All required libraries are already installed in {file_path}")
55+
return
56+
57+
for lib in missing_libraries:
58+
print(f"Installing missing libraries: {lib}")
59+
await micropip.install(lib)
60+
previously_installed.add(lib)
61+
62+
63+
async def main():
64+
lectures_py = list(glob.glob(PY_FILES_DIR + '/*.py'))
65+
previously_installed = set()
66+
for file in lectures_py:
67+
await install_missing_libraries(file, previously_installed)
68+
69+
70+
if __name__ == '__main__':
71+
asyncio.run(main())

0 commit comments

Comments
 (0)