diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 12dc0d966d..51bca259eb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -269,102 +269,6 @@ jobs: - run: cd cranelift/codegen && cargo build --features all-arch - run: ci/ensure_deterministic_build.sh - # Builds a Python wheel (package) for Windows/Mac/Linux. Note that we're - # careful to create binary-compatible releases here to old releases of - # Windows/Mac/Linux. This will also build wheels for Python 3.6, 3.7 and 3.8. - wheels: - name: Python Wheel - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - steps: - - uses: actions/checkout@v1 - with: - submodules: true - - uses: ./.github/actions/install-rust - with: - toolchain: nightly-2020-01-06 - - uses: ./.github/actions/binary-compatible-builds - - run: mkdir crates/misc/py/wheelhouse - shell: bash - - # Install Python & dependencies needed for our `setup.py` scripts - - name: Setup Python 3.6 - uses: actions/setup-python@v1 - with: - python-version: '3.6' - architecture: x64 - - run: $CENTOS pip3 install setuptools wheel setuptools-rust - shell: bash - - run: (cd crates/misc/py && $CENTOS $python setup.py bdist_wheel) - shell: bash - - # Clear the build directory between building different wheels for different - # Python versions to ensure that we don't package dynamic libraries twice by - # accident. - - run: $CENTOS rm -rf crates/misc/py/build - shell: bash - - # Set up Python 3.7 (and build it on Linux), reinstall dependencies, then - # rebuild our wheels - - name: Setup Python 3.7 - uses: actions/setup-python@v1 - with: - python-version: '3.7' - architecture: x64 - if: matrix.os != 'ubuntu-latest' - - name: Build Python 3.7 - run: $CENTOS sh ci/setup_centos6_python3.sh 3.7.3 - if: matrix.os == 'ubuntu-latest' - - run: $CENTOS pip3 install setuptools wheel setuptools-rust auditwheel - shell: bash - - run: (cd crates/misc/py && $CENTOS $python setup.py bdist_wheel) - shell: bash - - # Clear the build directory between building different wheels for different - # Python versions to ensure that we don't package dynamic libraries twice by - # accident. - - run: $CENTOS rm -rf crates/misc/py/build - shell: bash - - # Set up Python 3.8 (and build it on Linux), reinstall dependencies, then - # rebuild our wheels - - name: Setup Python 3.8 - uses: actions/setup-python@v1 - with: - python-version: '3.8' - architecture: x64 - if: matrix.os != 'ubuntu-latest' - - name: Build Python 3.8 - run: $CENTOS sh ci/setup_centos6_python3.sh 3.8.0 - if: matrix.os == 'ubuntu-latest' - - run: $CENTOS pip3 install setuptools wheel setuptools-rust auditwheel - shell: bash - - run: (cd crates/misc/py && $CENTOS $python setup.py bdist_wheel) - shell: bash - - # Move `dist/*.whl` into `wheelhouse/` so we can deploy them, but on Linux we - # need to run an `auditwheel` command as well to turn these into "manylinux" - # wheels to run across a number of distributions. - - run: cp crates/misc/py/dist/*.whl crates/misc/py/wheelhouse/ - shell: bash - if: matrix.os != 'ubuntu-latest' - - run: | - set -e - cd crates/misc/py - for whl in dist/*.whl; do - $CENTOS auditwheel repair "$whl" -w wheelhouse/ - done - shell: bash - if: matrix.os == 'ubuntu-latest' - - # Upload this for the publishing stage of pipelines - - uses: actions/upload-artifact@v1 - with: - name: wheels-${{ matrix.os }} - path: crates/misc/py/wheelhouse - # Perform release builds of `wasmtime` and `libwasmtime.so`. Builds on # Windows/Mac/Linux, and artifacts are uploaded after the build is finished. # Note that we also run tests here to test exactly what we're deploying. @@ -491,7 +395,7 @@ jobs: # github releases and/or tags for pushes. publish: name: Publish - needs: [doc_book, doc_api, wheels, build] + needs: [doc_book, doc_api, build] runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 @@ -510,26 +414,14 @@ jobs: uses: actions/download-artifact@v1 with: name: doc-api - - name: Download macOS Wheel - uses: actions/download-artifact@v1 - with: - name: wheels-macos-latest - name: Download macOS binaries uses: actions/download-artifact@v1 with: name: bins-macos-latest - - name: Download Linux Wheel - uses: actions/download-artifact@v1 - with: - name: wheels-ubuntu-latest - name: Download Linux binaries uses: actions/download-artifact@v1 with: name: bins-ubuntu-latest - - name: Download Windows Wheel - uses: actions/download-artifact@v1 - with: - name: wheels-windows-latest - name: Download Windows binaries uses: actions/download-artifact@v1 with: @@ -586,14 +478,6 @@ jobs: mkdir -p tmp/whl find dist/ -name '*.whl' -type f -exec cp '{}' tmp/whl -v \; - - name: Publish Python wheels on Pypi - uses: pypa/gh-action-pypi-publish@37e305e7413032d8422456179fee28fac7d25187 - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') - with: - user: __token__ - password: ${{ secrets.pypi_password }} - packages_dir: tmp/whl - # ... and if this was an actual push (tag or `master`) then we publish a # new release. This'll automatically publish a tag release or update `dev` # with this `sha` diff --git a/Cargo.lock b/Cargo.lock index 3034e4a107..f6f810b6a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -637,16 +637,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "ctor" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c5e5ac752e18207b12e16b10631ae5f7f68f8805f335f9b817ead83d9ffce1" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "cvt" version = "0.1.1" @@ -915,17 +905,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "ghost" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a36606a68532b5640dc86bb1f33c64b45c4682aad4c50f3937b317ea387f3d6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "gimli" version = "0.20.0" @@ -1025,51 +1004,6 @@ dependencies = [ "regex", ] -[[package]] -name = "indoc" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79255cf29f5711995ddf9ec261b4057b1deb34e66c90656c201e41376872c544" -dependencies = [ - "indoc-impl", - "proc-macro-hack", -] - -[[package]] -name = "indoc-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54554010aa3d17754e484005ea0022f1c93839aabc627c2c55f3d7b47206134c" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", - "unindent", -] - -[[package]] -name = "inventory" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf98296081bd2cb540acc09ef9c97f22b7e487841520350293605db1b2c7a27" -dependencies = [ - "ctor", - "ghost", - "inventory-impl", -] - -[[package]] -name = "inventory-impl" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a8e30575afe28eea36a9a39136b70b2fb6b0dd0a212a5bd1f30a498395c0274" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "itertools" version = "0.8.2" @@ -1339,28 +1273,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "paste" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8292c1e1e81ddb552c4c90c36af201a0ce7e34995f55f0480f01052f242811c9" -dependencies = [ - "paste-impl", - "proc-macro-hack", -] - -[[package]] -name = "paste-impl" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9c43f2645f06ee452544ad032886a75f3d1797b9487dcadcae9100ba58a51c" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "plain" version = "0.2.3" @@ -1455,50 +1367,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "pyo3" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1bfe257586436fbe1296d917f14a167d4253d0873bf43e2c9b9bdd58a3f9f35" -dependencies = [ - "indoc", - "inventory", - "lazy_static", - "libc", - "num-traits", - "paste", - "pyo3cls", - "regex", - "serde", - "serde_json", - "spin", - "unindent", - "version_check", -] - -[[package]] -name = "pyo3-derive-backend" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4882d8237fd8c7373cc25cb802fe0dab9ff70830fd56f47ef6c7f3f287fcc057" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pyo3cls" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf321cfab555f7411298733c86d21e5136f5ded13f5872fabf9de3337beecda" -dependencies = [ - "proc-macro2", - "pyo3-derive-backend", - "quote", - "syn", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -1934,12 +1802,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "stable_deref_trait" version = "1.1.1" @@ -2180,12 +2042,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -[[package]] -name = "unindent" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63f18aa3b0e35fed5a0048f029558b1518095ffe2a0a31fb87c93dece93a4993" - [[package]] name = "unsafe-any" version = "0.4.2" @@ -2473,19 +2329,6 @@ dependencies = [ "wasmtime-runtime", ] -[[package]] -name = "wasmtime-py" -version = "0.14.0" -dependencies = [ - "anyhow", - "pyo3", - "region", - "target-lexicon", - "wasmparser", - "wasmtime", - "wasmtime-wasi", -] - [[package]] name = "wasmtime-runtime" version = "0.14.0" diff --git a/Cargo.toml b/Cargo.toml index b0f85dc544..e5b131b445 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,6 @@ members = [ "cranelift", "crates/c-api", "crates/fuzzing", - "crates/misc/py", "crates/misc/run-examples", "crates/misc/rust", "crates/wiggle", diff --git a/ci/build-tarballs.sh b/ci/build-tarballs.sh index 5603e1b59e..0c3150fcbb 100755 --- a/ci/build-tarballs.sh +++ b/ci/build-tarballs.sh @@ -51,6 +51,3 @@ mv bins-$src/* tmp/$api_pkgname/lib cp crates/c-api/wasm-c-api/include/wasm.h tmp/$api_pkgname/include cp crates/c-api/include/{wasmtime,wasi}.h tmp/$api_pkgname/include mktarball $api_pkgname - -# Move wheels to dist folder -mv wheels-$src/* dist diff --git a/crates/misc/py/.gitignore b/crates/misc/py/.gitignore deleted file mode 100644 index cdee78184b..0000000000 --- a/crates/misc/py/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -*.bk -*.swp -*.swo -*.swx -tags -target -Cargo.lock -.*.rustfmt -cranelift.dbg* -rusty-tags.* -*~ -\#*\# -build -dist -*.egg-info diff --git a/crates/misc/py/Cargo.toml b/crates/misc/py/Cargo.toml deleted file mode 100644 index 3c3ec4e7b5..0000000000 --- a/crates/misc/py/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "wasmtime-py" -version = "0.14.0" -authors = ["The Wasmtime Project Developers"] -description = "Python extension for Wasmtime" -license = "Apache-2.0 WITH LLVM-exception" -categories = ["wasm", "python"] -keywords = ["webassembly", "wasm"] -repository = "https://github.com/bytecodealliance/wasmtime" -readme = "README.md" -edition = "2018" - -[lib] -name = "_wasmtime" -crate-type = ["cdylib"] -test = false -doc = false - -[dependencies] -wasmtime = { path = "../../api", version = "0.14.0" } -wasmtime-wasi = { path = "../../wasi", version = "0.14.0" } -target-lexicon = { version = "0.10.0", default-features = false } -anyhow = "1.0.19" -region = "2.0.0" -wasmparser = "0.51.2" -pyo3 = { version = "0.8.0", features = ["extension-module"] } - -[badges] -maintenance = { status = "actively-developed" } diff --git a/crates/misc/py/LICENSE b/crates/misc/py/LICENSE deleted file mode 100644 index f9d81955f4..0000000000 --- a/crates/misc/py/LICENSE +++ /dev/null @@ -1,220 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - ---- LLVM Exceptions to the Apache 2.0 License ---- - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you -may redistribute such embedded portions in such Object form without complying -with the conditions of Sections 4(a), 4(b) and 4(d) of the License. - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a -court of competent jurisdiction determines that the patent provision (Section -3), the indemnity provision (Section 9) or other Section of the License -conflicts with the conditions of the GPLv2, you may retroactively and -prospectively choose to deem waived or otherwise exclude such Section(s) of -the License, but only in their entirety and only with respect to the Combined -Software. - diff --git a/crates/misc/py/README.md b/crates/misc/py/README.md deleted file mode 100644 index 4e38bd46fa..0000000000 --- a/crates/misc/py/README.md +++ /dev/null @@ -1,20 +0,0 @@ -Python 3 extension for interface with Wasmtime/Cranelift. - -# Build - -First, you'll need to install some Python dependencies: - -``` -$ pip3 install setuptools wheel==0.31.1 setuptools-rust -``` - -Next you can build the extension with: - -``` -rustup run nightly python3 setup.py build -``` - -Note that a nightly version of Rust is required due to our usage of PyO3. - -This will create a directory called `build/lib` which you can add to -`PYTHONPATH` in order to get `import wasmtime` working. diff --git a/crates/misc/py/examples/gcd/.gitignore b/crates/misc/py/examples/gcd/.gitignore deleted file mode 100644 index 46cc3441e8..0000000000 --- a/crates/misc/py/examples/gcd/.gitignore +++ /dev/null @@ -1 +0,0 @@ -gcd.wasm diff --git a/crates/misc/py/examples/gcd/README.md b/crates/misc/py/examples/gcd/README.md deleted file mode 100644 index f8be453d28..0000000000 --- a/crates/misc/py/examples/gcd/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Build example's file - -To build `gcd.wasm` use rustc (nightly) for wasm32 target with debug information: - -``` -rustc +nightly --target=wasm32-unknown-unknown -g gcd.rs --crate-type=cdylib -``` - -# Run example - -Point path to the built wasmtime_py library location when running python, e.g. - -``` -PYTHONPATH=../../target/debug python3 run.py -``` diff --git a/crates/misc/py/examples/gcd/gcd.rs b/crates/misc/py/examples/gcd/gcd.rs deleted file mode 100644 index 2f6ef85b62..0000000000 --- a/crates/misc/py/examples/gcd/gcd.rs +++ /dev/null @@ -1,17 +0,0 @@ -#[inline(never)] -#[no_mangle] -pub extern "C" fn gcd(m_: u32, n_: u32) -> u32 { - let mut m = m_; - let mut n = n_; - while m > 0 { - let tmp = m; - m = n % m; - n = tmp; - } - return n; -} - -#[no_mangle] -pub extern "C" fn test() -> u32 { - gcd(24, 9) -} diff --git a/crates/misc/py/examples/gcd/run.py b/crates/misc/py/examples/gcd/run.py deleted file mode 100644 index 648d1ecc6d..0000000000 --- a/crates/misc/py/examples/gcd/run.py +++ /dev/null @@ -1,5 +0,0 @@ -import wasmtime -import gcd - -print("gcd(27, 6) =", gcd.gcd(27, 6)) - diff --git a/crates/misc/py/examples/import/.gitignore b/crates/misc/py/examples/import/.gitignore deleted file mode 100644 index 2a6475ec63..0000000000 --- a/crates/misc/py/examples/import/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -import.wasm -main.wasm -__pycache__ \ No newline at end of file diff --git a/crates/misc/py/examples/import/README.md b/crates/misc/py/examples/import/README.md deleted file mode 100644 index db56dcbe9b..0000000000 --- a/crates/misc/py/examples/import/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Build example's file - -To build `demo.wasm` use rustc (nightly) for wasm32 target with debug information: - -``` -rustc +nightly --target=wasm32-unknown-unknown demo.rs --crate-type=cdylib -``` - -# Run example - -Point path to the built `wasmtime_py` library location when running python, e.g. - -``` -PYTHONPATH=../../target/debug python3 run.py -``` diff --git a/crates/misc/py/examples/import/demo.rs b/crates/misc/py/examples/import/demo.rs deleted file mode 100644 index eb9bca3d00..0000000000 --- a/crates/misc/py/examples/import/demo.rs +++ /dev/null @@ -1,12 +0,0 @@ -extern "C" { - fn callback(s: *const u8, s_len: u32) -> u32; -} - -static MSG: &str = "Hello, world!"; - -#[no_mangle] -pub extern "C" fn test() { - unsafe { - callback(MSG.as_ptr(), MSG.len() as u32); - } -} diff --git a/crates/misc/py/examples/import/env.py b/crates/misc/py/examples/import/env.py deleted file mode 100644 index 068cff6aec..0000000000 --- a/crates/misc/py/examples/import/env.py +++ /dev/null @@ -1,8 +0,0 @@ -import demo - -def callback(msg_p: 'i32', msg_len: 'i32') -> 'i32': - mv = memoryview(demo.memory) - msg = bytes(mv[msg_p:(msg_p + msg_len)]).decode('utf-8') - - print(msg) - return 42 diff --git a/crates/misc/py/examples/import/run.py b/crates/misc/py/examples/import/run.py deleted file mode 100644 index f39baccd49..0000000000 --- a/crates/misc/py/examples/import/run.py +++ /dev/null @@ -1,4 +0,0 @@ -import wasmtime -import demo - -demo.test() diff --git a/crates/misc/py/examples/two_modules/.gitignore b/crates/misc/py/examples/two_modules/.gitignore deleted file mode 100644 index 64ee0b754f..0000000000 --- a/crates/misc/py/examples/two_modules/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -one.wasm -two.wasm -__pycache__ \ No newline at end of file diff --git a/crates/misc/py/examples/two_modules/README.md b/crates/misc/py/examples/two_modules/README.md deleted file mode 100644 index e23101408b..0000000000 --- a/crates/misc/py/examples/two_modules/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Build example's file - -To build `one.wasm` use rustc (nightly) for wasm32 target with debug information: - -``` -rustc +nightly --target=wasm32-unknown-unknown one.rs --crate-type=cdylib -``` - -To build `two.wasm` use wabt. -``` -wat2wasm two.wat -o two.wasm -``` - -# Run example - -Point path to the built wasmtime_py library location when running python, e.g. - -``` -PYTHONPATH=../../target/debug python3 run.py -``` diff --git a/crates/misc/py/examples/two_modules/env.py b/crates/misc/py/examples/two_modules/env.py deleted file mode 100644 index 5af7f13e3f..0000000000 --- a/crates/misc/py/examples/two_modules/env.py +++ /dev/null @@ -1,2 +0,0 @@ -def answer() -> 'i32': - return 42 diff --git a/crates/misc/py/examples/two_modules/one.rs b/crates/misc/py/examples/two_modules/one.rs deleted file mode 100644 index 032b83f822..0000000000 --- a/crates/misc/py/examples/two_modules/one.rs +++ /dev/null @@ -1,16 +0,0 @@ -extern "C" { - fn answer() -> u32; -} - -// For the purpose of this wasm example, we don't worry about multi-threading, -// and will be using the PLACE in unsafe manner below. -static mut PLACE: u32 = 23; - -#[no_mangle] -pub extern "C" fn bar() -> *const u32 { - unsafe { - PLACE = answer(); - // Return a pointer to the exported memory. - (&PLACE) as *const u32 - } -} diff --git a/crates/misc/py/examples/two_modules/run.py b/crates/misc/py/examples/two_modules/run.py deleted file mode 100644 index f5b0aa6c25..0000000000 --- a/crates/misc/py/examples/two_modules/run.py +++ /dev/null @@ -1,4 +0,0 @@ -import wasmtime -import two - -print("answer() returned", two.ask()) diff --git a/crates/misc/py/examples/two_modules/two.wat b/crates/misc/py/examples/two_modules/two.wat deleted file mode 100644 index 265d633284..0000000000 --- a/crates/misc/py/examples/two_modules/two.wat +++ /dev/null @@ -1,11 +0,0 @@ -(module - (import "one" "memory" (memory $memory 0)) - (import "one" "bar" (func $bar (result i32))) - (export "ask" (func $foo)) - - (func $foo (result i32) - call $bar - ;; Deference returned pointer to the value from imported memory - i32.load - ) -) diff --git a/crates/misc/py/python/wasmtime/__init__.py b/crates/misc/py/python/wasmtime/__init__.py deleted file mode 100644 index 8c611175fd..0000000000 --- a/crates/misc/py/python/wasmtime/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -from .lib_wasmtime import imported_modules, instantiate -import sys -import os.path - -from importlib import import_module -from importlib.abc import Loader, MetaPathFinder -from importlib.util import spec_from_file_location - -# Mostly copied from -# https://stackoverflow.com/questions/43571737/how-to-implement-an-import-hook-that-can-modify-the-source-code-on-the-fly-using -class MyMetaFinder(MetaPathFinder): - def find_spec(self, fullname, path, target=None): - if path is None or path == "": - path = [os.getcwd()] # top level import -- - path.extend(sys.path) - if "." in fullname: - *parents, name = fullname.split(".") - else: - name = fullname - for entry in path: - filename = os.path.join(entry, name + ".wasm") - if not os.path.exists(filename): - continue - - return spec_from_file_location(fullname, filename, loader=MyLoader(filename)) - return None - -class MyLoader(Loader): - def __init__(self, filename): - self.filename = filename - - def create_module(self, spec): - return None # use default module creation semantics - - def exec_module(self, module): - with open(self.filename, "rb") as f: - data = f.read() - - imports = {} - for module_name, fields in imported_modules(data).items(): - imports[module_name] = {} - imported_module = import_module(module_name) - for field_name in fields: - imports[module_name][field_name] = imported_module.__dict__[field_name] - - res = instantiate(data, imports) - module.__dict__.update(res.instance.exports) - -sys.meta_path.insert(0, MyMetaFinder()) diff --git a/crates/misc/py/setup.py b/crates/misc/py/setup.py deleted file mode 100644 index 9ad1e3f075..0000000000 --- a/crates/misc/py/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -from setuptools import setup -from setuptools_rust import Binding, RustExtension - - -def no_tag_default_to_dev(version): - if version.exact: - return version.format_with("{tag}") - return "0.0.1" - - -setup(name='wasmtime', - classifiers=[ - "Development Status :: 1 - Planning", - "Intended Audience :: Developers", - "Programming Language :: Python", - "Programming Language :: Rust", - "Operating System :: POSIX", - "Operating System :: MacOS :: MacOS X", - "Operating System :: Microsoft :: Windows", - ], - packages=['wasmtime'], - package_dir={'wasmtime': 'python/wasmtime'}, - use_scm_version = { - "root": "../../..", - "relative_to": __file__, - "version_scheme": no_tag_default_to_dev, - "local_scheme": lambda _: "", - }, - setup_requires=['setuptools_scm'], - rust_extensions=[RustExtension('wasmtime.lib_wasmtime', 'Cargo.toml', binding=Binding.PyO3)], - zip_safe=False) diff --git a/crates/misc/py/src/function.rs b/crates/misc/py/src/function.rs deleted file mode 100644 index 45443e23e4..0000000000 --- a/crates/misc/py/src/function.rs +++ /dev/null @@ -1,120 +0,0 @@ -//! Support for a calling of a bounds (exported) function. - -use crate::value::{pyobj_to_value, value_to_pyobj}; -use pyo3::exceptions::Exception; -use pyo3::prelude::*; -use pyo3::types::{PyAny, PyDict, PyTuple}; - -// TODO support non-export functions -#[pyclass] -pub struct Function { - pub func: wasmtime::Func, -} - -impl Function { - pub fn func(&self) -> wasmtime::Func { - self.func.clone() - } -} - -#[pymethods] -impl Function { - #[__call__] - #[args(args = "*")] - fn call(&self, py: Python, args: &PyTuple) -> PyResult { - let mut runtime_args = Vec::new(); - for item in args.iter() { - runtime_args.push(pyobj_to_value(py, item)?); - } - let results = self - .func - .call(&runtime_args) - .map_err(|e| crate::err2py(e.into()))?; - let mut py_results = Vec::new(); - for result in results.into_vec() { - py_results.push(value_to_pyobj(py, result)?); - } - if py_results.len() == 1 { - Ok(py_results[0].clone_ref(py)) - } else { - Ok(PyTuple::new(py, py_results).to_object(py)) - } - } -} - -fn parse_annotation_type(s: &str) -> wasmtime::ValType { - match s { - "I32" | "i32" => wasmtime::ValType::I32, - "I64" | "i64" => wasmtime::ValType::I64, - "F32" | "f32" => wasmtime::ValType::F32, - "F64" | "f64" => wasmtime::ValType::F64, - _ => panic!("unknown type in annotations"), - } -} -pub fn wrap_into_pyfunction(store: &wasmtime::Store, callable: &PyAny) -> PyResult { - if !callable.hasattr("__annotations__")? { - // TODO support calls without annotations? - return Err(PyErr::new::( - "import is not a function".to_string(), - )); - } - - let annot = callable.getattr("__annotations__")?.cast_as::()?; - let mut params = Vec::new(); - let mut returns = Vec::new(); - for (name, value) in annot.iter() { - let ty = parse_annotation_type(&value.to_string()); - match name.to_string().as_str() { - "return" => returns.push(ty), - _ => params.push(ty), - } - } - - let ft = wasmtime::FuncType::new( - params.into_boxed_slice(), - returns.clone().into_boxed_slice(), - ); - - let gil = Python::acquire_gil(); - let func = callable.to_object(gil.python()); - Ok(wasmtime::Func::new(store, ft, move |_, params, results| { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let params = params - .iter() - .map(|p| match p { - wasmtime::Val::I32(i) => i.clone().into_py(py), - wasmtime::Val::I64(i) => i.clone().into_py(py), - wasmtime::Val::F32(i) => i.clone().into_py(py), - wasmtime::Val::F64(i) => i.clone().into_py(py), - _ => panic!(), - }) - .collect::>(); - - let result = func - .call(py, PyTuple::new(py, params), None) - .expect("TODO: convert result to trap"); - - let result = if let Ok(t) = result.cast_as::(py) { - t - } else { - if result.is_none() { - PyTuple::empty(py) - } else { - PyTuple::new(py, &[result]) - } - }; - for (i, ty) in returns.iter().enumerate() { - let result_item = result.get_item(i); - results[i] = match ty { - wasmtime::ValType::I32 => wasmtime::Val::I32(result_item.extract::().unwrap()), - wasmtime::ValType::I64 => wasmtime::Val::I64(result_item.extract::().unwrap()), - _ => { - panic!(); - } - }; - } - Ok(()) - })) -} diff --git a/crates/misc/py/src/instance.rs b/crates/misc/py/src/instance.rs deleted file mode 100644 index 87953f9ce2..0000000000 --- a/crates/misc/py/src/instance.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! WebAssembly Instance API object. - -use crate::function::Function; -use crate::memory::Memory; -use pyo3::prelude::*; -use pyo3::types::PyDict; - -#[pyclass] -pub struct Instance { - pub instance: wasmtime::Instance, -} - -#[pymethods] -impl Instance { - #[getter(exports)] - fn get_exports(&mut self) -> PyResult { - let gil = Python::acquire_gil(); - let py = gil.python(); - let exports = PyDict::new(py); - let module = self.instance.module().clone(); - for (i, e) in module.exports().iter().enumerate() { - match e.ty() { - wasmtime::ExternType::Func(ft) => { - let mut args_types = Vec::new(); - for ty in ft.params().iter() { - args_types.push(ty.clone()); - } - let f = Py::new( - py, - Function { - func: self.instance.exports()[i].func().unwrap().clone(), - }, - )?; - exports.set_item(e.name().to_string(), f)?; - } - wasmtime::ExternType::Memory(_) => { - let f = Py::new( - py, - Memory { - memory: self.instance.exports()[i].memory().unwrap().clone(), - }, - )?; - exports.set_item(e.name().to_string(), f)?; - } - _ => { - // Skip unknown export type. - continue; - } - } - } - - Ok(exports.to_object(py)) - } -} diff --git a/crates/misc/py/src/lib.rs b/crates/misc/py/src/lib.rs deleted file mode 100644 index 4551ca99f5..0000000000 --- a/crates/misc/py/src/lib.rs +++ /dev/null @@ -1,175 +0,0 @@ -use crate::function::{wrap_into_pyfunction, Function}; -use crate::instance::Instance; -use crate::memory::Memory; -use crate::module::Module; -use pyo3::exceptions::Exception; -use pyo3::prelude::*; -use pyo3::types::{PyAny, PyBytes, PyDict, PySet}; -use pyo3::wrap_pyfunction; - -mod function; -mod instance; -mod memory; -mod module; -mod value; - -fn err2py(err: anyhow::Error) -> PyErr { - PyErr::new::(format!("{:?}", err)) -} - -#[pyclass] -pub struct InstantiateResultObject { - instance: Py, - module: Py, -} - -#[pymethods] -impl InstantiateResultObject { - #[getter(instance)] - fn get_instance(&self) -> PyResult> { - let gil = Python::acquire_gil(); - let py = gil.python(); - Ok(self.instance.clone_ref(py)) - } - - #[getter(module)] - fn get_module(&self) -> PyResult> { - let gil = Python::acquire_gil(); - let py = gil.python(); - Ok(self.module.clone_ref(py)) - } -} - -fn find_export_in(obj: &PyAny, store: &wasmtime::Store, name: &str) -> PyResult { - let obj = obj.cast_as::()?; - - Ok(if let Some(item) = obj.get_item(name) { - if item.is_callable() { - if item.get_type().is_subclass::()? { - let wasm_fn = item.cast_as::()?; - wasm_fn.func().into() - } else { - wrap_into_pyfunction(store, item)?.into() - } - } else if item.get_type().is_subclass::()? { - let wasm_mem = item.cast_as::()?; - wasm_mem.memory.clone().into() - } else { - return Err(PyErr::new::(format!( - "unsupported import type {}", - name - ))); - } - } else { - return Err(PyErr::new::(format!( - "import {} is not found", - name - ))); - }) -} - -/// WebAssembly instantiate API method. -#[pyfunction] -pub fn instantiate( - py: Python, - buffer_source: &PyBytes, - import_obj: &PyDict, -) -> PyResult> { - let wasm_data = buffer_source.as_bytes(); - - let engine = wasmtime::Engine::new(&wasmtime::Config::new().wasm_multi_value(true)); - let store = wasmtime::Store::new(&engine); - - let module = wasmtime::Module::new(&store, wasm_data).map_err(err2py)?; - - // If this module expects to be able to use wasi then go ahead and hook - // that up into the imported crates. - let cx = wasmtime_wasi::WasiCtxBuilder::new() - .build() - .map_err(|e| err2py(e.into()))?; - let wasi_snapshot_preview1 = wasmtime_wasi::Wasi::new(&store, cx); - let cx = wasmtime_wasi::old::snapshot_0::WasiCtxBuilder::new() - .build() - .map_err(|e| err2py(e.into()))?; - let wasi_snapshot = wasmtime_wasi::old::snapshot_0::Wasi::new(&store, cx); - - let mut imports: Vec = Vec::new(); - for i in module.imports() { - if i.module() == "wasi_snapshot" { - if let Some(func) = wasi_snapshot.get_export(i.name()) { - imports.push(func.clone().into()); - continue; - } - } - if i.module() == "wasi_snapshot_preview1" { - if let Some(func) = wasi_snapshot_preview1.get_export(i.name()) { - imports.push(func.clone().into()); - continue; - } - } - let module_name = i.module(); - if let Some(m) = import_obj.get_item(module_name) { - let e = find_export_in(m, &store, i.name())?; - imports.push(e); - } else { - return Err(PyErr::new::(format!( - "imported module {} is not found", - module_name - ))); - } - } - - let instance = wasmtime::Instance::new(&module, &imports) - .map_err(|t| PyErr::new::(format!("instantiated with trap {:?}", t)))?; - - let module = Py::new(py, Module { module })?; - - let instance = Py::new(py, Instance { instance })?; - - Py::new(py, InstantiateResultObject { instance, module }) -} - -#[pyfunction] -pub fn imported_modules<'p>(py: Python<'p>, buffer_source: &PyBytes) -> PyResult<&'p PyDict> { - let wasm_data = buffer_source.as_bytes(); - let dict = PyDict::new(py); - // TODO: error handling - let mut parser = wasmparser::ModuleReader::new(wasm_data).unwrap(); - while !parser.eof() { - let section = parser.read().unwrap(); - match section.code { - wasmparser::SectionCode::Import => {} - _ => continue, - }; - let reader = section.get_import_section_reader().unwrap(); - for import in reader { - let import = import.unwrap(); - // Skip over wasi-looking imports since those aren't imported from - // Python but rather they're implemented natively. - if wasmtime_wasi::is_wasi_module(import.module) { - continue; - } - let set = match dict.get_item(import.module) { - Some(set) => set.downcast_ref::().unwrap(), - None => { - let set = PySet::new::(py, &[])?; - dict.set_item(import.module, set)?; - set - } - }; - set.add(import.field)?; - } - } - Ok(dict) -} - -#[pymodule] -fn lib_wasmtime(_: Python, m: &PyModule) -> PyResult<()> { - m.add_class::()?; - m.add_class::()?; - m.add_class::()?; - m.add_class::()?; - m.add_wrapped(wrap_pyfunction!(instantiate))?; - m.add_wrapped(wrap_pyfunction!(imported_modules))?; - Ok(()) -} diff --git a/crates/misc/py/src/memory.rs b/crates/misc/py/src/memory.rs deleted file mode 100644 index 1a43c82d84..0000000000 --- a/crates/misc/py/src/memory.rs +++ /dev/null @@ -1,82 +0,0 @@ -//! WebAssembly Memory API object. - -use pyo3::class::PyBufferProtocol; -use pyo3::exceptions::BufferError; -use pyo3::ffi; -use pyo3::prelude::*; -use std::ffi::CStr; -use std::os::raw::{c_int, c_void}; -use std::ptr; - -#[pyclass] -pub struct Memory { - pub memory: wasmtime::Memory, -} - -#[pymethods] -impl Memory { - #[getter(current)] - pub fn current(&self) -> u32 { - self.memory.size() - } - - pub fn grow(&self, _number: u32) -> u32 { - (-1i32) as u32 - } -} - -#[pyproto] -impl PyBufferProtocol for Memory { - fn bf_getbuffer(&self, view: *mut ffi::Py_buffer, flags: c_int) -> PyResult<()> { - if view.is_null() { - return Err(BufferError::py_err("View is null")); - } - - unsafe { - /* - As a special case, for temporary buffers that are wrapped by - PyMemoryView_FromBuffer() or PyBuffer_FillInfo() this field is NULL. - In general, exporting objects MUST NOT use this scheme. - */ - (*view).obj = ptr::null_mut(); - } - - let readonly = if (flags & ffi::PyBUF_WRITABLE) == ffi::PyBUF_WRITABLE { - 0 - } else { - 1 - }; - - unsafe { - let base = self.memory.data_ptr(); - let current_length = self.memory.data_size(); - - (*view).buf = base as *mut c_void; - (*view).len = current_length as isize; - (*view).readonly = readonly; - (*view).itemsize = 1; - - (*view).format = ptr::null_mut(); - if (flags & ffi::PyBUF_FORMAT) == ffi::PyBUF_FORMAT { - let msg = CStr::from_bytes_with_nul(b"B\0").unwrap(); - (*view).format = msg.as_ptr() as *mut _; - } - - (*view).ndim = 1; - (*view).shape = ptr::null_mut(); - if (flags & ffi::PyBUF_ND) == ffi::PyBUF_ND { - (*view).shape = (&((*view).len)) as *const _ as *mut _; - } - - (*view).strides = ptr::null_mut(); - if (flags & ffi::PyBUF_STRIDES) == ffi::PyBUF_STRIDES { - (*view).strides = &((*view).itemsize) as *const _ as *mut _; - } - - (*view).suboffsets = ptr::null_mut(); - (*view).internal = ptr::null_mut(); - } - - Ok(()) - } -} diff --git a/crates/misc/py/src/module.rs b/crates/misc/py/src/module.rs deleted file mode 100644 index 4c7e4a30c2..0000000000 --- a/crates/misc/py/src/module.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! WebAssembly Module API object. - -use pyo3::prelude::*; - -#[pyclass] -pub struct Module { - pub module: wasmtime::Module, -} diff --git a/crates/misc/py/src/value.rs b/crates/misc/py/src/value.rs deleted file mode 100644 index b899dcfbe2..0000000000 --- a/crates/misc/py/src/value.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Utility functions to handle values conversion between abstractions/targets. - -use pyo3::exceptions::Exception; -use pyo3::prelude::*; -use pyo3::types::PyAny; -use wasmtime::Val; - -pub fn pyobj_to_value(_: Python, p: &PyAny) -> PyResult { - if let Ok(n) = p.extract() { - Ok(Val::I32(n)) - } else if let Ok(n) = p.extract() { - Ok(Val::I64(n)) - } else if let Ok(n) = p.extract() { - Ok(Val::F64(n)) - } else if let Ok(n) = p.extract() { - Ok(Val::F32(n)) - } else { - Err(PyErr::new::("unsupported value type")) - } -} - -pub fn value_to_pyobj(py: Python, value: Val) -> PyResult { - Ok(match value { - Val::I32(i) => i.into_py(py), - Val::I64(i) => i.into_py(py), - Val::F32(i) => i.into_py(py), - Val::F64(i) => i.into_py(py), - Val::AnyRef(_) | Val::FuncRef(_) | Val::V128(_) => { - return Err(PyErr::new::("unsupported value type")) - } - }) -} diff --git a/docs/lang-python.md b/docs/lang-python.md index 9eddd0a225..83a8a17681 100644 --- a/docs/lang-python.md +++ b/docs/lang-python.md @@ -1,17 +1,13 @@ # Using WebAssembly from Python -Wasmtime can be used as a python module loader, which allows almost any +Wasmtime [is available on PyPI](https://pypi.org/project/wasmtime/) and can be +used programmatically or as a python module loader, which allows almost any WebAssembly module to be used as a python module. This guide will go over adding Wasmtime to your project, and some provided examples of what can be done with WebAssembly modules. -## Prerequisites - -To follow this guide, you'll need - - - Python 3.6 or newer - - The [WebAssembly binary toolkit](https://github.com/WebAssembly/wabt/releases) - - The rust toolchain installer [rustup](https://rustup.rs/) +Make sure you've got Python 3.5 or newer installed locally, and we can get +started! ## Getting started and simple example @@ -22,20 +18,9 @@ function for calculating the greatest common denominator of two numbers. {{#include ../examples/gcd.wat}} ``` -Before we can do anything with this module, we need to convert it to the -WebAssembly binary format. We can do this with the command line tools provided -by the WebAssembly binary toolkit - -```bash -wat2wasm gcd.wat -``` - -This will create the binary form of the gcd module `gcd.wasm`, we'll use this -module in the following steps. - -Next, install the Wasmtime module loader, which is provided as a [python package](https://pypi.org/project/wasmtime/) -on PyPi. It can be installed as a dependency through Pip or related tools such -as Pipenv. +Next, install the [Wasmtime package](https://pypi.org/project/wasmtime/) from +PyPi. It can be installed as a dependency through Pip or related tools such as +Pipenv. ```bash pip install wasmtime @@ -51,7 +36,10 @@ After you have Wasmtime installed and you've imported `wasmtime`, you can import WebAssembly modules in your project like any other python module. ```python -{{#include ../crates/misc/py/examples/gcd/run.py}} +import wasmtime.loader +import gcd + +print("gcd(27, 6) =", gcd.gcd(27, 6)) ``` This script should output @@ -63,58 +51,26 @@ gcd(27, 6) = 3 If this is the output you see, congrats! You've successfully ran your first WebAssembly code in python! -## Host interaction and memory - -In the first example, we called a function exported by a WebAssembly -module. Depeding on what you need to accomplish, WebAssembly modules can also -call functions from other modules and python itself. This is done through the -module imports mechanism, which allows other modules and the host environment to -provide functions, globals, and memory spaces. The following example will show -you how to use module imports and work with module linear memory. - -> Note: At the moment, the Wasmtime python module can only import functions and -> memories. - -To show how we can use functions from the host, take a look at this rust code - -```rust -{{#include ../crates/misc/py/examples/import/demo.rs}} -``` - -We have a `test` function which calls `callback`. Since it's wrapped in `extern "C"`, -this function will be dynamically linked. The Wasmtime module does this linking -automatically by importing any needed modules at runtime. If we compile this -example without any extra linker options, the result module will import -`callback` from a module called `env`, so we need to provide an implementation of -`callback` inside an `env.py` module. +You can also alternatively use the [`wasmtime` package's +API](https://bytecodealliance.github.io/wasmtime-py/): ```python -{{#include ../crates/misc/py/examples/import/env.py}} +from wasmtime import Store, Module, Instance + +store = Store() +module = Module.from_file(store, 'gcd.wat') +instance = Instance(module, []) +gcd = instance.get_export('gcd') +print("gcd(27, 6) =", gcd(27, 6)) ``` -The module provides `callback` with a pointer to a string message. We use this -to index into the demo module's memory, extract the message bytes and print them -as a string. Every WebAssembly module exports its main linear memory as "memory" -by default, so it's accessible as `demo.memory` in python. We wrap the memory -into a `memoryview` so we can safely access the values inside. +## More examples and contributing -Before we move on, note the type annotations on `callback`. These are necessary -for representing your function as something callable in WebAssembly, since -WebAssembly functions only operate on 32 and 64 bit floats and integers. When -defining functions for use by WebAssembly modules, make sure the parameters and -return value are annotated appropriately as any of `'i32'`, `'i64'`, `'f32'`, or -`'f64'`. - -Before we can use `demo.rs` we need to compile it - -```bash -rustup run nightly rustc --target=wasm32-unknown-unknown --crate-type=cdylib demo.rs -``` - -We can then use it like this - -```python -{{#include ../crates/misc/py/examples/import/run.py}} -``` - -The script should print `Hello, world!` and exit. +The `wasmtime` Python package currently [lives in its own repository outside of +`wasmtime`](https://github.com/bytecodealliance/wasmtime-py) and has a [number +of other more advanced +examples](https://github.com/bytecodealliance/wasmtime-py/tree/master/examples) +as well. Feel free to browse those, but if you find anything missing don't +hesitate to [open an +issue](https://github.com/bytecodealliance/wasmtime-py/issues/new) and let us +know if you have any questions!