diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml new file mode 100644 index 00000000..36b40e04 --- /dev/null +++ b/.github/workflows/wasm.yml @@ -0,0 +1,21 @@ +name: Check WASM +on: [pull_request] +jobs: + preview: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install required libraries + shell: bash -l {0} + run: | + pip install pyodide-py jupytext PyYAML + # Soft check on CI to check the WASM compatibility. + - name: Check WASM lectures + shell: bash -l {0} + run: | + python testing/check_wasm.py diff --git a/testing/check_wasm.py b/testing/check_wasm.py new file mode 100644 index 00000000..595b9ad2 --- /dev/null +++ b/testing/check_wasm.py @@ -0,0 +1,68 @@ +# Testing script to check whether the lectures added in +# wasm_compatible.yml are compatible with WASM. +# This is a soft-check check based on the imports present. +# It is still recommended to test the lecture on the +# https://github.com/QuantEcon/project.lecture-wasm + +import os +import yaml +import jupytext + +from pyodide.code import find_imports + +# This is the list of imports (libraries) that are not supported +# WASM pyodide kernel in Jupyterlite. +UNSUPPORTED_LIBS = { + 'quantecon', + 'numba', +} + +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT_DIR = os.path.dirname(CURRENT_DIR) +LECTURES_DIR = os.path.join(ROOT_DIR, "lectures") +CONFIG_FILE = os.path.join(ROOT_DIR, "wasm_compatible.yml") + + +def get_wasm_compatible_files(): + """ + Get the list of lectures names from the config file. + """ + # Load the YAML configuration + with open(CONFIG_FILE, "r") as file: + config = yaml.safe_load(file) + + return config.get("lectures", []) + + +def convert_md_to_py_string(md_file_path): + """ + Convert a .md(myst) file to Python code string without creating a .py file. + + Args: + md_file_path (str): Path to the Markdown file (.md) + + Returns: + str: Python code as a string + """ + # Read the markdown file as a Jupytext notebook + with open(md_file_path, 'r', encoding='utf-8') as md_file: + notebook = jupytext.read(md_file) + + # Convert the notebook to Python script format + py_code = jupytext.writes(notebook, fmt="py") + + return py_code + + +def test_compatibility(): + file_names = get_wasm_compatible_files() + for file_name in file_names: + py_string = convert_md_to_py_string(os.path.join(LECTURES_DIR, file_name + ".md")) + file_imports = find_imports(py_string) + for file_import in file_imports: + if file_import in UNSUPPORTED_LIBS: + error = 'import `{}` in lecture `{}` is not supported by WASM'.format(file_import, file_name) + raise ValueError(error) + +if __name__ == "__main__": + test_compatibility() diff --git a/wasm_compatible.yml b/wasm_compatible.yml new file mode 100644 index 00000000..7a3d5844 --- /dev/null +++ b/wasm_compatible.yml @@ -0,0 +1,4 @@ +# Config file for adding lectures that are WASM compatible. +# Please add the lecture file names without `.md` extension. +lectures: + - cobweb