From d96bb06571ed2217955b866ccd0c18777a72cdfa Mon Sep 17 00:00:00 2001 From: Giannis Doukas Date: Tue, 30 Jun 2020 14:46:56 +0100 Subject: [PATCH 1/6] fix link for docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 57fa170..63b22d8 100644 --- a/README.md +++ b/README.md @@ -42,4 +42,4 @@ jupyter repo2cwl https://github.com/giannisdoukas/cwl-annotated-jupyter-notebook ### Docs -[https://ipython2cwl.readthedocs.io/](https://readthedocs.org/projects/ipython2cwl/badge/?version=latest) \ No newline at end of file +[https://ipython2cwl.readthedocs.io/](https://ipython2cwl.readthedocs.io/en/latest/) From 037e5a54822496b0679a9c2fd71133500e5f5dcf Mon Sep 17 00:00:00 2001 From: Giannis Doukas Date: Tue, 30 Jun 2020 19:35:09 +0100 Subject: [PATCH 2/6] rename test file --- ipython2cwl/cwltoolextractor.py | 2 +- tests/{test_cwltool.py => test_cwltoolextractor.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/{test_cwltool.py => test_cwltoolextractor.py} (100%) diff --git a/ipython2cwl/cwltoolextractor.py b/ipython2cwl/cwltoolextractor.py index 6aec361..3f5d288 100644 --- a/ipython2cwl/cwltoolextractor.py +++ b/ipython2cwl/cwltoolextractor.py @@ -22,7 +22,7 @@ SETUP_TEMPLATE = f.read() -# TODO: does not support recursion if main function exists +# TODO: check if supports recursion if main function exists class AnnotatedVariablesExtractor(ast.NodeTransformer): input_type_mapper = { diff --git a/tests/test_cwltool.py b/tests/test_cwltoolextractor.py similarity index 100% rename from tests/test_cwltool.py rename to tests/test_cwltoolextractor.py From 9c9a15bc519c8c87c65d0e340c2b5ffca2367831 Mon Sep 17 00:00:00 2001 From: Giannis Doukas Date: Tue, 30 Jun 2020 21:23:04 +0100 Subject: [PATCH 3/6] fix issue with arrays --- .gitignore | 1 + ipython2cwl/cwltoolextractor.py | 28 ++++++++++++++++++++++------ tests/repo-like/example1.ipynb | 29 +++++++++++++---------------- tests/repo-like/requirements.txt | 3 +-- tests/simple.ipynb | 27 ++++++++++++++++++++------- tests/test_ipython2cwl_from_repo.py | 16 ++++++++++++++++ tests/test_system_tests.py | 9 ++++++++- 7 files changed, 81 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index 636ff67..3ee1292 100644 --- a/.gitignore +++ b/.gitignore @@ -246,3 +246,4 @@ tmp.py /html/ cwlbuild /tests/repo-like/result.yaml +/tests/repo-like/messages.txt diff --git a/ipython2cwl/cwltoolextractor.py b/ipython2cwl/cwltoolextractor.py index 3f5d288..e528796 100644 --- a/ipython2cwl/cwltoolextractor.py +++ b/ipython2cwl/cwltoolextractor.py @@ -55,7 +55,10 @@ def visit_AnnAssign(self, node): try: if (isinstance(node.annotation, ast.Name) and node.annotation.id in self.input_type_mapper) or \ (isinstance(node.annotation, ast.Str) and node.annotation.s in self.input_type_mapper): - mapper = self.input_type_mapper[node.annotation.id] + if hasattr(node.annotation, 'id'): + mapper = self.input_type_mapper[node.annotation.id] + else: + mapper = self.input_type_mapper[node.annotation.s] self.extracted_nodes.append( (node, mapper[0], mapper[1], True, True, False) ) @@ -152,6 +155,7 @@ def from_jupyter_notebook_node(cls, node: NotebookNode) -> 'AnnotatedIPython2CWL @classmethod def _wrap_script_to_method(cls, tree, variables) -> str: + add_args = cls.__get_add_arguments__([v for v in variables if v.is_input]) main_template_code = os.linesep.join([ f"def main({','.join([v.name for v in variables if v.is_input])}):", "\tpass", @@ -160,12 +164,9 @@ def _wrap_script_to_method(cls, tree, variables) -> str: "import argparse", 'import pathlib', "parser = argparse.ArgumentParser()", - *[f'parser.add_argument("--{variable.name}", ' - f'type={variable.argparse_typeof}, ' - f'required={variable.required})' - for variable in variables], + *add_args, "args = parser.parse_args()", - f"main({','.join([f'{v.name}=args.{v.name}' for v in variables if v.is_input])})" + f"main({','.join([f'{v.name}=args.{v.name} ' for v in variables if v.is_input])})" ]], ]) main_function = ast.parse(main_template_code) @@ -173,6 +174,21 @@ def _wrap_script_to_method(cls, tree, variables) -> str: .body = tree.body return astor.to_source(main_function) + @classmethod + def __get_add_arguments__(cls, variables): + args = [] + for variable in variables: + is_array = variable.cwl_typeof.endswith('[]') + arg: str = f'parser.add_argument("--{variable.name}", ' + arg += f'type={variable.argparse_typeof}, ' + arg += f'required={variable.required}, ' + if is_array: + arg += f'nargs="+", ' + arg = arg.strip() + arg += ')' + args.append(arg) + return args + def cwl_command_line_tool(self, docker_image_id: str = 'jn2cwl:latest') -> Dict: """ Creates the description of the CWL Command Line Tool. diff --git a/tests/repo-like/example1.ipynb b/tests/repo-like/example1.ipynb index 04ea14c..476d70f 100644 --- a/tests/repo-like/example1.ipynb +++ b/tests/repo-like/example1.ipynb @@ -2,30 +2,24 @@ "cells": [ { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "from ipython2cwl.iotypes import CWLFilePathInput, CWLFilePathOutput\n", - "import yaml" + "from ipython2cwl.iotypes import CWLFilePathInput, CWLStringInput, CWLFilePathOutput\n", + "from typing import List\n", + "import yaml\n", + "import os" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'entry1': 1, 'entry2': 'foo', 'entry3': 'bar'}\n" - ] - } - ], + "outputs": [], "source": [ "datafilename: CWLFilePathInput = 'data.yaml'\n", - "\n", + "messages: List[CWLStringInput] = ['hello', 'world']\n", "with open(datafilename) as fd: \n", " data = yaml.safe_load(fd)\n", "print(data)" @@ -42,13 +36,16 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "results_filename: CWLFilePathOutput = 'result.yaml'\n", "with open(results_filename, 'w') as fd:\n", - " yaml.safe_dump(data, fd)" + " yaml.safe_dump(data, fd)\n", + "messages_outputs: CWLFilePathOutput = 'messages.txt'\n", + "with open(messages_outputs, 'w') as f:\n", + " f.write(' '.join(messages))" ] } ], diff --git a/tests/repo-like/requirements.txt b/tests/repo-like/requirements.txt index f98e8cd..3101697 100644 --- a/tests/repo-like/requirements.txt +++ b/tests/repo-like/requirements.txt @@ -1,2 +1 @@ -PyYAML==5.3.1 -ipython2cwl==0.0.1 \ No newline at end of file +PyYAML==5.3.1 \ No newline at end of file diff --git a/tests/simple.ipynb b/tests/simple.ipynb index a9d7b61..405e667 100644 --- a/tests/simple.ipynb +++ b/tests/simple.ipynb @@ -2,27 +2,29 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", + "from typing import List\n", "import matplotlib\n", - "from ipython2cwl.iotypes import CWLFilePathInput, CWLFilePathOutput" + "from ipython2cwl.iotypes import CWLFilePathInput, CWLFilePathOutput, CWLStringInput" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "dataset: CWLFilePathInput = 'example.csv'" + "dataset: CWLFilePathInput = './data/data.csv'\n", + "messages: List[CWLStringInput] = ['hello', 'world']" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -36,17 +38,28 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# transform data\n", - "data.sort_values(by='Random B', ascending=False, inplace=True, ignore_index=True)\n", + "data.sort_values(by='Y', ascending=False, inplace=True, ignore_index=True)\n", "fig = data.plot()\n", "\n", "after_transform_data: CWLFilePathOutput = 'new_data.png'\n", "fig.figure.savefig(after_transform_data)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "messages_filename = 'messages.txt'\n", + "with open(messages_filename, 'w') as f:\n", + " f.write(' '.join(messages))" + ] } ], "metadata": { diff --git a/tests/test_ipython2cwl_from_repo.py b/tests/test_ipython2cwl_from_repo.py index f46b62b..9e70d33 100644 --- a/tests/test_ipython2cwl_from_repo.py +++ b/tests/test_ipython2cwl_from_repo.py @@ -1,3 +1,4 @@ +import ast import os import shutil import tempfile @@ -39,6 +40,15 @@ def test_docker_build(self): docker_client = docker.from_env() script = docker_client.containers.run(dockerfile_image_id, '/app/cwl/bin/simple', entrypoint='/bin/cat') self.assertIn('fig.figure.savefig(after_transform_data)', script.decode()) + messages_array_arg_line = ast.parse([line.strip() for line in script.decode().splitlines() if '--messages' in line][-1]) + self.assertEqual( + '+', # nargs = '+' + [k.value.s for k in messages_array_arg_line.body[0].value.keywords if k.arg == 'nargs'][0] + ) + self.assertEqual( + 'str', # type = 'str' + [k.value.id for k in messages_array_arg_line.body[0].value.keywords if k.arg == 'type'][0] + ) self.assertDictEqual( { 'cwlVersion': "v1.1", @@ -54,6 +64,12 @@ def test_docker_build(self): 'inputBinding': { 'prefix': '--dataset' } + }, + 'messages': { + 'type': 'string[]', + 'inputBinding': { + 'prefix': '--messages' + } } }, 'outputs': { diff --git a/tests/test_system_tests.py b/tests/test_system_tests.py index e476cdc..d48b4fd 100644 --- a/tests/test_system_tests.py +++ b/tests/test_system_tests.py @@ -43,10 +43,17 @@ def test_repo2cwl(self): example1_tool = fac.make(os.path.join(output_dir, 'example1.cwl')) result = example1_tool( - datafilename={'class': 'File', 'location': os.path.join(self.repo_like_dir, 'data.yaml')}) + datafilename={ + 'class': 'File', 'location': os.path.join(self.repo_like_dir, 'data.yaml') + }, + messages=["hello", "test", "!!!"] + ) with open(result['results_filename']['location'][7:]) as f: new_data = yaml.safe_load(f) self.assertDictEqual({'entry1': 2, 'entry2': 'foo', 'entry3': 'bar'}, new_data) + with open(result['messages_outputs']['location'][7:]) as f: + message = f.read() + self.assertEqual("hello test !!!", message) shutil.rmtree(output_dir) def test_repo2cwl_output_dir_does_not_exists(self): From e245624199713835a59895c2c83a75a3642f3acb Mon Sep 17 00:00:00 2001 From: Giannis Doukas Date: Tue, 30 Jun 2020 22:33:18 +0100 Subject: [PATCH 4/6] improve typing parsing on list & optionals --- ipython2cwl/cwltoolextractor.py | 56 ++++++++++++++++++--------------- tests/test_cwltoolextractor.py | 49 +++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/ipython2cwl/cwltoolextractor.py b/ipython2cwl/cwltoolextractor.py index e528796..a78b349 100644 --- a/ipython2cwl/cwltoolextractor.py +++ b/ipython2cwl/cwltoolextractor.py @@ -26,23 +26,31 @@ class AnnotatedVariablesExtractor(ast.NodeTransformer): input_type_mapper = { - CWLFilePathInput.__name__: ( + (CWLFilePathInput.__name__,): ( 'File', 'pathlib.Path', ), - CWLBooleanInput.__name__: ( + (CWLBooleanInput.__name__,): ( 'boolean', 'lambda flag: flag.upper() == "TRUE"', ), - CWLIntInput.__name__: ( + (CWLIntInput.__name__,): ( 'int', 'int', ), - CWLStringInput.__name__: ( + (CWLStringInput.__name__,): ( 'string', 'str', ), } + input_type_mapper = {**input_type_mapper, **{ + ('List', *(t for t in types_names)): (types[0] + "[]", types[1]) + for types_names, types in input_type_mapper.items() + }, **{ + ('Optional', *(t for t in types_names)): (types[0] + "?", types[1]) + for types_names, types in input_type_mapper.items() + }} + output_type_mapper = { CWLFilePathOutput.__name__ } @@ -51,33 +59,29 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.extracted_nodes = [] + def __get_annotation__(self, type_annotation): + annotation = None + if isinstance(type_annotation, ast.Name): + annotation = (type_annotation.id,) + elif isinstance(type_annotation, ast.Str): + annotation = (type_annotation.s,) + ann_expr = ast.parse(type_annotation.s.strip()).body[0] + if hasattr(ann_expr, 'value') and isinstance(ann_expr.value, ast.Subscript): + annotation = self.__get_annotation__(ann_expr.value) + elif isinstance(type_annotation, ast.Subscript): + annotation = (type_annotation.value.id, *self.__get_annotation__(type_annotation.slice.value)) + return annotation + def visit_AnnAssign(self, node): try: - if (isinstance(node.annotation, ast.Name) and node.annotation.id in self.input_type_mapper) or \ - (isinstance(node.annotation, ast.Str) and node.annotation.s in self.input_type_mapper): - if hasattr(node.annotation, 'id'): - mapper = self.input_type_mapper[node.annotation.id] - else: - mapper = self.input_type_mapper[node.annotation.s] + annotation = self.__get_annotation__(node.annotation) + if annotation in self.input_type_mapper: + mapper = self.input_type_mapper[annotation] self.extracted_nodes.append( (node, mapper[0], mapper[1], True, True, False) ) return None - elif isinstance(node.annotation, ast.Subscript): - if node.annotation.value.id == "Optional" \ - and node.annotation.slice.value.id in self.input_type_mapper: - mapper = self.input_type_mapper[node.annotation.slice.value.id] - self.extracted_nodes.append( - (node, mapper[0] + '?', mapper[1], False, True, False) - ) - return None - elif node.annotation.value.id == "List" \ - and node.annotation.slice.value.id in self.input_type_mapper: - mapper = self.input_type_mapper[node.annotation.slice.value.id] - self.extracted_nodes.append( - (node, mapper[0] + '[]', mapper[1], True, True, False) - ) - return None + elif (isinstance(node.annotation, ast.Name) and node.annotation.id in self.output_type_mapper) or \ (isinstance(node.annotation, ast.Str) and node.annotation.s in self.output_type_mapper): self.extracted_nodes.append( @@ -90,7 +94,7 @@ def visit_AnnAssign(self, node): targets=[node.target], value=node.value ) - except AttributeError: + except Exception: pass return node diff --git a/tests/test_cwltoolextractor.py b/tests/test_cwltoolextractor.py index fab8988..da573ad 100644 --- a/tests/test_cwltoolextractor.py +++ b/tests/test_cwltoolextractor.py @@ -328,3 +328,52 @@ def test_AnnotatedIPython2CWLToolConverter_exclamation_mark_command(self): exec(new_script_without_magics) locals()['main']('original\n!ls -l') self.assertEqual('original\n!ls -l', globals()['printed_message']) + + def test_AnnotatedIPython2CWLToolConverter_optional_array_input(self): + s1 = os.linesep.join([ + 'x1: CWLBooleanInput = True', + ]) + s2 = os.linesep.join([ + 'x1: "CWLBooleanInput" = True', + ]) + # all variables must be the same + self.assertEqual( + AnnotatedIPython2CWLToolConverter(s1)._variables[0], + AnnotatedIPython2CWLToolConverter(s2)._variables[0], + ) + + s1 = os.linesep.join([ + 'x1: Optional[CWLBooleanInput] = True', + ]) + s2 = os.linesep.join([ + 'x1: "Optional[CWLBooleanInput]" = True', + ]) + s3 = os.linesep.join([ + 'x1: Optional["CWLBooleanInput"] = True', + ]) + # all variables must be the same + self.assertEqual( + AnnotatedIPython2CWLToolConverter(s1)._variables[0], + AnnotatedIPython2CWLToolConverter(s2)._variables[0], + ) + self.assertEqual( + AnnotatedIPython2CWLToolConverter(s1)._variables[0], + AnnotatedIPython2CWLToolConverter(s3)._variables[0], + ) + + # test that does not crash + self.assertListEqual([], AnnotatedIPython2CWLToolConverter(os.linesep.join([ + 'x1: RandomHint = True' + ]))._variables) + self.assertListEqual([], AnnotatedIPython2CWLToolConverter(os.linesep.join([ + 'x1: List[RandomHint] = True' + ]))._variables) + self.assertListEqual([], AnnotatedIPython2CWLToolConverter(os.linesep.join([ + 'x1: List["RandomHint"] = True' + ]))._variables) + self.assertListEqual([], AnnotatedIPython2CWLToolConverter(os.linesep.join([ + 'x1: "List[List[Union[RandomHint, Foo]]]" = True' + ]))._variables) + self.assertListEqual([], AnnotatedIPython2CWLToolConverter(os.linesep.join([ + 'x1: "RANDOM CHARACTERS!!!!!!" = True' + ]))._variables) From 9c78d862b8ab09b717962b22d758cd081595d6b4 Mon Sep 17 00:00:00 2001 From: Giannis Doukas Date: Wed, 1 Jul 2020 00:17:24 +0100 Subject: [PATCH 5/6] fix optionals --- ipython2cwl/cwltoolextractor.py | 11 +++++++++-- tests/simple.ipynb | 15 +++++++++++++-- tests/test_ipython2cwl_from_repo.py | 23 ++++++++++++++++++++++- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/ipython2cwl/cwltoolextractor.py b/ipython2cwl/cwltoolextractor.py index a78b349..2970e55 100644 --- a/ipython2cwl/cwltoolextractor.py +++ b/ipython2cwl/cwltoolextractor.py @@ -11,6 +11,7 @@ import astor import nbconvert import yaml +from astor.string_repr import pretty_string from nbformat.notebooknode import NotebookNode from .iotypes import CWLFilePathInput, CWLBooleanInput, CWLIntInput, CWLStringInput, CWLFilePathOutput @@ -78,7 +79,7 @@ def visit_AnnAssign(self, node): if annotation in self.input_type_mapper: mapper = self.input_type_mapper[annotation] self.extracted_nodes.append( - (node, mapper[0], mapper[1], True, True, False) + (node, mapper[0], mapper[1], not mapper[0].endswith('?'), True, False) ) return None @@ -176,18 +177,24 @@ def _wrap_script_to_method(cls, tree, variables) -> str: main_function = ast.parse(main_template_code) [node for node in main_function.body if isinstance(node, ast.FunctionDef) and node.name == 'main'][0] \ .body = tree.body - return astor.to_source(main_function) + return astor.to_source( + main_function, + pretty_string=lambda s, embedded, current_line, uni: pretty_string(s, embedded, current_line, uni, max_line=500) + ) @classmethod def __get_add_arguments__(cls, variables): args = [] for variable in variables: is_array = variable.cwl_typeof.endswith('[]') + is_optional = variable.cwl_typeof.endswith('?') arg: str = f'parser.add_argument("--{variable.name}", ' arg += f'type={variable.argparse_typeof}, ' arg += f'required={variable.required}, ' if is_array: arg += f'nargs="+", ' + if is_optional: + arg += f'default=None, ' arg = arg.strip() arg += ')' args.append(arg) diff --git a/tests/simple.ipynb b/tests/simple.ipynb index 405e667..e5e654f 100644 --- a/tests/simple.ipynb +++ b/tests/simple.ipynb @@ -7,7 +7,7 @@ "outputs": [], "source": [ "import pandas as pd\n", - "from typing import List\n", + "from typing import List, Optional\n", "import matplotlib\n", "from ipython2cwl.iotypes import CWLFilePathInput, CWLFilePathOutput, CWLStringInput" ] @@ -19,7 +19,8 @@ "outputs": [], "source": [ "dataset: CWLFilePathInput = './data/data.csv'\n", - "messages: List[CWLStringInput] = ['hello', 'world']" + "messages: List[CWLStringInput] = ['hello', 'world']\n", + "optional_message: Optional[CWLStringInput] = \"Hello from optional\"" ] }, { @@ -60,6 +61,16 @@ "with open(messages_filename, 'w') as f:\n", " f.write(' '.join(messages))" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if optional_message is not None:\n", + " print(optional_message)" + ] } ], "metadata": { diff --git a/tests/test_ipython2cwl_from_repo.py b/tests/test_ipython2cwl_from_repo.py index 9e70d33..a26ab9c 100644 --- a/tests/test_ipython2cwl_from_repo.py +++ b/tests/test_ipython2cwl_from_repo.py @@ -40,7 +40,9 @@ def test_docker_build(self): docker_client = docker.from_env() script = docker_client.containers.run(dockerfile_image_id, '/app/cwl/bin/simple', entrypoint='/bin/cat') self.assertIn('fig.figure.savefig(after_transform_data)', script.decode()) - messages_array_arg_line = ast.parse([line.strip() for line in script.decode().splitlines() if '--messages' in line][-1]) + messages_array_arg_line = ast.parse( + [line.strip() for line in script.decode().splitlines() if '--messages' in line][-1] + ) self.assertEqual( '+', # nargs = '+' [k.value.s for k in messages_array_arg_line.body[0].value.keywords if k.arg == 'nargs'][0] @@ -49,6 +51,19 @@ def test_docker_build(self): 'str', # type = 'str' [k.value.id for k in messages_array_arg_line.body[0].value.keywords if k.arg == 'type'][0] ) + + script_tree = ast.parse(script.decode()) + optional_expression = [x for x in script_tree.body[-1].body if + isinstance(x, ast.Expr) and isinstance(x.value, ast.Call) and len(x.value.args) > 0 and + x.value.args[0].s == '--optional_message'][0] + self.assertEqual( + False, + [k.value for k in optional_expression.value.keywords if k.arg == 'required'][0].value + ) + self.assertEqual( + None, + [k.value for k in optional_expression.value.keywords if k.arg == 'default'][0].value + ) self.assertDictEqual( { 'cwlVersion': "v1.1", @@ -70,6 +85,12 @@ def test_docker_build(self): 'inputBinding': { 'prefix': '--messages' } + }, + 'optional_message': { + 'type': 'string?', + 'inputBinding': { + 'prefix': '--optional_message' + } } }, 'outputs': { From 7b858a019eb7e8b57602d5c372a74ef4c9adc6f1 Mon Sep 17 00:00:00 2001 From: Giannis Doukas Date: Wed, 1 Jul 2020 00:23:56 +0100 Subject: [PATCH 6/6] cleanup --- ipython2cwl/cwltoolextractor.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ipython2cwl/cwltoolextractor.py b/ipython2cwl/cwltoolextractor.py index 2970e55..861c861 100644 --- a/ipython2cwl/cwltoolextractor.py +++ b/ipython2cwl/cwltoolextractor.py @@ -11,7 +11,6 @@ import astor import nbconvert import yaml -from astor.string_repr import pretty_string from nbformat.notebooknode import NotebookNode from .iotypes import CWLFilePathInput, CWLBooleanInput, CWLIntInput, CWLStringInput, CWLFilePathOutput @@ -177,10 +176,7 @@ def _wrap_script_to_method(cls, tree, variables) -> str: main_function = ast.parse(main_template_code) [node for node in main_function.body if isinstance(node, ast.FunctionDef) and node.name == 'main'][0] \ .body = tree.body - return astor.to_source( - main_function, - pretty_string=lambda s, embedded, current_line, uni: pretty_string(s, embedded, current_line, uni, max_line=500) - ) + return astor.to_source(main_function) @classmethod def __get_add_arguments__(cls, variables):