Skip to content

Commit b9509ba

Browse files
committed
feat: Python File interface types support
1 parent 8cc00d9 commit b9509ba

File tree

6 files changed

+77
-11
lines changed

6 files changed

+77
-11
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from dataclasses import dataclass
2+
from pathlib import PurePosixPath
23

34
@dataclass
45
class BinaryFile:
5-
data: bytes
6-
path: str
6+
path: PurePosixPath

src/python/itkwasm/itkwasm/pipeline.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import json
22
from pathlib import Path
3-
from typing import List, Union, Dict
3+
from typing import List, Union, Dict, Tuple
44
from .interface_types import InterfaceTypes
55
from .pipeline_input import PipelineInput
66
from .pipeline_output import PipelineOutput
77
from .text_stream import TextStream
88
from .binary_stream import BinaryStream
9+
from .text_file import TextFile
10+
from .binary_file import BinaryFile
911

1012
from wasmer import engine, wasi, Store, Module, ImportObject, Instance
1113
from wasmer_compiler_cranelift import Compiler
@@ -26,9 +28,21 @@ def __init__(self, pipeline: Union[str, Path, bytes]):
2628
self.module = Module(self.store, wasm_bytes)
2729
self.wasi_version = wasi.get_version(self.module, strict=True)
2830

29-
def run(self, args: List[str], outputs: List[PipelineOutput]=[], inputs: List[PipelineInput]=[], preopen_directories=[], map_directories={}, environments={}) -> List[PipelineOutput]:
31+
def run(self, args: List[str], outputs: List[PipelineOutput]=[], inputs: List[PipelineInput]=[]) -> Tuple[PipelineOutput]:
3032
"""Run the itk-wasm pipeline."""
3133

34+
preopen_directories=set()
35+
map_directories={}
36+
# Todo: expose?
37+
environments={}
38+
for index, input_ in enumerate(inputs):
39+
if input_.type == InterfaceTypes.TextFile or input_.type == InterfaceTypes.BinaryFile:
40+
preopen_directories.add(str(input_.data.path.parent))
41+
for index, output in enumerate(outputs):
42+
if output.type == InterfaceTypes.TextFile or output.type == InterfaceTypes.BinaryFile:
43+
preopen_directories.add(str(output.data.path.parent))
44+
preopen_directories = list(preopen_directories)
45+
3246
wasi_state = wasi.StateBuilder('itk-wasm-pipeline')
3347
wasi_state.arguments(args)
3448
wasi_state.environments(environments)
@@ -60,6 +74,10 @@ def run(self, args: List[str], outputs: List[PipelineOutput]=[], inputs: List[Pi
6074
array_ptr = self._set_input_array(data_array, index, 0)
6175
data_json = { "size": len(data_array), "data": f"data:application/vnd.itk.address,0:{array_ptr}" }
6276
self._set_input_json(data_json, index)
77+
elif input_.type == InterfaceTypes.TextFile:
78+
pass
79+
elif input_.type == InterfaceTypes.BinaryFile:
80+
pass
6381
else:
6482
raise ValueError(f'Unexpected/not yet supported input.type {input_.type}')
6583

@@ -80,13 +98,17 @@ def run(self, args: List[str], outputs: List[PipelineOutput]=[], inputs: List[Pi
8098
data_size = self.output_array_size(0, index, 0)
8199
data_array = bytes(memoryview(self.memory.buffer)[data_ptr:data_ptr+data_size])
82100
output_data = PipelineOutput(InterfaceTypes.BinaryStream, BinaryStream(data_array))
101+
elif output.type == InterfaceTypes.TextFile:
102+
output_data = PipelineOutput(InterfaceTypes.TextFile, TextFile(output.data.path))
103+
elif output.type == InterfaceTypes.BinaryFile:
104+
output_data = PipelineOutput(InterfaceTypes.BinaryFile, BinaryFile(output.data.path))
83105
populated_outputs.append(output_data)
84106

85107
delayed_exit = instance.exports.itk_wasm_delayed_exit
86108
delayed_exit(return_code)
87109

88110
# Should we be returning the return_code?
89-
return populated_outputs
111+
return tuple(populated_outputs)
90112

91113
def _set_input_array(self, data_array: bytes, input_index: int, sub_index: int) -> int:
92114
data_ptr = 0
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from dataclasses import dataclass
2+
from pathlib import PurePosixPath
23

34
@dataclass
45
class TextFile:
5-
data: str
6-
path: str
6+
path: PurePosixPath
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ޭ��
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The answer is 42.

src/python/itkwasm/test/test_pipeline.py

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
from pathlib import Path
1+
from pathlib import Path, PurePosixPath
22

3-
from itkwasm import InterfaceTypes, TextStream, BinaryStream, PipelineInput, PipelineOutput, Pipeline
3+
from itkwasm import InterfaceTypes, TextStream, BinaryStream, PipelineInput, PipelineOutput, Pipeline, TextFile, BinaryFile
44

55
test_input_dir = Path(__file__).resolve().parent / 'input'
6+
import tempfile
67

78

89
def test_stdout_stderr():
@@ -16,7 +17,7 @@ def test_pipeline_bytes():
1617
pipeline = Pipeline(wasm_bytes)
1718
pipeline.run([])
1819

19-
def test_pipeline_input_output_files():
20+
def test_pipeline_input_output_streams():
2021
pipeline = Pipeline(test_input_dir / 'input-output-files-test.wasi.wasm')
2122

2223
pipeline_inputs = [
@@ -45,4 +46,45 @@ def test_pipeline_input_output_files():
4546
assert outputs[1].data.data[0], 222
4647
assert outputs[1].data.data[1], 173
4748
assert outputs[1].data.data[2], 190
48-
assert outputs[1].data.data[3], 239
49+
assert outputs[1].data.data[3], 239
50+
51+
def test_pipeline_input_output_files():
52+
pipeline = Pipeline(test_input_dir / 'input-output-files-test.wasi.wasm')
53+
input_text_file = PurePosixPath(test_input_dir / 'input.txt')
54+
input_binary_file = PurePosixPath(test_input_dir / 'input.bin')
55+
56+
with tempfile.TemporaryDirectory() as tmpdirname:
57+
output_text_file = PurePosixPath(Path(tmpdirname) / 'output.txt')
58+
output_binary_file = PurePosixPath(Path(tmpdirname) / 'output.bin')
59+
60+
pipeline_inputs = [
61+
PipelineInput(InterfaceTypes.TextFile, TextFile(input_text_file)),
62+
PipelineInput(InterfaceTypes.BinaryFile, BinaryFile(input_binary_file)),
63+
]
64+
65+
pipeline_outputs = [
66+
PipelineOutput(InterfaceTypes.TextFile, TextFile(output_text_file)),
67+
PipelineOutput(InterfaceTypes.BinaryFile, BinaryFile(output_binary_file)),
68+
]
69+
70+
args = [
71+
'--use-files',
72+
'--input-text-file', str(input_text_file),
73+
'--input-binary-file', str(input_binary_file),
74+
'--output-text-file', str(output_text_file),
75+
'--output-binary-file', str(output_binary_file)
76+
]
77+
78+
outputs = pipeline.run(args, pipeline_outputs, pipeline_inputs)
79+
80+
assert outputs[0].type == InterfaceTypes.TextFile
81+
with open(outputs[0].data.path, 'r') as fp:
82+
content = fp.read()
83+
assert content == 'The answer is 42.'
84+
assert outputs[1].type, InterfaceTypes.BinaryFile
85+
with open(outputs[1].data.path, 'rb') as fp:
86+
content = fp.read()
87+
assert content[0] == 222
88+
assert content[1] == 173
89+
assert content[2] == 190
90+
assert content[3] == 239

0 commit comments

Comments
 (0)