diff --git a/examples/intro.ipynb b/examples/intro.ipynb index 6934942..c7cdf8c 100644 --- a/examples/intro.ipynb +++ b/examples/intro.ipynb @@ -17,6 +17,7 @@ "metadata": {}, "outputs": [], "source": [ + "%matplotlib inline\n", "import pandas as pd\n", "import matplotlib\n", "from ipython2cwl.iotypes import CWLFilePathInput, CWLFilePathOutput" @@ -31,6 +32,15 @@ "dataset: CWLFilePathInput = 'example.csv'" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To expose a variable a variable as a CWL output we can use the basic output data types or the dumpables. \n", + "\n", + "Let's suppose that the Jupyter Notebook user wants to save the image to a file we can use the CWLFilePathOutput annotation. " + ] + }, { "cell_type": "code", "execution_count": 3, @@ -51,13 +61,22 @@ ], "source": [ "data = pd.read_csv(dataset)\n", - "# original data\n", "fig = data.plot()\n", "\n", + "# original data\n", "original_image: CWLFilePathOutput = 'original_data.png'\n", "fig.figure.savefig(original_image)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's say that the Jupyter Notebook user does not want to store the image but in the CWL we want that as an output file. We can use the PNGPlot annotation. The ipython2cwl will store that image to a png file for you in a file with the name `new_data.png`. \n", + "\n", + "> For more complicated use cases check CWLDumpable in the [docs](https://ipython2cwl.readthedocs.io/)" + ] + }, { "cell_type": "code", "execution_count": 4, @@ -65,7 +84,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -78,37 +97,20 @@ ], "source": [ "# transform data\n", + "import matplotlib.pyplot as plt\n", "data.sort_values(by='Random B', 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)" + "plt.figure()\n", + "new_data: 'CWLPNGPlot' = plt.plot(data)" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 5, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Traceback (most recent call last):\r\n", - " File \"/Users/dks/.pyenv/versions/3.6.10/bin/jupyter-jn2cwl\", line 33, in \r\n", - " sys.exit(load_entry_point('ipython2cwl', 'console_scripts', 'jupyter-jn2cwl')())\r\n", - " File \"/Users/dks/Workspaces/IPython2CWL/ipython2cwl/ipython2cwl.py\", line 32, in main\r\n", - " converter = AnnotatedIPython2CWLToolConverter(script_code)\r\n", - " File \"/Users/dks/Workspaces/IPython2CWL/ipython2cwl/cwltoolextractor.py\", line 133, in __init__\r\n", - " self._tree = ast.fix_missing_locations(extractor.visit(ast.parse(self._code)))\r\n", - " File \"/Users/dks/.pyenv/versions/3.6.10/lib/python3.6/ast.py\", line 35, in parse\r\n", - " return compile(source, filename, mode, PyCF_ONLY_AST)\r\n", - "TypeError: compile() arg 1 must be a string, bytes or AST object\r\n" - ] - } - ], + "outputs": [], "source": [ - "# !jupyter-jn2cwl -o compiled_tool intro.ipynb" + "#! jupyter-repo2cwl . -o .\n", + "#!open new_data.png" ] }, { @@ -117,67 +119,58 @@ "source": [ "To compile the presented jupyter notebook to a CWL CommandLineTool run the following commands:\n", "```sh\n", - "mkdir tool\n", - "jupyter-jn2cwl -o tool/tool.tar intro.ipynb\n", - "```\n", - "The tar file contains all the required files. Now we can extract them and build the docker image. \n", - "\n", - "```sh\n", - "cd tool\n", - "tar -xvf tool.tar\n", - "docker build . -t jn2cwl:latest .\n", + "jupyter-repo2cwl . -o .\n", "```\n", "\n", "To test the tool as a cwl we can execute the following command:\n", "```sh\n", - "cwltool tool.cwl --dataset ../example.csv\n", + "cwltool intro.cwl --dataset example.csv\n", "```\n", "\n", "`\n", - "INFO /Users/dks/.pyenv/versions/3.6.10/bin/cwltool 3.0.20200530110633\n", - "INFO Resolved 'tool.cwl' to 'file:///Users/dks/Workspaces/IPython2CWL/examples/compiled_tool/tool.cwl'\n", - "INFO [job tool.cwl] /private/tmp/docker_tmpq5oemdog$ docker \\\n", + "INFO /Users/dks/.pyenv/versions/3.6.10/bin/cwltool 3.0.20200706173533\n", + "INFO Resolved 'intro.cwl' to 'file:///Users/dks/Workspaces/IPython2CWL/examples/intro.cwl'\n", + "INFO [job intro.cwl] /private/tmp/docker_tmp7wzg7cbi$ docker \\\n", " run \\\n", " -i \\\n", - " --mount=type=bind,source=/private/tmp/docker_tmpq5oemdog,target=/mWoQja \\\n", - " --mount=type=bind,source=/private/tmp/docker_tmpombd7mgl,target=/tmp \\\n", - " --mount=type=bind,source=/Users/dks/Workspaces/IPython2CWL/examples/example.csv,target=/var/lib/cwl/stgf1649a28-7fa0-4a19-9b59-54d4839f363e/example.csv,readonly \\\n", - " --workdir=/mWoQja \\\n", + " --mount=type=bind,source=/private/tmp/docker_tmp7wzg7cbi,target=/Oxibvb \\\n", + " --mount=type=bind,source=/private/tmp/docker_tmpje9_oz4b,target=/tmp \\\n", + " --mount=type=bind,source=/Users/dks/Workspaces/IPython2CWL/examples/example.csv,target=/var/lib/cwl/stg5a294c1c-b254-4c5b-b925-ccabe08460ca/example.csv,readonly \\\n", + " --workdir=/Oxibvb \\\n", " --read-only=true \\\n", " --net=none \\\n", " --user=501:20 \\\n", " --rm \\\n", " --env=TMPDIR=/tmp \\\n", - " --env=HOME=/mWoQja \\\n", - " --cidfile=/private/tmp/docker_tmprs7uv65u/20200622183924-428346.cid \\\n", - " jn2cwl:latest \\\n", - " notebookTool \\\n", + " --env=HOME=/Oxibvb \\\n", + " --cidfile=/private/tmp/docker_tmp2wk9z9z0/20200709182229-968352.cid \\\n", + " r2d-2fvar-2ffolders-2fk8-2f800hfw-5fn2md-5f2zb44lhhtqqr0000gn-2ft-2frepo2cwl-5f3n29rdzx-2frepo1594315330 \\\n", + " /app/cwl/bin/intro \\\n", + " -- \\\n", " --dataset \\\n", - " /var/lib/cwl/stgf1649a28-7fa0-4a19-9b59-54d4839f363e/example.csv\n", - "INFO [job tool.cwl] Max memory used: 198MiB\n", - "INFO [job tool.cwl] completed success\n", + " /var/lib/cwl/stg5a294c1c-b254-4c5b-b925-ccabe08460ca/example.csv\n", + "INFO [job intro.cwl] Max memory used: 227MiB\n", + "INFO [job intro.cwl] completed success\n", "{\n", - " \"after_transform_data\": {\n", - " \"location\": \"file:///Users/dks/Workspaces/IPython2CWL/examples/compiled_tool/new_data.png\",\n", + " \"new_data\": {\n", + " \"location\": \"file:///Users/dks/Workspaces/IPython2CWL/examples/new_data.png\",\n", " \"basename\": \"new_data.png\",\n", " \"class\": \"File\",\n", - " \"checksum\": \"sha1$d4d3a83c00d744931753c9aa93981d4a599ed391\",\n", - " \"size\": 40115,\n", - " \"path\": \"/Users/dks/Workspaces/IPython2CWL/examples/compiled_tool/new_data.png\"\n", + " \"checksum\": \"sha1$5d1154b55c741efc5adcd5e200abf626345dda3c\",\n", + " \"size\": 22656,\n", + " \"path\": \"/Users/dks/Workspaces/IPython2CWL/examples/new_data.png\"\n", " },\n", " \"original_image\": {\n", - " \"location\": \"file:///Users/dks/Workspaces/IPython2CWL/examples/compiled_tool/original_data.png\",\n", + " \"location\": \"file:///Users/dks/Workspaces/IPython2CWL/examples/original_data.png\",\n", " \"basename\": \"original_data.png\",\n", " \"class\": \"File\",\n", - " \"checksum\": \"sha1$48966757640d677f3065b4e79ece68e5d4b324dd\",\n", - " \"size\": 52590,\n", - " \"path\": \"/Users/dks/Workspaces/IPython2CWL/examples/compiled_tool/original_data.png\"\n", + " \"checksum\": \"sha1$f5dd2d7ce249b247b48bc31018aa793a342dd120\",\n", + " \"size\": 31326,\n", + " \"path\": \"/Users/dks/Workspaces/IPython2CWL/examples/original_data.png\"\n", " }\n", "}\n", "INFO Final process status is success\n", - "`\n", - "\n", - "Currently, in the presented version of the ipython2cwl the tool does not support magic commands but that feature will be added in soon!! for the reason if we write commands in the format \"!ipython2cwl intro.ipynb\" it will not work!" + "`" ] } ], diff --git a/examples/new_data.png b/examples/new_data.png index bb756b7..a97e7d3 100644 Binary files a/examples/new_data.png and b/examples/new_data.png differ diff --git a/examples/requirements.txt b/examples/requirements.txt new file mode 100644 index 0000000..5d56fdd --- /dev/null +++ b/examples/requirements.txt @@ -0,0 +1,2 @@ +pandas +matplotlib diff --git a/ipython2cwl/cwltoolextractor.py b/ipython2cwl/cwltoolextractor.py index dae0c1a..abe9962 100644 --- a/ipython2cwl/cwltoolextractor.py +++ b/ipython2cwl/cwltoolextractor.py @@ -15,7 +15,7 @@ from nbformat.notebooknode import NotebookNode # type: ignore from .iotypes import CWLFilePathInput, CWLBooleanInput, CWLIntInput, CWLStringInput, CWLFilePathOutput, \ - CWLDumpableFile, CWLDumpableBinaryFile, CWLDumpable + CWLDumpableFile, CWLDumpableBinaryFile, CWLDumpable, CWLPNGPlot, CWLPNGFigure from .requirements_manager import RequirementsManager with open(os.sep.join([os.path.abspath(os.path.dirname(__file__)), 'templates', 'template.dockerfile'])) as f: @@ -64,9 +64,21 @@ class AnnotatedVariablesExtractor(ast.NodeTransformer): } dumpable_mapper = { - (CWLDumpableFile.__name__,): "with open('{var_name}', 'w') as f:\n\tf.write({var_name})", - (CWLDumpableBinaryFile.__name__,): "with open('{var_name}', 'wb') as f:\n\tf.write({var_name})", + (CWLDumpableFile.__name__,): ( + (None, "with open('{var_name}', 'w') as f:\n\tf.write({var_name})",), + lambda node: node.target.id + ), + (CWLDumpableBinaryFile.__name__,): ( + (None, "with open('{var_name}', 'wb') as f:\n\tf.write({var_name})"), + lambda node: node.target.id + ), (CWLDumpable.__name__, CWLDumpable.dump.__name__): None, + (CWLPNGPlot.__name__,): ( + (None, '{var_name}[-1].figure.savefig("{var_name}.png")'), + lambda node: str(node.target.id) + '.png'), + (CWLPNGFigure.__name__,): ( + ('import matplotlib.pyplot as plt\nplt.figure()', '{var_name}[-1].figure.savefig("{var_name}.png")'), + lambda node: str(node.target.id) + '.png'), } def __init__(self, *args, **kwargs): @@ -110,12 +122,18 @@ def _visit_input_ann_assign(self, node, annotation): return None def _visit_default_dumper(self, node, dumper): - dump_tree = ast.parse(dumper.format(var_name=node.target.id)) - self.to_dump.append(dump_tree.body) + if dumper[0][0] is None: + pre_code_body = [] + else: + pre_code_body = ast.parse(dumper[0][0].format(var_name=node.target.id)).body + if dumper[0][1] is None: + post_code_body = [] + else: + post_code_body = ast.parse(dumper[0][1].format(var_name=node.target.id)).body self.extracted_variables.append(_VariableNameTypePair( - node.target.id, None, None, None, False, True, node.target.id) + node.target.id, None, None, None, False, True, dumper[1](node)) ) - return self.conv_AnnAssign_to_Assign(node) + return [*pre_code_body, self.conv_AnnAssign_to_Assign(node), *post_code_body] def _visit_user_defined_dumper(self, node): load_ctx = ast.Load() diff --git a/ipython2cwl/iotypes.py b/ipython2cwl/iotypes.py index 6ae99db..0adf544 100644 --- a/ipython2cwl/iotypes.py +++ b/ipython2cwl/iotypes.py @@ -158,3 +158,50 @@ class CWLDumpableBinaryFile(CWLDumpable): and at the CWL, the data, will be mapped as a output. """ pass + + +class CWLPNGPlot(CWLDumpable): + """Use that annotation to define that after the assigment of that variable the plt.savefig() should + be called. + + >>> import matplotlib.pyplot as plt + >>> data = [1,2,3] + >>> new_data: 'CWLPNGPlot' = plt.plot(data) + + the converter will tranform these lines to + + >>> import matplotlib.pyplot as plt + >>> data = [1,2,3] + >>> new_data: 'CWLPNGPlot' = plt.plot(data) + >>> plt.savefig('new_data.png') + + + Note that by default if you have multiple plot statements in the same notebook will be written + in the same file. If you want to write them in separates you have to do it in separate figures. + To do that in your notebook you have to create a new figure before the plot command or use the CWLPNGFigure. + + >>> import matplotlib.pyplot as plt + >>> data = [1,2,3] + >>> plt.figure() + >>> new_data: 'CWLPNGPlot' = plt.plot(data) + """ + pass + + +class CWLPNGFigure(CWLDumpable): + """The same with :class:`~ipython2cwl.iotypes.CWLPNGPlot` but creates new figures before plotting. Use that + annotation of you don't want to write multiple graphs in the same image + + >>> import matplotlib.pyplot as plt + >>> data = [1,2,3] + >>> new_data: 'CWLPNGPlot' = plt.plot(data) + + the converter will tranform these lines to + + >>> import matplotlib.pyplot as plt + >>> data = [1,2,3] + >>> plt.figure() + >>> new_data: 'CWLPNGPlot' = plt.plot(data) + >>> plt.savefig('new_data.png') + + """ diff --git a/test-requirements.txt b/test-requirements.txt index 34e0730..8afa0bd 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,6 +4,7 @@ coveralls>=2.0.0 virtualenv>=3.1.0 gitpython>=3.1.3 docker>=4.2.1 -git+https://github.com/giannisdoukas/cwltool.git#egg=cwltool +cwltool==3.0.20200706173533 pandas==1.0.5 mypy +matplotlib \ No newline at end of file diff --git a/tests/test_cwltoolextractor.py b/tests/test_cwltoolextractor.py index 3c50fa6..724a030 100644 --- a/tests/test_cwltoolextractor.py +++ b/tests/test_cwltoolextractor.py @@ -474,3 +474,87 @@ def test_AnnotatedIPython2CWLToolConverter_custom_dumpables(self): os.remove(f) except FileNotFoundError: pass + + def test_AnnotatedIPython2CWLToolConverter_CWLPNGPlot(self): + code = os.linesep.join([ + "import matplotlib.pyplot as plt", + "new_data: 'CWLPNGPlot' = plt.plot([1,2,3,4])", + ]) + converter = AnnotatedIPython2CWLToolConverter(code) + new_script = converter._wrap_script_to_method( + converter._tree, + converter._variables + ) + try: + os.remove('new_data.png') + except FileNotFoundError: + pass + exec(new_script) + locals()['main']() + self.assertTrue(os.path.isfile('new_data.png')) + os.remove('new_data.png') + + tool = converter.cwl_command_line_tool() + self.assertDictEqual( + { + 'cwlVersion': "v1.1", + 'class': 'CommandLineTool', + 'baseCommand': 'notebookTool', + 'hints': { + 'DockerRequirement': {'dockerImageId': 'jn2cwl:latest'} + }, + 'arguments': ['--'], + 'inputs': {}, + 'outputs': { + 'new_data': { + 'type': 'File', + 'outputBinding': { + 'glob': 'new_data.png' + } + } + }, + }, + tool + ) + + def test_AnnotatedIPython2CWLToolConverter_CWLPNGFigure(self): + code = os.linesep.join([ + "import matplotlib.pyplot as plt", + "new_data: 'CWLPNGFigure' = plt.plot([1,2,3,4])", + ]) + converter = AnnotatedIPython2CWLToolConverter(code) + new_script = converter._wrap_script_to_method( + converter._tree, + converter._variables + ) + try: + os.remove('new_data.png') + except FileNotFoundError: + pass + exec(new_script) + locals()['main']() + self.assertTrue(os.path.isfile('new_data.png')) + os.remove('new_data.png') + + tool = converter.cwl_command_line_tool() + self.assertDictEqual( + { + 'cwlVersion': "v1.1", + 'class': 'CommandLineTool', + 'baseCommand': 'notebookTool', + 'hints': { + 'DockerRequirement': {'dockerImageId': 'jn2cwl:latest'} + }, + 'arguments': ['--'], + 'inputs': {}, + 'outputs': { + 'new_data': { + 'type': 'File', + 'outputBinding': { + 'glob': 'new_data.png' + } + } + }, + }, + tool + ) \ No newline at end of file diff --git a/tests/test_system_tests.py b/tests/test_system_tests.py index d48b4fd..2ca482b 100644 --- a/tests/test_system_tests.py +++ b/tests/test_system_tests.py @@ -29,8 +29,8 @@ def test_repo2cwl(self): self.assertListEqual(['example1.cwl'], [f for f in os.listdir(output_dir) if not f.startswith('.')]) with open(os.path.join(output_dir, 'example1.cwl')) as f: - print(20 * '=') print('workflow file') + print(20 * '=') print(f.read()) print(20 * '=')