diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3dc945a..39f8ae6 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -5,8 +5,11 @@ on: push: branches: - main - # pull_request: - + paths: + - 'docs/**' + pull_request: + paths: + - 'docs/**' defaults: run: @@ -19,42 +22,66 @@ jobs: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - emsdk_ver: "3.1.73" - python_version: "3.13" - - steps: - - uses: actions/checkout@v2 - - - name: Get number of CPU cores - uses: SimenB/github-actions-cpu-cores@v1 + - uses: actions/checkout@v6 - name: Install micromamba - uses: mamba-org/setup-micromamba@v1 + uses: mamba-org/setup-micromamba@v2 with: - environment-file: environment-dev.yml - environment-name: pyjs-wasm - condarc: | - channels: - - https://repo.mamba.pm/emscripten-forge-4x - - conda-forge - - - name: build the docs - shell: bash -el {0} + environment-file: environments/environment-docs.yml + environment-name: pyjs-docs + + - name: Create build directory for docs + run: mkdir docs_build + + - name: Convert *.py and *.js to *.ipynb using jupytext + run: | + mkdir -p docs_build/notebooks + + for f in examples/*.py; do + # get the filename without the extension and path + filename=$(basename -- "$f") + jupytext $f --to ipynb --output docs_build/notebooks/${filename%.*}.ipynb \ + --update-metadata '{"kernelspec": {"name": "xpython"}}' + done + for f in examples/*.js; do + # get the filename without the extension and path + filename=$(basename -- "$f") + jupytext $f --to ipynb --output docs_build/notebooks/${filename%.*}.ipynb \ + --update-metadata '{"kernelspec": {"name": "xjavascript"}}' + done + + - name: Create WASM environment for docs run: | - ./build_mkdocs.sh ${{matrix.emsdk_ver}} ${{ matrix.python_version }} - - ################################################################ - # upload to github pages - ################################################################ + micromamba create -n pyjs-docs-wasm \ + --platform=emscripten-wasm32 \ + -f environments/environment-docs-wasm.yml \ + --yes + + - name: Build the docs + run: | + mkdir docs_build/mkdocs + export PYTHONPATH=$PYTHONPATH:$(pwd)/stubs:$(pwd)/module + mkdocs build --site-dir=docs_build/mkdocs + + - name: Build JupyterLite + run: | + jupyter lite build \ + --contents=docs_build/notebooks \ + --output-dir docs_build/mkdocs/lite \ + --XeusAddon.default_channels=https://repo.prefix.dev/emscripten-forge-4x \ + --XeusAddon.default_channels=https://repo.prefix.dev/conda-forge \ + --XeusAddon.prefix=$MAMBA_ROOT_PREFIX/envs/pyjs-docs-wasm + + - name: Copy pyjs binary + run: | + cp -v $MAMBA_ROOT_PREFIX/envs/pyjs-docs-wasm/lib_js/pyjs/* \ + docs_build/mkdocs/lite/xeus/pyjs-docs-wasm/bin/ + - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v4 with: - path: docs_build/mkdocs + path: docs_build/mkdocs deploy: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8fe8ef3..e6191ca 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,8 +3,16 @@ name: CI on: workflow_dispatch: push: + branches: + - main + paths-ignore: + - 'docs/**' pull_request: + paths-ignore: + - 'docs/**' +env: + PYTHON_VERSION: 3.13 defaults: run: @@ -21,124 +29,78 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Install micromamba - uses: mamba-org/setup-micromamba@v1 + uses: mamba-org/setup-micromamba@v2 with: - environment-file: environment-dev.yml - environment-name: pyjs-wasm - condarc: | - channels: - - https://repo.prefix.dev/emscripten-forge-4x - - conda-forge - + environment-file: environments/environment-dev.yml + environment-name: pyjs-dev - name: Install Playwright run: | playwright install - - name: Build pyjs + - name: Create WASM environment to build pyjs run: | - micromamba activate pyjs-wasm - micromamba create -n pyjs-build-wasm \ --platform=emscripten-wasm32 \ - -c https://repo.prefix.dev/emscripten-forge-4x \ - -c https://repo.mamba.pm/conda-forge \ - --yes \ - "python=3.13" \ - "pybind11<3" \ - nlohmann_json pybind11_json numpy \ - pytest bzip2 sqlite zlib zstd libffi \ - exceptiongroup emscripten-abi>=4 \ - openssl liblzma - + -f environments/environment-wasm.yml \ + --yes - mkdir build - pushd build - - + - name: Build pyjs + run: | export PREFIX=$MAMBA_ROOT_PREFIX/envs/pyjs-build-wasm - export CMAKE_PREFIX_PATH=$PREFIX - export CMAKE_SYSTEM_PREFIX_PATH=$PREFIX - - # build pyjs - emcmake cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ON \ - -DBUILD_RUNTIME_BROWSER=ON \ - -DBUILD_RUNTIME_NODE=OFF \ - -DCMAKE_INSTALL_PREFIX=$PREFIX \ - .. + ./scripts/build_pyjs.sh $PREFIX - make -j2 - - make install - - popd - - - name: setup env with numpy + - name: Create test environment with numpy run: | - micromamba activate pyjs-wasm - micromamba create -n pyjs-build-wasm-with-numpy \ + micromamba create -n pyjs-wasm-test-with-numpy \ --platform=emscripten-wasm32 \ - -c https://repo.prefix.dev/emscripten-forge-4x \ - -c https://repo.mamba.pm/conda-forge \ - --yes \ - "python=3.13" pytest numpy exceptiongroup - + -f environments/environment-wasm-test.yml \ + numpy \ + --yes - - name: Test in browser-main + - name: Test browser-main with numpy run: | - micromamba activate pyjs-wasm pyjs_code_runner run script \ browser-main \ - --conda-env $MAMBA_ROOT_PREFIX/envs/pyjs-build-wasm-with-numpy \ + --conda-env $MAMBA_ROOT_PREFIX/envs/pyjs-wasm-test-with-numpy \ --mount $(pwd)/tests:/tests \ - --mount $(pwd)/module/pyjs:/lib/python3.13/site-packages/pyjs \ + --mount $(pwd)/module/pyjs:/lib/python${PYTHON_VERSION}/site-packages/pyjs \ --script main.py \ --work-dir /tests \ --pyjs-dir $(pwd)/build \ --headless \ --async-main - - name: Test in browser-worker + - name: Test browser-worker with numpy run: | - micromamba activate pyjs-wasm - - pyjs_code_runner run script \ browser-worker \ - --conda-env $MAMBA_ROOT_PREFIX/envs/pyjs-build-wasm-with-numpy \ + --conda-env $MAMBA_ROOT_PREFIX/envs/pyjs-wasm-test-with-numpy \ --mount $(pwd)/tests:/tests \ - --mount $(pwd)/module/pyjs:/lib/python3.13/site-packages/pyjs \ + --mount $(pwd)/module/pyjs:/lib/python${PYTHON_VERSION}/site-packages/pyjs \ --script main.py \ --work-dir /tests \ --pyjs-dir $(pwd)/build \ --headless \ --async-main - - name: setup minimal env without numpy + - name: Create test environment without numpy run: | - micromamba activate pyjs-wasm - micromamba create -n pyjs-build-wasm-no-numpy \ + micromamba create -n pyjs-wasm-test-no-numpy \ --platform=emscripten-wasm32 \ - -c https://repo.prefix.dev/emscripten-forge-4x \ - -c https://repo.mamba.pm/conda-forge \ - --yes \ - "python=3.13" pytest exceptiongroup + -f environments/environment-wasm-test.yml \ + --yes - - name: Test in browser-main-no-numpy + - name: Test browser-main without numpy run: | - micromamba activate pyjs-wasm - - pyjs_code_runner run script \ browser-main \ - --conda-env $MAMBA_ROOT_PREFIX/envs/pyjs-build-wasm-no-numpy \ + --conda-env $MAMBA_ROOT_PREFIX/envs/pyjs-wasm-test-no-numpy \ --mount $(pwd)/tests:/tests \ - --mount $(pwd)/module/pyjs:/lib/python3.13/site-packages/pyjs \ + --mount $(pwd)/module/pyjs:/lib/python${PYTHON_VERSION}/site-packages/pyjs \ --script main.py \ --work-dir /tests \ --pyjs-dir $(pwd)/build \ diff --git a/docs/design.md b/docs/design.md index c8a090f..a9843b7 100644 --- a/docs/design.md +++ b/docs/design.md @@ -2,7 +2,7 @@ ## Main Idea ### pybind11 -[Pybind11](https://github.com/pybind/pybind11) is a library that exposes C++ types in Python. It is a wrapper around the Python C API that allows for seamless integration of C++ and Python. +[Pybind11](https://github.com/pybind/pybind11) is a library that exposes C++ types in Python. It is a wrapper around the Python C API that allows for seamless integration of C++ and Python. To export a C++ class like the following to Python, you would use pybind11: ```C++ @@ -27,7 +27,7 @@ PYBIND11_MODULE(example, m) { } ``` Not only can Python call C++ functions, but C++ can also call Python functions. In particular, one can interact -with Python objects. An object is represented by the `py::object` type on the C++ side. +with Python objects. An object is represented by the `py::object` type on the C++ side. ```C++ // main.cpp diff --git a/docs/index.md b/docs/index.md index 5e74a38..2153d4a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,17 @@ # Welcome to `pyjs` -Pyjs is a python - javascript FFI for webassembly. -It allows you to write python code and run it in the browser. +Pyjs is a Python - JavaScript FFI for WebAssembly. +It allows you to write python code and run it in the browser. -## Quickstart +## Quick Start -Access Javascript from Python: +Access JavaScript from Python: ```python import pyjs -# hello world +# hello world pyjs.js.console.log("Hello, World!") # create a JavaScript function to add two numbers @@ -24,7 +24,7 @@ js_function = pyjs.js.Function("a", "b", """ result = js_function(1, 2) ``` -Access Python from Javascript: +Access Python from JavaScript: ```JavaScript // hello world @@ -33,11 +33,11 @@ pyjs.eval("print('Hello, World!')") // eval a python expression and get the result const py_list = pyjs.eval("[i for i in range(10)]") -/// access +/// access console.log(py_list.get(0)) // same as py_list[0] on the python side ``` -## Try it out +## Try it out! To try it out, you can use [jupyterlite](../lite), the [JavaScript REPL](try_from_js) or the [Python REPL](try_from_py). \ No newline at end of file diff --git a/docs/installation.md b/docs/installation.md index 3b15895..f163f42 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,60 +1,68 @@ # Deploying pyjs -## Prerequisites: +## Prerequisites Before we start, lets introduce a few concepts and tools that are used in the pyjs workflow. -### Conda-forge Emscripten-Forge -[Emscripten-forge](https://github.com/emscripten-forge/recipes) is similar to [conda-forge](https://conda-forge.org/) and provides packages compiled to webassembly using emscripten. +### Emscripten-forge + +Similar to [conda-forge](https://conda-forge.org/), [emscripten-forge](https://github.com/emscripten-forge/recipes) provides packages which are compiled to WebAssembly using [Emscripten](https://emscripten.org/). ### Empack -https://github.com/emscripten-forge/empack is a tool to "pack" conda environments into a set of files that can be consumed by pyjs. +[Empack](https://github.com/emscripten-forge/empack) is a tool to "pack" conda environments into a set of files that can be consumed by pyjs. +```bash +micromamba install empack -c conda-forge +``` ## Installation Steps -So we assume there is a directory called `/path/to/deploy` where we will -put all tools which need to be served to the user. -### Define a conda environment -Pyjs has a conda-like workflow. This means the first step -is to create a environment with the `pyjs` package installed -and all packages required for the project. +### 1. Define a conda environment +Pyjs has a conda-like workflow. This means the first step +is to define a WebAssembly environment that includes `pyjs` and any other packages required for your project. + +Create an *environment.yml* file with the following: ```yaml -name: my-pyjs-env +name: my-pyjs-env channels: - - https://repo.prefix.dev/emscripten-forge-dev + - https://repo.prefix.dev/emscripten-forge-4x - https://repo.prefix.dev/conda-forge dependencies: - pyjs - numpy ``` -The name of the environment can be choosen by the user. -The `channels` section specifies the conda channels to use. -The `https://repo.mamba.pm/emscripten-forge` is mandatory to install the `pyjs` package. -The `conda-forge` channel is used to install `noarch`. -All compiled packages need to be available in the `emscripten-forge` channel. +- The `name` of the environment can be chosen by the user. +- The `channels` section specifies the conda channels to use. +- The `https://repo.prefix.dev/emscripten-forge-4x` is mandatory to install the `pyjs` package. +- The `conda-forge` channel is used to install `noarch` packages. + -### Create the environment -Assuming the yaml file above is called `environment.yml` and is in the current directory, the environment can be created using `micromamba`: +Please note, all compiled packages need to be available in the [`emscripten-forge-4x`](https://prefix.dev/channels/emscripten-forge-4x) channel. + +### 2. Create the environment +The environment can be created using `micromamba`: ```Bash micromamba create -f environment.yml --platform emscripten-wasm32 --prefix /path/to/env ``` -### Copy pyjs +### 3. Populate the directory to be deployed + +Create a deploy directory, */path/to/deploy/*, where we will +put all of the tools needed to be served to the user. + Copy the pyjs binary from the environment to the deploy directory. -This should move pyjs_runtime_browser.js and pyjs_runtime_browser.wasm to the deployment directory. +This will move *pyjs_runtime_browser.js* and *pyjs_runtime_browser.wasm* to the deployment directory. ```Bash -cp /path/to/env/lib_js/pyjs/* /path/to/deploy +cp /path/to/env/lib_js/pyjs/* /path/to/deploy/ ``` - -### Pack the environment +### 4. Pack the environment After the environment is defined, the next step is to pack the environment using `empack`. @@ -64,31 +72,31 @@ empack pack env --env-prefix /path/to/env --outdir /path/to/deploy This will create a tarball for each package in the environment and a `empack_env_meta.json` file that describes the environment. -### The html/JavaScript code +### 5. Add the HTML/JavaScript code -The last step is to create a html file that loads the pyjs runtime and the packed environment. +The last step is to create an html file that loads the pyjs runtime and the packed environment. ```html Pyjs Example - +