Skip to content

Commit

Permalink
feat(bindgen): Add python WASI support
Browse files Browse the repository at this point in the history
  • Loading branch information
thewtex committed Apr 6, 2023
1 parent d8d0748 commit fec10f3
Show file tree
Hide file tree
Showing 21 changed files with 892 additions and 46 deletions.
25 changes: 24 additions & 1 deletion .github/workflows/python-wasm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand All @@ -30,3 +30,26 @@ jobs:
uses: mikepenz/action-junit-report@v2
with:
report_paths: 'src/python/itkwasm/junit/test-results*.xml'

test-pythonpackages:
runs-on: ${{ matrix.os }}
strategy:
max-parallel: 3
matrix:
os: [ubuntu-22.04, windows-2022, macos-12]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install hatch
- name: Test compress-stringify wasi
working-directory: ./packages/compress-stringify/python/compress-stringify-wasi
run: |
hatch run test
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@
"build:emscripten": "node ./src/build-emscripten.js",
"build:emscripten:compress-stringify": "node ./src/itk-wasm-cli.js -s packages/compress-stringify -b emscripten-build build ",
"build:bindgen:typescript:compress-stringify": "./src/itk-wasm-cli.js -s packages/compress-stringify -b emscripten-build bindgen --package-name @itk-wasm/compress-stringify --package-description \"Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:python:compress-stringify": "./src/itk-wasm-cli.js -s packages/compress-stringify -b wasi-build bindgen --language python --package-name compress-stringify --package-description \"Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:emscripten:dicom": "node ./src/itk-wasm-cli.js -s packages/dicom -b emscripten-build build ",
"build:bindgen:typescript:dicom": "./src/itk-wasm-cli.js -s packages/dicom -b emscripten-build bindgen --package-name @itk-wasm/dicom --package-description \"Read files and images related to DICOM file format.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:emscripten:packages": "npm run build:emscripten:compress-stringify && npm run build:bindgen:typescript:compress-stringify && npm run build:emscripten:dicom && npm run build:bindgen:typescript:dicom",
"build:wasi": "node ./src/build-wasi.js && npm run build:wasi:packages",
"build:wasi:compress-stringify": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/compress-stringify -b wasi-build build",
"build:wasi:packages": "npm run build:wasi:compress-stringify",
"build:wasi:packages": "npm run build:wasi:compress-stringify && npm run build:bindgen:python:compress-stringify",
"cypress:open": "npx cypress open",
"cypress:run": "npx cypress run --config defaultCommandTimeout=8000",
"cypress:install": "npx cypress install",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# compress-stringify-wasi

[![PyPI version](https://badge.fury.io/py/compress-stringify-wasi.svg)](https://badge.fury.io/py/compress-stringify-wasi)

Zstandard compression and decompression and base64 encoding and decoding in WebAssembly. WASI implementation.

## Installation

```sh
pip install compress-stringify-wasi
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""compress-stringify-wasi: Zstandard compression and decompression and base64 encoding and decoding in WebAssembly. WASI implementation."""

from .compress_stringify import compress_stringify
from .parse_string_decompress import parse_string_decompress

from ._version import __version__
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "0.1.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Generated file. Do not edit.

from pathlib import Path
import os
from typing import Dict, Tuple

from importlib_resources import files as file_resources

from itkwasm import (
InterfaceTypes,
PipelineOutput,
PipelineInput,
Pipeline,
BinaryStream,
)

def compress_stringify(
input: bytes,
stringify: bool = False,
compression_level: int = 3,
data_url_prefix: str = "data:base64,",
) -> bytes:
"""Given a binary, compress and optionally base64 encode.
Parameters
----------
input: bytes
Input binary
stringify: bool, optional
Stringify the output
compression_level: int, optional
Compression level, typically 1-9
data_url_prefix: str, optional
dataURL prefix
Returns
-------
bytes
Output compressed binary
"""
pipeline = Pipeline(file_resources('compress_stringify_wasi').joinpath(Path('wasm_modules') / Path('compress-stringify.wasi.wasm')))

pipeline_outputs: List[PipelineOutput] = [
PipelineOutput(InterfaceTypes.BinaryStream),
]

pipeline_inputs: List[PipelineInput] = [
PipelineInput(InterfaceTypes.BinaryStream, BinaryStream(input)),
]

args: List[str] = ['--memory-io',]
# Inputs
args.append('0')
# Outputs
args.append('0')
# Options
if stringify is not None:
args.append('--stringify')

if compression_level is not None:
args.append('--compression-level')
args.append(str(compression_level))

if data_url_prefix is not None:
args.append('--data-url-prefix')
args.append(str(data_url_prefix))



outputs = pipeline.run(args, pipeline_outputs, pipeline_inputs)

result = outputs[0].data.data
return result


del pipeline
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Generated file. Do not edit.

from pathlib import Path
import os
from typing import Dict, Tuple

from importlib_resources import files as file_resources

from itkwasm import (
InterfaceTypes,
PipelineOutput,
PipelineInput,
Pipeline,
BinaryStream,
)

def parse_string_decompress(
input: bytes,
parse_string: bool = False,
) -> bytes:
"""Given a binary or string produced with compress-stringify, decompress and optionally base64 decode.
Parameters
----------
input: bytes
Compressed input
parse_string: bool, optional
Parse the input string before decompression
Returns
-------
bytes
Output decompressed binary
"""
pipeline = Pipeline(file_resources('compress_stringify_wasi').joinpath(Path('wasm_modules') / Path('parse-string-decompress.wasi.wasm')))

pipeline_outputs: List[PipelineOutput] = [
PipelineOutput(InterfaceTypes.BinaryStream),
]

pipeline_inputs: List[PipelineInput] = [
PipelineInput(InterfaceTypes.BinaryStream, BinaryStream(input)),
]

args: List[str] = ['--memory-io',]
# Inputs
args.append('0')
# Outputs
args.append('0')
# Options
if parse_string is not None:
args.append('--parse-string')



outputs = pipeline.run(args, pipeline_outputs, pipeline_inputs)

result = outputs[0].data.data
return result


del pipeline
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"

[project]
name = "compress-stringify-wasi"
readme = "README.md"
license = "Apache-2.0"
dynamic = ["version", "description"]
classifiers = [
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
"Programming Language :: C++",
"Environment :: WebAssembly",
"Environment :: WebAssembly :: Emscripten",
"Environment :: WebAssembly :: WASI",
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
]
keywords = [
"itkwasm",
"webassembly",
"emscripten",
"wasi",
]

requires-python = ">=3.7"
dependencies = [
"itkwasm >= 1.0b.82",
"importlib_resources",
]

[tool.hatch.version]
path = "compress_stringify_wasi/_version.py"

[tool.hatch.envs.default]
dependencies = [
"pytest",
]

[tool.hatch.envs.default.scripts]
test = "pytest"

[tool.hatch.build]
exclude = [
"/examples",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from compress_stringify_wasi import compress_stringify, parse_string_decompress

def test_decompress_returns_what_was_compressed():
data = bytes([222, 173, 190, 239])
compressed_data = compress_stringify(data, compression_level=8)
decompressed_data = parse_string_decompress(compressed_data)

assert decompressed_data[0] == 222
assert decompressed_data[1] == 173
assert decompressed_data[2] == 190
assert decompressed_data[3] == 239

def test_we_can_stringify_during_compression():
data = bytes([222, 173, 190, 239])
compressed_data = compress_stringify(data, compression_level=8, stringify=True)
assert compressed_data.decode() == 'data:base64,KLUv/SAEIQAA3q2+7w=='
decompressed_data = parse_string_decompress(compressed_data, parse_string=True)

assert decompressed_data[0] == 222
assert decompressed_data[1] == 173
assert decompressed_data[2] == 190
assert decompressed_data[3] == 239

def test_we_can_use_a_custom_data_url_prefix():
data = bytes([222, 173, 190, 239])
compressed_data = compress_stringify(data, compression_level=8, stringify=True, data_url_prefix='custom,')
assert compressed_data.decode() == 'custom,KLUv/SAEIQAA3q2+7w=='
decompressed_data = parse_string_decompress(compressed_data, parse_string=True)

assert decompressed_data[0] == 222
assert decompressed_data[1] == 173
assert decompressed_data[2] == 190
assert decompressed_data[3] == 239
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ test('We can stringify during compression', async t => {

test('We can use a custom dataUrlPrefix', async t => {
const data = new Uint8Array([222, 173, 190, 239])
const { output: compressedData } = await compressStringifyNode(data, { compressionLevel: 8, stringify: true, dataUrlPrefix: 'data:base64,' })
const { output: compressedData } = await compressStringifyNode(data, { compressionLevel: 8, stringify: true, dataUrlPrefix: 'custom,' })
const decoder = new TextDecoder()
t.is(decoder.decode(compressedData.buffer), 'data:base64,KLUv/SAEIQAA3q2+7w==')
t.is(decoder.decode(compressedData.buffer), 'custom,KLUv/SAEIQAA3q2+7w==')
const { output: decompressedData } = await parseStringDecompressNode(compressedData, { parseString: true })

t.is(decompressedData[0], 222)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
interface ApplyPresentationStateToImageOptions {
/** filename: string. Process using presentation state file */
presentationStateFile?: Uint8Array
presentationStateFile: Uint8Array

/** filename: string. Process using settings from configuration file */
configFile?: string
Expand Down
File renamed without changes.
20 changes: 20 additions & 0 deletions src/bindgen/interfaceJsonNodeWasi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { readFile } from 'node:fs/promises';
import { WASI } from 'wasi';
import { argv, env } from 'node:process';

const wasi = new WASI({
args: ['--interface-json'],
env,
preopens: {
},
});

const importObject = { wasi_snapshot_preview1: wasi.wasiImport };

const wasm = await WebAssembly.compile(
await readFile(new URL(process.argv[2], import.meta.url)),
);
const instance = await WebAssembly.instantiate(wasm, importObject);

wasi.initialize(instance);
instance.exports['']()
19 changes: 19 additions & 0 deletions src/bindgen/interfaceJsonTypeToInterfaceType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const interfaceJsonTypeToInterfaceType = new Map([
['INPUT_TEXT_FILE:FILE', 'TextFile'],
['OUTPUT_TEXT_FILE:FILE', 'TextFile'],
['INPUT_BINARY_FILE:FILE', 'BinaryFile'],
['OUTPUT_BINARY_FILE:FILE', 'BinaryFile'],
['INPUT_TEXT_STREAM', 'TextStream'],
['OUTPUT_TEXT_STREAM', 'TextStream'],
['INPUT_BINARY_STREAM', 'BinaryStream'],
['OUTPUT_BINARY_STREAM', 'BinaryStream'],
['INPUT_IMAGE', 'Image'],
['OUTPUT_IMAGE', 'Image'],
['INPUT_MESH', 'Mesh'],
['OUTPUT_MESH', 'Mesh'],
['INPUT_POLYDATA', 'PolyData'],
['OUTPUT_POLYDATA', 'PolyData'],
['OUTPUT_JSON', 'JsonObject'],
])

export default interfaceJsonTypeToInterfaceType
Loading

0 comments on commit fec10f3

Please sign in to comment.