Skip to content

Commit 9c9a15b

Browse files
committed
fix issue with arrays
1 parent 037e5a5 commit 9c9a15b

File tree

7 files changed

+81
-32
lines changed

7 files changed

+81
-32
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,3 +246,4 @@ tmp.py
246246
/html/
247247
cwlbuild
248248
/tests/repo-like/result.yaml
249+
/tests/repo-like/messages.txt

ipython2cwl/cwltoolextractor.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ def visit_AnnAssign(self, node):
5555
try:
5656
if (isinstance(node.annotation, ast.Name) and node.annotation.id in self.input_type_mapper) or \
5757
(isinstance(node.annotation, ast.Str) and node.annotation.s in self.input_type_mapper):
58-
mapper = self.input_type_mapper[node.annotation.id]
58+
if hasattr(node.annotation, 'id'):
59+
mapper = self.input_type_mapper[node.annotation.id]
60+
else:
61+
mapper = self.input_type_mapper[node.annotation.s]
5962
self.extracted_nodes.append(
6063
(node, mapper[0], mapper[1], True, True, False)
6164
)
@@ -152,6 +155,7 @@ def from_jupyter_notebook_node(cls, node: NotebookNode) -> 'AnnotatedIPython2CWL
152155

153156
@classmethod
154157
def _wrap_script_to_method(cls, tree, variables) -> str:
158+
add_args = cls.__get_add_arguments__([v for v in variables if v.is_input])
155159
main_template_code = os.linesep.join([
156160
f"def main({','.join([v.name for v in variables if v.is_input])}):",
157161
"\tpass",
@@ -160,19 +164,31 @@ def _wrap_script_to_method(cls, tree, variables) -> str:
160164
"import argparse",
161165
'import pathlib',
162166
"parser = argparse.ArgumentParser()",
163-
*[f'parser.add_argument("--{variable.name}", '
164-
f'type={variable.argparse_typeof}, '
165-
f'required={variable.required})'
166-
for variable in variables],
167+
*add_args,
167168
"args = parser.parse_args()",
168-
f"main({','.join([f'{v.name}=args.{v.name}' for v in variables if v.is_input])})"
169+
f"main({','.join([f'{v.name}=args.{v.name} ' for v in variables if v.is_input])})"
169170
]],
170171
])
171172
main_function = ast.parse(main_template_code)
172173
[node for node in main_function.body if isinstance(node, ast.FunctionDef) and node.name == 'main'][0] \
173174
.body = tree.body
174175
return astor.to_source(main_function)
175176

177+
@classmethod
178+
def __get_add_arguments__(cls, variables):
179+
args = []
180+
for variable in variables:
181+
is_array = variable.cwl_typeof.endswith('[]')
182+
arg: str = f'parser.add_argument("--{variable.name}", '
183+
arg += f'type={variable.argparse_typeof}, '
184+
arg += f'required={variable.required}, '
185+
if is_array:
186+
arg += f'nargs="+", '
187+
arg = arg.strip()
188+
arg += ')'
189+
args.append(arg)
190+
return args
191+
176192
def cwl_command_line_tool(self, docker_image_id: str = 'jn2cwl:latest') -> Dict:
177193
"""
178194
Creates the description of the CWL Command Line Tool.

tests/repo-like/example1.ipynb

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,24 @@
22
"cells": [
33
{
44
"cell_type": "code",
5-
"execution_count": 3,
5+
"execution_count": null,
66
"metadata": {},
77
"outputs": [],
88
"source": [
9-
"from ipython2cwl.iotypes import CWLFilePathInput, CWLFilePathOutput\n",
10-
"import yaml"
9+
"from ipython2cwl.iotypes import CWLFilePathInput, CWLStringInput, CWLFilePathOutput\n",
10+
"from typing import List\n",
11+
"import yaml\n",
12+
"import os"
1113
]
1214
},
1315
{
1416
"cell_type": "code",
15-
"execution_count": 4,
17+
"execution_count": null,
1618
"metadata": {},
17-
"outputs": [
18-
{
19-
"name": "stdout",
20-
"output_type": "stream",
21-
"text": [
22-
"{'entry1': 1, 'entry2': 'foo', 'entry3': 'bar'}\n"
23-
]
24-
}
25-
],
19+
"outputs": [],
2620
"source": [
2721
"datafilename: CWLFilePathInput = 'data.yaml'\n",
28-
"\n",
22+
"messages: List[CWLStringInput] = ['hello', 'world']\n",
2923
"with open(datafilename) as fd: \n",
3024
" data = yaml.safe_load(fd)\n",
3125
"print(data)"
@@ -42,13 +36,16 @@
4236
},
4337
{
4438
"cell_type": "code",
45-
"execution_count": 8,
39+
"execution_count": null,
4640
"metadata": {},
4741
"outputs": [],
4842
"source": [
4943
"results_filename: CWLFilePathOutput = 'result.yaml'\n",
5044
"with open(results_filename, 'w') as fd:\n",
51-
" yaml.safe_dump(data, fd)"
45+
" yaml.safe_dump(data, fd)\n",
46+
"messages_outputs: CWLFilePathOutput = 'messages.txt'\n",
47+
"with open(messages_outputs, 'w') as f:\n",
48+
" f.write(' '.join(messages))"
5249
]
5350
}
5451
],

tests/repo-like/requirements.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
PyYAML==5.3.1
2-
ipython2cwl==0.0.1
1+
PyYAML==5.3.1

tests/simple.ipynb

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,29 @@
22
"cells": [
33
{
44
"cell_type": "code",
5-
"execution_count": 1,
5+
"execution_count": null,
66
"metadata": {},
77
"outputs": [],
88
"source": [
99
"import pandas as pd\n",
10+
"from typing import List\n",
1011
"import matplotlib\n",
11-
"from ipython2cwl.iotypes import CWLFilePathInput, CWLFilePathOutput"
12+
"from ipython2cwl.iotypes import CWLFilePathInput, CWLFilePathOutput, CWLStringInput"
1213
]
1314
},
1415
{
1516
"cell_type": "code",
16-
"execution_count": 1,
17+
"execution_count": null,
1718
"metadata": {},
1819
"outputs": [],
1920
"source": [
20-
"dataset: CWLFilePathInput = 'example.csv'"
21+
"dataset: CWLFilePathInput = './data/data.csv'\n",
22+
"messages: List[CWLStringInput] = ['hello', 'world']"
2123
]
2224
},
2325
{
2426
"cell_type": "code",
25-
"execution_count": 1,
27+
"execution_count": null,
2628
"metadata": {},
2729
"outputs": [],
2830
"source": [
@@ -36,17 +38,28 @@
3638
},
3739
{
3840
"cell_type": "code",
39-
"execution_count": 1,
41+
"execution_count": null,
4042
"metadata": {},
4143
"outputs": [],
4244
"source": [
4345
"# transform data\n",
44-
"data.sort_values(by='Random B', ascending=False, inplace=True, ignore_index=True)\n",
46+
"data.sort_values(by='Y', ascending=False, inplace=True, ignore_index=True)\n",
4547
"fig = data.plot()\n",
4648
"\n",
4749
"after_transform_data: CWLFilePathOutput = 'new_data.png'\n",
4850
"fig.figure.savefig(after_transform_data)"
4951
]
52+
},
53+
{
54+
"cell_type": "code",
55+
"execution_count": null,
56+
"metadata": {},
57+
"outputs": [],
58+
"source": [
59+
"messages_filename = 'messages.txt'\n",
60+
"with open(messages_filename, 'w') as f:\n",
61+
" f.write(' '.join(messages))"
62+
]
5063
}
5164
],
5265
"metadata": {

tests/test_ipython2cwl_from_repo.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ast
12
import os
23
import shutil
34
import tempfile
@@ -39,6 +40,15 @@ def test_docker_build(self):
3940
docker_client = docker.from_env()
4041
script = docker_client.containers.run(dockerfile_image_id, '/app/cwl/bin/simple', entrypoint='/bin/cat')
4142
self.assertIn('fig.figure.savefig(after_transform_data)', script.decode())
43+
messages_array_arg_line = ast.parse([line.strip() for line in script.decode().splitlines() if '--messages' in line][-1])
44+
self.assertEqual(
45+
'+', # nargs = '+'
46+
[k.value.s for k in messages_array_arg_line.body[0].value.keywords if k.arg == 'nargs'][0]
47+
)
48+
self.assertEqual(
49+
'str', # type = 'str'
50+
[k.value.id for k in messages_array_arg_line.body[0].value.keywords if k.arg == 'type'][0]
51+
)
4252
self.assertDictEqual(
4353
{
4454
'cwlVersion': "v1.1",
@@ -54,6 +64,12 @@ def test_docker_build(self):
5464
'inputBinding': {
5565
'prefix': '--dataset'
5666
}
67+
},
68+
'messages': {
69+
'type': 'string[]',
70+
'inputBinding': {
71+
'prefix': '--messages'
72+
}
5773
}
5874
},
5975
'outputs': {

tests/test_system_tests.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,17 @@ def test_repo2cwl(self):
4343

4444
example1_tool = fac.make(os.path.join(output_dir, 'example1.cwl'))
4545
result = example1_tool(
46-
datafilename={'class': 'File', 'location': os.path.join(self.repo_like_dir, 'data.yaml')})
46+
datafilename={
47+
'class': 'File', 'location': os.path.join(self.repo_like_dir, 'data.yaml')
48+
},
49+
messages=["hello", "test", "!!!"]
50+
)
4751
with open(result['results_filename']['location'][7:]) as f:
4852
new_data = yaml.safe_load(f)
4953
self.assertDictEqual({'entry1': 2, 'entry2': 'foo', 'entry3': 'bar'}, new_data)
54+
with open(result['messages_outputs']['location'][7:]) as f:
55+
message = f.read()
56+
self.assertEqual("hello test !!!", message)
5057
shutil.rmtree(output_dir)
5158

5259
def test_repo2cwl_output_dir_does_not_exists(self):

0 commit comments

Comments
 (0)